Auto merge of #91469 - matthiaskrgr:rollup-xom3j55, r=matthiaskrgr

Rollup of 12 pull requests

Successful merges:

 - #89954 (Fix legacy_const_generic doc arguments display)
 - #91321 (Handle placeholder regions in NLL type outlive constraints)
 - #91329 (Fix incorrect usage of `EvaluatedToOk` when evaluating `TypeOutlives`)
 - #91364 (Improve error message for incorrect field accesses through raw pointers)
 - #91387 (Clarify and tidy up explanation of E0038)
 - #91410 (Move `#![feature(const_precise_live_drops)]` checks earlier in the pipeline)
 - #91435 (Improve diagnostic for missing half of binary operator in `if` condition)
 - #91444 (disable tests in Miri that take too long)
 - #91457 (Add additional test from rust issue number 91068)
 - #91460 (Document how `last_os_error` should be used)
 - #91464 (Document file path case sensitivity)
 - #91466 (Improve the comments in `Symbol::interner`.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-12-02 21:58:48 +00:00
commit ff23ad3179
39 changed files with 707 additions and 81 deletions

View File

@ -6,6 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::DUMMY_SP;
@ -95,11 +96,23 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
self.add_outlives(r1_vid, r2_vid);
}
GenericArgKind::Type(t1) => {
GenericArgKind::Type(mut t1) => {
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
// Placeholder regions need to be converted now because it may
// create new region variables, which can't be done later when
// verifying these bounds.
if t1.has_placeholders() {
t1 = tcx.fold_regions(&t1, &mut false, |r, _| match *r {
ty::RegionKind::RePlaceholder(placeholder) => {
self.constraints.placeholder_region(self.infcx, placeholder)
}
_ => r,
});
}
TypeOutlives::new(
&mut *self,
tcx,

View File

@ -80,7 +80,8 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
match &terminator.kind {
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
mir::TerminatorKind::Drop { place: dropped_place, .. }
| mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
// Instead of throwing a bug, we just return here. This is because we have to
@ -104,11 +105,6 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
}
}
mir::TerminatorKind::DropAndReplace { .. } => span_bug!(
terminator.source_info.span,
"`DropAndReplace` should be removed by drop elaboration",
),
mir::TerminatorKind::Abort
| mir::TerminatorKind::Call { .. }
| mir::TerminatorKind::Assert { .. }

View File

@ -1,34 +1,64 @@
Trait objects like `Box<Trait>` can only be constructed when certain
requirements are satisfied by the trait in question.
For any given trait `Trait` there may be a related _type_ called the _trait
object type_ which is typically written as `dyn Trait`. In earlier editions of
Rust, trait object types were written as plain `Trait` (just the name of the
trait, written in type positions) but this was a bit too confusing, so we now
write `dyn Trait`.
Trait objects are a form of dynamic dispatch and use a dynamically sized type
for the inner type. So, for a given trait `Trait`, when `Trait` is treated as a
type, as in `Box<Trait>`, the inner type is 'unsized'. In such cases the boxed
pointer is a 'fat pointer' that contains an extra pointer to a table of methods
(among other things) for dynamic dispatch. This design mandates some
restrictions on the types of traits that are allowed to be used in trait
objects, which are collectively termed as 'object safety' rules.
Some traits are not allowed to be used as trait object types. The traits that
are allowed to be used as trait object types are called "object-safe" traits.
Attempting to use a trait object type for a trait that is not object-safe will
trigger error E0038.
Attempting to create a trait object for a non object-safe trait will trigger
this error.
Two general aspects of trait object types give rise to the restrictions:
There are various rules:
1. Trait object types are dynamically sized types (DSTs), and trait objects of
these types can only be accessed through pointers, such as `&dyn Trait` or
`Box<dyn Trait>`. The size of such a pointer is known, but the size of the
`dyn Trait` object pointed-to by the pointer is _opaque_ to code working
with it, and different tait objects with the same trait object type may
have different sizes.
### The trait cannot require `Self: Sized`
2. The pointer used to access a trait object is paired with an extra pointer
to a "virtual method table" or "vtable", which is used to implement dynamic
dispatch to the object's implementations of the trait's methods. There is a
single such vtable for each trait implementation, but different trait
objects with the same trait object type may point to vtables from different
implementations.
When `Trait` is treated as a type, the type does not implement the special
`Sized` trait, because the type does not have a known size at compile time and
can only be accessed behind a pointer. Thus, if we have a trait like the
following:
The specific conditions that violate object-safety follow, most of which relate
to missing size information and vtable polymorphism arising from these aspects.
### The trait requires `Self: Sized`
Traits that are declared as `Trait: Sized` or which otherwise inherit a
constraint of `Self:Sized` are not object-safe.
The reasoning behind this is somewhat subtle. It derives from the fact that Rust
requires (and defines) that every trait object type `dyn Trait` automatically
implements `Trait`. Rust does this to simplify error reporting and ease
interoperation between static and dynamic polymorphism. For example, this code
works:
```
trait Foo where Self: Sized {
trait Trait {
}
fn static_foo<T:Trait + ?Sized>(b: &T) {
}
fn dynamic_bar(a: &dyn Trait) {
static_foo(a)
}
```
We cannot create an object of type `Box<Foo>` or `&Foo` since in this case
`Self` would not be `Sized`.
This code works because `dyn Trait`, if it exists, always implements `Trait`.
However as we know, any `dyn Trait` is also unsized, and so it can never
implement a sized trait like `Trait:Sized`. So, rather than allow an exception
to the rule that `dyn Trait` always implements `Trait`, Rust chooses to prohibit
such a `dyn Trait` from existing at all.
Only unsized traits are considered object-safe.
Generally, `Self: Sized` is used to indicate that the trait should not be used
as a trait object. If the trait comes from your own crate, consider removing
@ -67,7 +97,7 @@ trait Trait {
fn foo(&self) -> Self;
}
fn call_foo(x: Box<Trait>) {
fn call_foo(x: Box<dyn Trait>) {
let y = x.foo(); // What type is y?
// ...
}
@ -76,7 +106,8 @@ fn call_foo(x: Box<Trait>) {
If only some methods aren't object-safe, you can add a `where Self: Sized` bound
on them to mark them as explicitly unavailable to trait objects. The
functionality will still be available to all other implementers, including
`Box<Trait>` which is itself sized (assuming you `impl Trait for Box<Trait>`).
`Box<dyn Trait>` which is itself sized (assuming you `impl Trait for Box<dyn
Trait>`).
```
trait Trait {
@ -115,7 +146,9 @@ impl Trait for u8 {
```
At compile time each implementation of `Trait` will produce a table containing
the various methods (and other items) related to the implementation.
the various methods (and other items) related to the implementation, which will
be used as the virtual method table for a `dyn Trait` object derived from that
implementation.
This works fine, but when the method gains generic parameters, we can have a
problem.
@ -174,7 +207,7 @@ Now, if we have the following code:
# impl Trait for u8 { fn foo<T>(&self, on: T) {} }
# impl Trait for bool { fn foo<T>(&self, on: T) {} }
# // etc.
fn call_foo(thing: Box<Trait>) {
fn call_foo(thing: Box<dyn Trait>) {
thing.foo(true); // this could be any one of the 8 types above
thing.foo(1);
thing.foo("hello");
@ -200,7 +233,7 @@ trait Trait {
```
If this is not an option, consider replacing the type parameter with another
trait object (e.g., if `T: OtherTrait`, use `on: Box<OtherTrait>`). If the
trait object (e.g., if `T: OtherTrait`, use `on: Box<dyn OtherTrait>`). If the
number of types you intend to feed to this method is limited, consider manually
listing out the methods of different types.
@ -226,7 +259,7 @@ trait Foo {
}
```
### The trait cannot contain associated constants
### Trait contains associated constants
Just like static functions, associated constants aren't stored on the method
table. If the trait or any subtrait contain an associated constant, they cannot
@ -248,7 +281,7 @@ trait Foo {
}
```
### The trait cannot use `Self` as a type parameter in the supertrait listing
### Trait uses `Self` as a type parameter in the supertrait listing
This is similar to the second sub-error, but subtler. It happens in situations
like the following:

View File

@ -1803,6 +1803,16 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Downcast(_, _) => false,
}
}
/// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
matches!(*self, Self::Downcast(_, x) if x == v)
}
/// Returns `true` if this is a `Field` projection with the given index.
pub fn is_field_to(&self, f: Field) -> bool {
matches!(*self, Self::Field(x, _) if x == f)
}
}
/// Alias for projections as they appear in places, where the base is a place

View File

@ -60,8 +60,10 @@ mod match_branches;
mod multiple_return_terminators;
mod normalize_array_len;
mod nrvo;
mod remove_false_edges;
mod remove_noop_landing_pads;
mod remove_storage_markers;
mod remove_uninit_drops;
mod remove_unneeded_drops;
mod remove_zsts;
mod required_consts;
@ -75,7 +77,7 @@ mod simplify_try;
mod uninhabited_enum_branching;
mod unreachable_prop;
use rustc_const_eval::transform::check_consts;
use rustc_const_eval::transform::check_consts::{self, ConstCx};
use rustc_const_eval::transform::promote_consts;
use rustc_const_eval::transform::validate;
use rustc_mir_dataflow::rustc_peek;
@ -444,8 +446,20 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
let (body, _) = tcx.mir_promoted(def);
let mut body = body.steal();
// IMPORTANT
remove_false_edges::RemoveFalseEdges.run_pass(tcx, &mut body);
// Do a little drop elaboration before const-checking if `const_precise_live_drops` is enabled.
//
// FIXME: Can't use `run_passes` for these, since `run_passes` SILENTLY DOES NOTHING IF THE MIR
// PHASE DOESN'T CHANGE.
if check_consts::post_drop_elaboration::checking_enabled(&ConstCx::new(tcx, &body)) {
simplify::SimplifyCfg::new("remove-false-edges").run_pass(tcx, &mut body);
remove_uninit_drops::RemoveUninitDrops.run_pass(tcx, &mut body);
check_consts::post_drop_elaboration::check_live_drops(tcx, &body);
}
run_post_borrowck_cleanup_passes(tcx, &mut body);
check_consts::post_drop_elaboration::check_live_drops(tcx, &body);
tcx.alloc_steal_mir(body)
}
@ -455,7 +469,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
// Remove all things only needed by analysis
&simplify_branches::SimplifyBranches::new("initial"),
&simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&cleanup_post_borrowck::CleanupNonCodegenStatements,
&simplify::SimplifyCfg::new("early-opt"),
@ -514,13 +528,13 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&instcombine::InstCombine,
&separate_const_switch::SeparateConstSwitch,
&const_prop::ConstProp,
&simplify_branches::SimplifyBranches::new("after-const-prop"),
&simplify_branches::SimplifyConstCondition::new("after-const-prop"),
&early_otherwise_branch::EarlyOtherwiseBranch,
&simplify_comparison_integral::SimplifyComparisonIntegral,
&simplify_try::SimplifyArmIdentity,
&simplify_try::SimplifyBranchSame,
&dest_prop::DestinationPropagation,
&simplify_branches::SimplifyBranches::new("final"),
&simplify_branches::SimplifyConstCondition::new("final"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("final"),
&nrvo::RenameReturnPlace,

View File

@ -0,0 +1,29 @@
use rustc_middle::mir::{Body, TerminatorKind};
use rustc_middle::ty::TyCtxt;
use crate::MirPass;
/// Removes `FalseEdge` and `FalseUnwind` terminators from the MIR.
///
/// These are only needed for borrow checking, and can be removed afterwards.
///
/// FIXME: This should probably have its own MIR phase.
pub struct RemoveFalseEdges;
impl<'tcx> MirPass<'tcx> for RemoveFalseEdges {
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
for block in body.basic_blocks_mut() {
let terminator = block.terminator_mut();
terminator.kind = match terminator.kind {
TerminatorKind::FalseEdge { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
}
TerminatorKind::FalseUnwind { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
}
_ => continue,
}
}
}
}

View File

@ -0,0 +1,171 @@
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{Body, Field, Rvalue, Statement, StatementKind, TerminatorKind};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, VariantDef};
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
use rustc_mir_dataflow::{self, move_path_children_matching, Analysis, MoveDataParamEnv};
use crate::MirPass;
/// Removes `Drop` and `DropAndReplace` terminators whose target is known to be uninitialized at
/// that point.
///
/// This is redundant with drop elaboration, but we need to do it prior to const-checking, and
/// running const-checking after drop elaboration makes it opimization dependent, causing issues
/// like [#90770].
///
/// [#90770]: https://github.com/rust-lang/rust/issues/90770
pub struct RemoveUninitDrops;
impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let param_env = tcx.param_env(body.source.def_id());
let Ok(move_data) = MoveData::gather_moves(body, tcx, param_env) else {
// We could continue if there are move errors, but there's not much point since our
// init data isn't complete.
return;
};
let mdpe = MoveDataParamEnv { move_data, param_env };
let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body)
.pass_name("remove_uninit_drops")
.iterate_to_fixpoint()
.into_results_cursor(body);
let mut to_remove = vec![];
for (bb, block) in body.basic_blocks().iter_enumerated() {
let terminator = block.terminator();
let (TerminatorKind::Drop { place, .. } | TerminatorKind::DropAndReplace { place, .. })
= &terminator.kind
else { continue };
maybe_inits.seek_before_primary_effect(body.terminator_loc(bb));
// If there's no move path for the dropped place, it's probably a `Deref`. Let it alone.
let LookupResult::Exact(mpi) = mdpe.move_data.rev_lookup.find(place.as_ref()) else {
continue;
};
let should_keep = is_needs_drop_and_init(
tcx,
param_env,
maybe_inits.get(),
&mdpe.move_data,
place.ty(body, tcx).ty,
mpi,
);
if !should_keep {
to_remove.push(bb)
}
}
for bb in to_remove {
let block = &mut body.basic_blocks_mut()[bb];
let (TerminatorKind::Drop { target, .. } | TerminatorKind::DropAndReplace { target, .. })
= &block.terminator().kind
else { unreachable!() };
// Replace block terminator with `Goto`.
let target = *target;
let old_terminator_kind = std::mem::replace(
&mut block.terminator_mut().kind,
TerminatorKind::Goto { target },
);
// If this is a `DropAndReplace`, we need to emulate the assignment to the return place.
if let TerminatorKind::DropAndReplace { place, value, .. } = old_terminator_kind {
block.statements.push(Statement {
source_info: block.terminator().source_info,
kind: StatementKind::Assign(Box::new((place, Rvalue::Use(value)))),
});
}
}
}
}
fn is_needs_drop_and_init(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
maybe_inits: &BitSet<MovePathIndex>,
move_data: &MoveData<'tcx>,
ty: Ty<'tcx>,
mpi: MovePathIndex,
) -> bool {
// No need to look deeper if the root is definitely uninit or if it has no `Drop` impl.
if !maybe_inits.contains(mpi) || !ty.needs_drop(tcx, param_env) {
return false;
}
let field_needs_drop_and_init = |(f, f_ty, mpi)| {
let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f));
let Some(mpi) = child else {
return f_ty.needs_drop(tcx, param_env);
};
is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi)
};
// This pass is only needed for const-checking, so it doesn't handle as many cases as
// `DropCtxt::open_drop`, since they aren't relevant in a const-context.
match ty.kind() {
ty::Adt(adt, substs) => {
let dont_elaborate = adt.is_union() || adt.is_manually_drop() || adt.has_dtor(tcx);
if dont_elaborate {
return true;
}
// Look at all our fields, or if we are an enum all our variants and their fields.
//
// If a field's projection *is not* present in `MoveData`, it has the same
// initializedness as its parent (maybe init).
//
// If its projection *is* present in `MoveData`, then the field may have been moved
// from separate from its parent. Recurse.
adt.variants.iter_enumerated().any(|(vid, variant)| {
// Enums have multiple variants, which are discriminated with a `Downcast` projection.
// Structs have a single variant, and don't use a `Downcast` projection.
let mpi = if adt.is_enum() {
let downcast =
move_path_children_matching(move_data, mpi, |x| x.is_downcast_to(vid));
let Some(dc_mpi) = downcast else {
return variant_needs_drop(tcx, param_env, substs, variant);
};
dc_mpi
} else {
mpi
};
variant
.fields
.iter()
.enumerate()
.map(|(f, field)| (Field::from_usize(f), field.ty(tcx, substs), mpi))
.any(field_needs_drop_and_init)
})
}
ty::Tuple(_) => ty
.tuple_fields()
.enumerate()
.map(|(f, f_ty)| (Field::from_usize(f), f_ty, mpi))
.any(field_needs_drop_and_init),
_ => true,
}
}
fn variant_needs_drop(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
substs: SubstsRef<'tcx>,
variant: &VariantDef,
) -> bool {
variant.fields.iter().any(|field| {
let f_ty = field.ty(tcx, substs);
f_ty.needs_drop(tcx, param_env)
})
}

View File

@ -1,4 +1,8 @@
//! This pass replaces a drop of a type that does not need dropping, with a goto
//! This pass replaces a drop of a type that does not need dropping, with a goto.
//!
//! When the MIR is built, we check `needs_drop` before emitting a `Drop` for a place. This pass is
//! useful because (unlike MIR building) it runs after type checking, so it can make use of
//! `Reveal::All` to provide more precies type information.
use crate::MirPass;
use rustc_middle::mir::*;

View File

@ -1,22 +1,21 @@
//! A pass that simplifies branches when their condition is known.
use crate::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use std::borrow::Cow;
pub struct SimplifyBranches {
/// A pass that replaces a branch with a goto when its condition is known.
pub struct SimplifyConstCondition {
label: String,
}
impl SimplifyBranches {
impl SimplifyConstCondition {
pub fn new(label: &str) -> Self {
SimplifyBranches { label: format!("SimplifyBranches-{}", label) }
SimplifyConstCondition { label: format!("SimplifyConstCondition-{}", label) }
}
}
impl<'tcx> MirPass<'tcx> for SimplifyBranches {
impl<'tcx> MirPass<'tcx> for SimplifyConstCondition {
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.label)
}
@ -53,12 +52,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches {
Some(v) if v == expected => TerminatorKind::Goto { target },
_ => continue,
},
TerminatorKind::FalseEdge { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
}
TerminatorKind::FalseUnwind { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
}
_ => continue,
};
}

View File

@ -1988,25 +1988,34 @@ impl<'a> Parser<'a> {
let lo = self.prev_token.span;
let cond = self.parse_cond_expr()?;
let missing_then_block_binop_span = || {
match cond.kind {
ExprKind::Binary(Spanned { span: binop_span, .. }, _, ref right)
if let ExprKind::Block(..) = right.kind => Some(binop_span),
_ => None
}
};
// Verify that the parsed `if` condition makes sense as a condition. If it is a block, then
// verify that the last statement is either an implicit return (no `;`) or an explicit
// return. This won't catch blocks with an explicit `return`, but that would be caught by
// the dead code lint.
let thn = if self.eat_keyword(kw::Else) || !cond.returns() {
self.error_missing_if_cond(lo, cond.span)
let thn = if self.token.is_keyword(kw::Else) || !cond.returns() {
if let Some(binop_span) = missing_then_block_binop_span() {
self.error_missing_if_then_block(lo, None, Some(binop_span)).emit();
self.mk_block_err(cond.span)
} else {
self.error_missing_if_cond(lo, cond.span)
}
} else {
let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
let not_block = self.token != token::OpenDelim(token::Brace);
let block = self.parse_block().map_err(|mut err| {
let block = self.parse_block().map_err(|err| {
if not_block {
err.span_label(lo, "this `if` expression has a condition, but no block");
if let ExprKind::Binary(_, _, ref right) = cond.kind {
if let ExprKind::Block(_, _) = right.kind {
err.help("maybe you forgot the right operand of the condition?");
}
}
self.error_missing_if_then_block(lo, Some(err), missing_then_block_binop_span())
} else {
err
}
err
})?;
self.error_on_if_block_attrs(lo, false, block.span, &attrs);
block
@ -2015,6 +2024,28 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs))
}
fn error_missing_if_then_block(
&self,
if_span: Span,
err: Option<DiagnosticBuilder<'a>>,
binop_span: Option<Span>,
) -> DiagnosticBuilder<'a> {
let msg = "this `if` expression has a condition, but no block";
let mut err = if let Some(mut err) = err {
err.span_label(if_span, msg);
err
} else {
self.struct_span_err(if_span, msg)
};
if let Some(binop_span) = binop_span {
err.span_help(binop_span, "maybe you forgot the right operand of the condition?");
}
err
}
fn error_missing_if_cond(&self, lo: Span, span: Span) -> P<ast::Block> {
let sp = self.sess.source_map().next_point(lo);
self.struct_span_err(sp, "missing condition for `if` expression")

View File

@ -1716,8 +1716,9 @@ pub(crate) struct Interner(Lock<InternerInner>);
// found that to regress performance up to 2% in some cases. This might be
// revisited after further improvements to `indexmap`.
//
// This type is private to prevent accidentally constructing more than one `Interner` on the same
// thread, which makes it easy to mixup `Symbol`s between `Interner`s.
// This type is private to prevent accidentally constructing more than one
// `Interner` on the same thread, which makes it easy to mixup `Symbol`s
// between `Interner`s.
#[derive(Default)]
struct InternerInner {
arena: DroplessArena,
@ -1743,14 +1744,20 @@ impl Interner {
let name = Symbol::new(inner.strings.len() as u32);
// `from_utf8_unchecked` is safe since we just allocated a `&str` which is known to be
// UTF-8.
// SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena,
// and immediately convert the clone back to `&[u8], all because there
// is no `inner.arena.alloc_str()` method. This is clearly safe.
let string: &str =
unsafe { str::from_utf8_unchecked(inner.arena.alloc_slice(string.as_bytes())) };
// It is safe to extend the arena allocation to `'static` because we only access
// these while the arena is still alive.
// SAFETY: we can extend the arena allocation to `'static` because we
// only access these while the arena is still alive.
let string: &'static str = unsafe { &*(string as *const str) };
inner.strings.push(string);
// This second hash table lookup can be avoided by using `RawEntryMut`,
// but this code path isn't hot enough for it to be worth it. See
// #91445 for details.
inner.names.insert(string, name);
name
}

View File

@ -521,7 +521,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
},
ty::PredicateKind::TypeOutlives(pred) => {
if pred.0.is_known_global() {
// A global type with no late-bound regions can only
// contain the "'static" lifetime (any other lifetime
// would either be late-bound or local), so it is guaranteed
// to outlive any other lifetime
if pred.0.is_global(self.infcx.tcx) && !pred.0.has_late_bound_regions() {
Ok(EvaluatedToOk)
} else {
Ok(EvaluatedToOkModuloRegions)

View File

@ -46,6 +46,7 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Pos};
use rustc_trait_selection::traits::{self, ObligationCauseCode};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@ -2063,7 +2064,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(span),
);
} else {
err.help("methods are immutable and cannot be assigned to");
let mut found = false;
if let ty::RawPtr(ty_and_mut) = expr_t.kind() {
if let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() {
if adt_def.variants.len() == 1
&& adt_def
.variants
.iter()
.next()
.unwrap()
.fields
.iter()
.any(|f| f.ident == field)
{
if let Some(dot_loc) = expr_snippet.rfind('.') {
found = true;
err.span_suggestion(
expr.span.with_hi(expr.span.lo() + BytePos::from_usize(dot_loc)),
"to access the field, dereference first",
format!("(*{})", &expr_snippet[0..dot_loc]),
Applicability::MaybeIncorrect,
);
}
}
}
}
if !found {
err.help("methods are immutable and cannot be assigned to");
}
}
err.emit();

View File

@ -2330,6 +2330,7 @@ macro_rules! empty_max_mut {
};
}
#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations)
take_tests! {
slice: &[(); usize::MAX], method: take,
(take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]),
@ -2337,6 +2338,7 @@ take_tests! {
(take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX),
}
#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations)
take_tests! {
slice: &mut [(); usize::MAX], method: take_mut,
(take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]),

View File

@ -440,12 +440,18 @@ impl Error {
/// `GetLastError` on Windows) and will return a corresponding instance of
/// [`Error`] for the error code.
///
/// This should be called immediately after a call to a platform function,
/// otherwise the state of the error value is indeterminate. In particular,
/// other standard library functions may call platform functions that may
/// (or may not) reset the error value even if they succeed.
///
/// # Examples
///
/// ```
/// use std::io::Error;
///
/// println!("last OS error: {:?}", Error::last_os_error());
/// let os_error = Error::last_os_error();
/// println!("last OS error: {:?}", os_error);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]

View File

@ -12,6 +12,13 @@
//! [`PathBuf`]; note that the paths may differ syntactically by the
//! normalization described in the documentation for the [`components`] method.
//!
//! ## Case sensitivity
//!
//! Unless otherwise indicated path methods that do not access the filesystem,
//! such as [`Path::starts_with`] and [`Path::ends_with`], are case sensitive no
//! matter the platform or filesystem. An exception to this is made for Windows
//! drive letters.
//!
//! ## Simple usage
//!
//! Path manipulation includes both parsing components from slices and building

View File

@ -749,11 +749,42 @@ fn clean_fn_or_proc_macro(
} else {
hir::Constness::NotConst
};
clean_fn_decl_legacy_const_generics(&mut func, attrs);
FunctionItem(func)
}
}
}
/// This is needed to make it more "readable" when documenting functions using
/// `rustc_legacy_const_generics`. More information in
/// <https://github.com/rust-lang/rust/issues/83167>.
fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) {
for meta_item_list in attrs
.iter()
.filter(|a| a.has_name(sym::rustc_legacy_const_generics))
.filter_map(|a| a.meta_item_list())
{
for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.literal()).enumerate() {
match literal.kind {
ast::LitKind::Int(a, _) => {
let gen = func.generics.params.remove(0);
if let GenericParamDef { name, kind: GenericParamDefKind::Const { ty, .. } } =
gen
{
func.decl
.inputs
.values
.insert(a as _, Argument { name, type_: *ty, is_const: true });
} else {
panic!("unexpected non const in position {}", pos);
}
}
_ => panic!("invalid arg index"),
}
}
}
}
impl<'a> Clean<Function> for (&'a hir::FnSig<'a>, &'a hir::Generics<'a>, hir::BodyId) {
fn clean(&self, cx: &mut DocContext<'_>) -> Function {
let (generics, decl) = enter_impl_trait(cx, |cx| {
@ -779,7 +810,7 @@ impl<'a> Clean<Arguments> for (&'a [hir::Ty<'a>], &'a [Ident]) {
if name.is_empty() {
name = kw::Underscore;
}
Argument { name, type_: ty.clean(cx) }
Argument { name, type_: ty.clean(cx), is_const: false }
})
.collect(),
}
@ -798,6 +829,7 @@ impl<'a> Clean<Arguments> for (&'a [hir::Ty<'a>], hir::BodyId) {
.map(|(i, ty)| Argument {
name: name_from_pat(body.params[i].pat),
type_: ty.clean(cx),
is_const: false,
})
.collect(),
}
@ -828,6 +860,7 @@ impl<'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) {
.map(|t| Argument {
type_: t.clean(cx),
name: names.next().map_or(kw::Empty, |i| i.name),
is_const: false,
})
.collect(),
},

View File

@ -1353,6 +1353,9 @@ crate struct Arguments {
crate struct Argument {
crate type_: Type,
crate name: Symbol,
/// This field is used to represent "const" arguments from the `rustc_legacy_const_generics`
/// feature. More information in <https://github.com/rust-lang/rust/issues/83167>.
crate is_const: bool,
}
#[derive(Clone, PartialEq, Debug)]

View File

@ -1177,6 +1177,10 @@ impl clean::FnDecl {
args.push_str(" <br>");
args_plain.push(' ');
}
if input.is_const {
args.push_str("const ");
args_plain.push_str("const ");
}
if !input.name.is_empty() {
args.push_str(&format!("{}: ", input.name));
args_plain.push_str(&format!("{}: ", input.name));

View File

@ -1,5 +1,5 @@
- // MIR for `main` before SimplifyBranches-after-const-prop
+ // MIR for `main` after SimplifyBranches-after-const-prop
- // MIR for `main` before SimplifyConstCondition-after-const-prop
+ // MIR for `main` after SimplifyConstCondition-after-const-prop
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/switch_int.rs:6:11: 6:11

View File

@ -2,7 +2,7 @@
fn foo(_: i32) { }
// EMIT_MIR switch_int.main.ConstProp.diff
// EMIT_MIR switch_int.main.SimplifyBranches-after-const-prop.diff
// EMIT_MIR switch_int.main.SimplifyConstCondition-after-const-prop.diff
fn main() {
match 1 {
1 => foo(0),

View File

@ -11,7 +11,7 @@ pub enum ViewportPercentageLength {
}
// EMIT_MIR early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff
// EMIT_MIR early_otherwise_branch_68867.try_sum EarlyOtherwiseBranch.before SimplifyBranches-final.after
// EMIT_MIR early_otherwise_branch_68867.try_sum EarlyOtherwiseBranch.before SimplifyConstCondition-final.after
#[no_mangle]
pub extern "C" fn try_sum(
x: &ViewportPercentageLength,

View File

@ -1,5 +1,5 @@
- // MIR for `try_sum` before EarlyOtherwiseBranch
+ // MIR for `try_sum` after SimplifyBranches-final
+ // MIR for `try_sum` after SimplifyConstCondition-final
fn try_sum(_1: &ViewportPercentageLength, _2: &ViewportPercentageLength) -> Result<ViewportPercentageLength, ()> {
debug x => _1; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:17:5: 17:6

View File

@ -1,5 +1,5 @@
- // MIR for `main` before SimplifyBranches-after-const-prop
+ // MIR for `main` after SimplifyBranches-after-const-prop
- // MIR for `main` before SimplifyConstCondition-after-const-prop
+ // MIR for `main` after SimplifyConstCondition-after-const-prop
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/simplify_if.rs:5:11: 5:11

View File

@ -1,7 +1,7 @@
#[inline(never)]
fn noop() {}
// EMIT_MIR simplify_if.main.SimplifyBranches-after-const-prop.diff
// EMIT_MIR simplify_if.main.SimplifyConstCondition-after-const-prop.diff
fn main() {
if false {
noop();

View File

@ -0,0 +1,16 @@
#![crate_name = "foo"]
#![feature(rustc_attrs)]
// @has 'foo/fn.foo.html'
// @has - '//*[@class="rust fn"]' 'fn foo(x: usize, const Y: usize, z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1)]
pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
[x, Y, z]
}
// @has 'foo/fn.bar.html'
// @has - '//*[@class="rust fn"]' 'fn bar(x: usize, const Y: usize, const Z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1, 2)]
pub fn bar<const Y: usize, const Z: usize>(x: usize) -> [usize; 3] {
[x, Y, z]
}

View File

@ -0,0 +1,17 @@
// check-fail
#![feature(const_precise_live_drops)]
struct S;
impl Drop for S {
fn drop(&mut self) {
println!("Hello!");
}
}
const fn foo() {
let s = S; //~ destructor
}
fn main() {}

View File

@ -0,0 +1,9 @@
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/drop_zst.rs:14:9
|
LL | let s = S;
| ^ constant functions cannot evaluate destructors
error: aborting due to previous error
For more information about this error, try `rustc --explain E0493`.

View File

@ -7,7 +7,11 @@ LL | if 5 == {
LL | }
| ^ expected `{`
|
= help: maybe you forgot the right operand of the condition?
help: maybe you forgot the right operand of the condition?
--> $DIR/if-without-block.rs:3:10
|
LL | if 5 == {
| ^^
error: aborting due to previous error

View File

@ -0,0 +1,22 @@
// check-pass
trait Trait {
type Type;
}
impl<T> Trait for T {
type Type = ();
}
fn f<'a, 'b>(_: <&'a &'b () as Trait>::Type)
where
'a: 'a,
'b: 'b,
{
}
fn g<'a, 'b>() {
f::<'a, 'b>(());
}
fn main() {}

View File

@ -0,0 +1,19 @@
// edition:2018
// check-pass
#![feature(unboxed_closures)]
use std::future::Future;
async fn wrapper<F>(f: F)
where for<'a> F: FnOnce<(&'a mut i32,)>,
for<'a> <F as FnOnce<(&'a mut i32,)>>::Output: Future<Output=()> + 'a
{
let mut i = 41;
f(&mut i).await;
}
async fn add_one(i: &mut i32) {
*i = *i + 1;
}
fn main() {}

View File

@ -0,0 +1,10 @@
// Regression test for issue #91421.
fn main() {
let value = if true && {
//~^ ERROR: this `if` expression has a condition, but no block
//~| HELP: maybe you forgot the right operand of the condition?
3
//~^ ERROR: mismatched types [E0308]
} else { 4 };
}

View File

@ -0,0 +1,21 @@
error: this `if` expression has a condition, but no block
--> $DIR/issue-91421.rs:4:17
|
LL | let value = if true && {
| ^^
|
help: maybe you forgot the right operand of the condition?
--> $DIR/issue-91421.rs:4:25
|
LL | let value = if true && {
| ^^
error[E0308]: mismatched types
--> $DIR/issue-91421.rs:7:9
|
LL | 3
| ^ expected `bool`, found integer
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,55 @@
// revisions: with_clause without_clause
// Tests that `EvaluatedToOkModuloRegions` from a projection sub-obligation
// is correctly propagated
#![feature(rustc_attrs)]
trait MyTrait {
type Assoc;
}
struct MyStruct;
impl MyTrait for MyStruct {
// Evaluating this projection will result in `EvaluatedToOkModuloRegions`
// (when `with_clause` is enabled)
type Assoc = <Bar as MyTrait>::Assoc;
}
struct Bar;
// The `where` clause on this impl will cause us to produce `EvaluatedToOkModuloRegions`
// when evaluating a projection involving this impl
#[cfg(with_clause)]
impl MyTrait for Bar where for<'b> &'b (): 'b {
type Assoc = bool;
}
// This impl tests that the `EvaluatedToOkModuoRegions` result that we get
// is really due to the `where` clause on the `with_clause` impl
#[cfg(without_clause)]
impl MyTrait for Bar {
type Assoc = bool;
}
// The implementation of `#[rustc_evaluate_where_clauses]` doesn't perform
// normalization, so we need to place the projection predicate behind a normal
// trait predicate
struct Helper {}
trait HelperTrait {}
impl HelperTrait for Helper where <MyStruct as MyTrait>::Assoc: Sized {}
// Evaluating this 'where' clause will (recursively) end up evaluating
// `for<'b> &'b (): 'b`, which will produce `EvaluatedToOkModuloRegions`
#[rustc_evaluate_where_clauses]
fn test(val: MyStruct) where Helper: HelperTrait {
panic!()
}
fn foo(val: MyStruct) {
test(val);
//[with_clause]~^ ERROR evaluate(Binder(TraitPredicate(<Helper as HelperTrait>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
//[without_clause]~^^ ERROR evaluate(Binder(TraitPredicate(<Helper as HelperTrait>, polarity:Positive), [])) = Ok(EvaluatedToOk)
}
fn main() {}

View File

@ -0,0 +1,11 @@
error: evaluate(Binder(TraitPredicate(<Helper as HelperTrait>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
--> $DIR/project-modulo-regions.rs:50:5
|
LL | fn test(val: MyStruct) where Helper: HelperTrait {
| ----------- predicate
...
LL | test(val);
| ^^^^
error: aborting due to previous error

View File

@ -0,0 +1,11 @@
error: evaluate(Binder(TraitPredicate(<Helper as HelperTrait>, polarity:Positive), [])) = Ok(EvaluatedToOk)
--> $DIR/project-modulo-regions.rs:50:5
|
LL | fn test(val: MyStruct) where Helper: HelperTrait {
| ----------- predicate
...
LL | test(val);
| ^^^^
error: aborting due to previous error

View File

@ -0,0 +1,15 @@
// Regression test for issue #91210.
// run-rustfix
#![allow(unused)]
struct Foo { read: i32 }
unsafe fn blah(x: *mut Foo) {
(*x).read = 4;
//~^ ERROR: attempted to take value of method
//~| HELP: to access the field, dereference first
}
fn main() {}

View File

@ -0,0 +1,15 @@
// Regression test for issue #91210.
// run-rustfix
#![allow(unused)]
struct Foo { read: i32 }
unsafe fn blah(x: *mut Foo) {
x.read = 4;
//~^ ERROR: attempted to take value of method
//~| HELP: to access the field, dereference first
}
fn main() {}

View File

@ -0,0 +1,11 @@
error[E0615]: attempted to take value of method `read` on type `*mut Foo`
--> $DIR/issue-91210-ptr-method.rs:10:7
|
LL | x.read = 4;
| - ^^^^ method, not a field
| |
| help: to access the field, dereference first: `(*x)`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0615`.