Auto merge of #139269 - matthiaskrgr:rollup-pk78gig, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #138992 (literal pattern lowering: use the pattern's type instead of the literal's in `const_to_pat`)
 - #139211 (interpret: add a version of run_for_validation for &self)
 - #139235 (`AstValidator` tweaks)
 - #139237 (Add a dep kind for use of the anon node with zero dependencies)
 - #139260 (Add dianqk to codegen reviewers)
 - #139264 (Fix two incorrect turbofish suggestions)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-02 18:39:21 +00:00
commit d5b4c2e4f1
22 changed files with 477 additions and 264 deletions

View file

@ -47,14 +47,14 @@ enum SelfSemantic {
} }
enum TraitOrTraitImpl { enum TraitOrTraitImpl {
Trait { span: Span, constness: Option<Span> }, Trait { span: Span, constness_span: Option<Span> },
TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
} }
impl TraitOrTraitImpl { impl TraitOrTraitImpl {
fn constness(&self) -> Option<Span> { fn constness(&self) -> Option<Span> {
match self { match self {
Self::Trait { constness: Some(span), .. } Self::Trait { constness_span: Some(span), .. }
| Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span), | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
_ => None, _ => None,
} }
@ -66,7 +66,7 @@ struct AstValidator<'a> {
features: &'a Features, features: &'a Features,
/// The span of the `extern` in an `extern { ... }` block, if any. /// The span of the `extern` in an `extern { ... }` block, if any.
extern_mod: Option<Span>, extern_mod_span: Option<Span>,
outer_trait_or_trait_impl: Option<TraitOrTraitImpl>, outer_trait_or_trait_impl: Option<TraitOrTraitImpl>,
@ -75,7 +75,7 @@ struct AstValidator<'a> {
/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`. /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
/// Nested `impl Trait` _is_ allowed in associated type position, /// Nested `impl Trait` _is_ allowed in associated type position,
/// e.g., `impl Iterator<Item = impl Debug>`. /// e.g., `impl Iterator<Item = impl Debug>`.
outer_impl_trait: Option<Span>, outer_impl_trait_span: Option<Span>,
disallow_tilde_const: Option<TildeConstReason>, disallow_tilde_const: Option<TildeConstReason>,
@ -96,17 +96,22 @@ impl<'a> AstValidator<'a> {
trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl { trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
constness, constness,
polarity, polarity,
trait_ref: trait_ref.path.span, trait_ref_span: trait_ref.path.span,
}), }),
); );
f(self); f(self);
self.outer_trait_or_trait_impl = old; self.outer_trait_or_trait_impl = old;
} }
fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) { fn with_in_trait(
&mut self,
span: Span,
constness_span: Option<Span>,
f: impl FnOnce(&mut Self),
) {
let old = mem::replace( let old = mem::replace(
&mut self.outer_trait_or_trait_impl, &mut self.outer_trait_or_trait_impl,
Some(TraitOrTraitImpl::Trait { span, constness }), Some(TraitOrTraitImpl::Trait { span, constness_span }),
); );
f(self); f(self);
self.outer_trait_or_trait_impl = old; self.outer_trait_or_trait_impl = old;
@ -170,10 +175,10 @@ impl<'a> AstValidator<'a> {
Err(errors::WhereClauseBeforeTypeAlias { span, sugg }) Err(errors::WhereClauseBeforeTypeAlias { span, sugg })
} }
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) { fn with_impl_trait(&mut self, outer_span: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.outer_impl_trait, outer); let old = mem::replace(&mut self.outer_impl_trait_span, outer_span);
f(self); f(self);
self.outer_impl_trait = old; self.outer_impl_trait_span = old;
} }
// Mirrors `visit::walk_ty`, but tracks relevant state. // Mirrors `visit::walk_ty`, but tracks relevant state.
@ -258,21 +263,22 @@ impl<'a> AstValidator<'a> {
&& let TraitOrTraitImpl::TraitImpl { && let TraitOrTraitImpl::TraitImpl {
constness: Const::No, constness: Const::No,
polarity: ImplPolarity::Positive, polarity: ImplPolarity::Positive,
trait_ref, trait_ref_span,
.. ..
} = parent } = parent
{ {
Some(trait_ref.shrink_to_lo()) Some(trait_ref_span.shrink_to_lo())
} else { } else {
None None
}; };
let make_trait_const_sugg = let make_trait_const_sugg = if const_trait_impl
if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent { && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent
Some(span.shrink_to_lo()) {
} else { Some(span.shrink_to_lo())
None } else {
}; None
};
let parent_constness = parent.constness(); let parent_constness = parent.constness();
self.dcx().emit_err(errors::TraitFnConst { self.dcx().emit_err(errors::TraitFnConst {
@ -448,13 +454,13 @@ impl<'a> AstValidator<'a> {
check_where_clause(where_clauses.after); check_where_clause(where_clauses.after);
} }
fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) { fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
let Some(body) = body else { let Some(body_span) = body_span else {
return; return;
}; };
self.dcx().emit_err(errors::BodyInExtern { self.dcx().emit_err(errors::BodyInExtern {
span: ident.span, span: ident.span,
body, body: body_span,
block: self.current_extern_span(), block: self.current_extern_span(),
kind, kind,
}); });
@ -473,7 +479,7 @@ impl<'a> AstValidator<'a> {
} }
fn current_extern_span(&self) -> Span { fn current_extern_span(&self) -> Span {
self.sess.source_map().guess_head_span(self.extern_mod.unwrap()) self.sess.source_map().guess_head_span(self.extern_mod_span.unwrap())
} }
/// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
@ -583,9 +589,10 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name }); self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name });
} }
fn deny_generic_params(&self, generics: &Generics, ident: Span) { fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
if !generics.params.is_empty() { if !generics.params.is_empty() {
self.dcx().emit_err(errors::AutoTraitGeneric { span: generics.span, ident }); self.dcx()
.emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span });
} }
} }
@ -605,11 +612,11 @@ impl<'a> AstValidator<'a> {
} }
} }
fn deny_items(&self, trait_items: &[P<AssocItem>], ident: Span) { fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
if !trait_items.is_empty() { if !trait_items.is_empty() {
let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect(); let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect();
let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span); let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident }); self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span });
} }
} }
@ -694,7 +701,7 @@ impl<'a> AstValidator<'a> {
} }
} }
TyKind::ImplTrait(_, bounds) => { TyKind::ImplTrait(_, bounds) => {
if let Some(outer_impl_trait_sp) = self.outer_impl_trait { if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span {
self.dcx().emit_err(errors::NestedImplTrait { self.dcx().emit_err(errors::NestedImplTrait {
span: ty.span, span: ty.span,
outer: outer_impl_trait_sp, outer: outer_impl_trait_sp,
@ -727,6 +734,19 @@ impl<'a> AstValidator<'a> {
) )
} }
} }
// Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
fn visit_attrs_vis(&mut self, attrs: &'a AttrVec, vis: &'a Visibility) {
walk_list!(self, visit_attribute, attrs);
self.visit_vis(vis);
}
// Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
fn visit_attrs_vis_ident(&mut self, attrs: &'a AttrVec, vis: &'a Visibility, ident: &'a Ident) {
walk_list!(self, visit_attribute, attrs);
self.visit_vis(vis);
self.visit_ident(ident);
}
} }
/// Checks that generic parameters are in the correct order, /// Checks that generic parameters are in the correct order,
@ -834,36 +854,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self_ty, self_ty,
items, items,
}) => { }) => {
self.visit_attrs_vis(&item.attrs, &item.vis);
self.visibility_not_permitted(
&item.vis,
errors::VisibilityNotPermittedNote::TraitImpl,
);
if let TyKind::Dummy = self_ty.kind {
// Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
// which isn't allowed. Not a problem for this obscure, obsolete syntax.
self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
}
if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) {
self.dcx().emit_err(errors::UnsafeNegativeImpl {
span: sp.to(t.path.span),
negative: sp,
r#unsafe: span,
});
}
let disallowed = matches!(constness, Const::No)
.then(|| TildeConstReason::TraitImpl { span: item.span });
self.with_tilde_const(disallowed, |this| this.visit_generics(generics));
self.visit_trait_ref(t);
self.visit_ty(self_ty);
self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| { self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
this.visibility_not_permitted(
&item.vis,
errors::VisibilityNotPermittedNote::TraitImpl,
);
if let TyKind::Dummy = self_ty.kind {
// Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
// which isn't allowed. Not a problem for this obscure, obsolete syntax.
this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
}
if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity)
{
this.dcx().emit_err(errors::UnsafeNegativeImpl {
span: sp.to(t.path.span),
negative: sp,
r#unsafe: span,
});
}
this.visit_vis(&item.vis);
let disallowed = matches!(constness, Const::No)
.then(|| TildeConstReason::TraitImpl { span: item.span });
this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
this.visit_trait_ref(t);
this.visit_ty(self_ty);
walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true }); walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
}); });
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
} }
ItemKind::Impl(box Impl { ItemKind::Impl(box Impl {
safety, safety,
@ -883,39 +900,36 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
only_trait, only_trait,
}; };
self.with_in_trait_impl(None, |this| { self.visit_attrs_vis(&item.attrs, &item.vis);
this.visibility_not_permitted( self.visibility_not_permitted(
&item.vis, &item.vis,
errors::VisibilityNotPermittedNote::IndividualImplItems, errors::VisibilityNotPermittedNote::IndividualImplItems,
); );
if let &Safety::Unsafe(span) = safety { if let &Safety::Unsafe(span) = safety {
this.dcx().emit_err(errors::InherentImplCannotUnsafe { self.dcx().emit_err(errors::InherentImplCannotUnsafe {
span: self_ty.span, span: self_ty.span,
annotation_span: span, annotation_span: span,
annotation: "unsafe", annotation: "unsafe",
self_ty: self_ty.span, self_ty: self_ty.span,
}); });
} }
if let &ImplPolarity::Negative(span) = polarity { if let &ImplPolarity::Negative(span) = polarity {
this.dcx().emit_err(error(span, "negative", false)); self.dcx().emit_err(error(span, "negative", false));
} }
if let &Defaultness::Default(def_span) = defaultness { if let &Defaultness::Default(def_span) = defaultness {
this.dcx().emit_err(error(def_span, "`default`", true)); self.dcx().emit_err(error(def_span, "`default`", true));
} }
if let &Const::Yes(span) = constness { if let &Const::Yes(span) = constness {
this.dcx().emit_err(error(span, "`const`", true)); self.dcx().emit_err(error(span, "`const`", true));
} }
this.visit_vis(&item.vis); self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
this.with_tilde_const( this.visit_generics(generics)
Some(TildeConstReason::Impl { span: item.span }), });
|this| this.visit_generics(generics), self.visit_ty(self_ty);
); self.with_in_trait_impl(None, |this| {
this.visit_ty(self_ty);
walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false }); walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false });
}); });
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
} }
ItemKind::Fn( ItemKind::Fn(
func @ box Fn { func @ box Fn {
@ -928,6 +942,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
define_opaque: _, define_opaque: _,
}, },
) => { ) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness); self.check_defaultness(item.span, *defaultness);
let is_intrinsic = let is_intrinsic =
@ -955,43 +970,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}); });
} }
self.visit_vis(&item.vis);
self.visit_ident(ident);
let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func); let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
self.visit_fn(kind, item.span, item.id); self.visit_fn(kind, item.span, item.id);
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again.
} }
ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => { ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
self.visibility_not_permitted(
&item.vis,
errors::VisibilityNotPermittedNote::IndividualForeignItems,
);
if &Safety::Default == safety {
if item.span.at_least_rust_2024() {
self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
} else {
self.lint_buffer.buffer_lint(
MISSING_UNSAFE_ON_EXTERN,
item.id,
item.span,
BuiltinLintDiag::MissingUnsafeOnExtern {
suggestion: item.span.shrink_to_lo(),
},
);
}
}
if abi.is_none() {
self.maybe_lint_missing_abi(*extern_span, item.id);
}
self.with_in_extern_mod(*safety, |this| { self.with_in_extern_mod(*safety, |this| {
let old_item = mem::replace(&mut this.extern_mod, Some(item.span));
this.visibility_not_permitted(
&item.vis,
errors::VisibilityNotPermittedNote::IndividualForeignItems,
);
if &Safety::Default == safety {
if item.span.at_least_rust_2024() {
this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
} else {
this.lint_buffer.buffer_lint(
MISSING_UNSAFE_ON_EXTERN,
item.id,
item.span,
BuiltinLintDiag::MissingUnsafeOnExtern {
suggestion: item.span.shrink_to_lo(),
},
);
}
}
if abi.is_none() {
this.maybe_lint_missing_abi(*extern_span, item.id);
}
visit::walk_item(this, item); visit::walk_item(this, item);
this.extern_mod = old_item;
}); });
return; // Avoid visiting again. self.extern_mod_span = old_item;
} }
ItemKind::Enum(_, def, _) => { ItemKind::Enum(_, def, _) => {
for variant in &def.variants { for variant in &def.variants {
@ -1006,34 +1016,31 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
); );
} }
} }
visit::walk_item(self, item)
} }
ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => { ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
let is_const_trait = let is_const_trait =
attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span); attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
self.with_in_trait(item.span, is_const_trait, |this| { if *is_auto == IsAuto::Yes {
if *is_auto == IsAuto::Yes { // Auto traits cannot have generics, super traits nor contain items.
// Auto traits cannot have generics, super traits nor contain items. self.deny_generic_params(generics, ident.span);
this.deny_generic_params(generics, ident.span); self.deny_super_traits(bounds, ident.span);
this.deny_super_traits(bounds, ident.span); self.deny_where_clause(&generics.where_clause, ident.span);
this.deny_where_clause(&generics.where_clause, ident.span); self.deny_items(items, ident.span);
this.deny_items(items, ident.span); }
}
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
// context for the supertraits. // context for the supertraits.
this.visit_vis(&item.vis); let disallowed =
this.visit_ident(ident); is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span });
let disallowed = is_const_trait self.with_tilde_const(disallowed, |this| {
.is_none() this.visit_generics(generics);
.then(|| TildeConstReason::Trait { span: item.span }); walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
this.with_tilde_const(disallowed, |this| { });
this.visit_generics(generics); self.with_in_trait(item.span, is_const_trait, |this| {
walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
});
walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
}); });
walk_list!(self, visit_attribute, &item.attrs);
return; // Avoid visiting again
} }
ItemKind::Mod(safety, ident, mod_kind) => { ItemKind::Mod(safety, ident, mod_kind) => {
if let &Safety::Unsafe(span) = safety { if let &Safety::Unsafe(span) = safety {
@ -1045,18 +1052,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
{ {
self.check_mod_file_item_asciionly(*ident); self.check_mod_file_item_asciionly(*ident);
} }
visit::walk_item(self, item)
} }
ItemKind::Struct(ident, vdata, generics) => match vdata { ItemKind::Struct(ident, vdata, generics) => match vdata {
VariantData::Struct { fields, .. } => { VariantData::Struct { fields, .. } => {
self.visit_vis(&item.vis); self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.visit_ident(ident);
self.visit_generics(generics); self.visit_generics(generics);
// Permit `Anon{Struct,Union}` as field type. // Permit `Anon{Struct,Union}` as field type.
walk_list!(self, visit_struct_field_def, fields); walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
} }
_ => {} _ => visit::walk_item(self, item),
}, },
ItemKind::Union(ident, vdata, generics) => { ItemKind::Union(ident, vdata, generics) => {
if vdata.fields().is_empty() { if vdata.fields().is_empty() {
@ -1064,15 +1069,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
} }
match vdata { match vdata {
VariantData::Struct { fields, .. } => { VariantData::Struct { fields, .. } => {
self.visit_vis(&item.vis); self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.visit_ident(ident);
self.visit_generics(generics); self.visit_generics(generics);
// Permit `Anon{Struct,Union}` as field type. // Permit `Anon{Struct,Union}` as field type.
walk_list!(self, visit_struct_field_def, fields); walk_list!(self, visit_struct_field_def, fields);
walk_list!(self, visit_attribute, &item.attrs);
return;
} }
_ => {} _ => visit::walk_item(self, item),
} }
} }
ItemKind::Const(box ConstItem { defaultness, expr, .. }) => { ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
@ -1083,6 +1085,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
replace_span: self.ending_semi_or_hi(item.span), replace_span: self.ending_semi_or_hi(item.span),
}); });
} }
visit::walk_item(self, item);
} }
ItemKind::Static(box StaticItem { expr, safety, .. }) => { ItemKind::Static(box StaticItem { expr, safety, .. }) => {
self.check_item_safety(item.span, *safety); self.check_item_safety(item.span, *safety);
@ -1096,6 +1099,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
replace_span: self.ending_semi_or_hi(item.span), replace_span: self.ending_semi_or_hi(item.span),
}); });
} }
visit::walk_item(self, item);
} }
ItemKind::TyAlias( ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
@ -1119,11 +1123,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
help: self.sess.is_nightly_build(), help: self.sess.is_nightly_build(),
}); });
} }
visit::walk_item(self, item);
} }
_ => {} _ => visit::walk_item(self, item),
} }
visit::walk_item(self, item);
} }
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
@ -1488,10 +1491,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|| ctxt == AssocCtxt::Trait || ctxt == AssocCtxt::Trait
|| matches!(func.sig.header.constness, Const::Yes(_)) => || matches!(func.sig.header.constness, Const::Yes(_)) =>
{ {
self.visit_vis(&item.vis); self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
self.visit_ident(&func.ident);
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func); let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
walk_list!(self, visit_attribute, &item.attrs);
self.visit_fn(kind, item.span, item.id); self.visit_fn(kind, item.span, item.id);
} }
AssocItemKind::Type(_) => { AssocItemKind::Type(_) => {
@ -1596,7 +1597,7 @@ fn deny_equality_constraints(
generics.where_clause.span generics.where_clause.span
} else { } else {
let mut span = predicate_span; let mut span = predicate_span;
let mut prev: Option<Span> = None; let mut prev_span: Option<Span> = None;
let mut preds = generics.where_clause.predicates.iter().peekable(); let mut preds = generics.where_clause.predicates.iter().peekable();
// Find the predicate that shouldn't have been in the where bound list. // Find the predicate that shouldn't have been in the where bound list.
while let Some(pred) = preds.next() { while let Some(pred) = preds.next() {
@ -1606,12 +1607,12 @@ fn deny_equality_constraints(
if let Some(next) = preds.peek() { if let Some(next) = preds.peek() {
// This is the first predicate, remove the trailing comma as well. // This is the first predicate, remove the trailing comma as well.
span = span.with_hi(next.span.lo()); span = span.with_hi(next.span.lo());
} else if let Some(prev) = prev { } else if let Some(prev_span) = prev_span {
// Remove the previous comma as well. // Remove the previous comma as well.
span = span.with_lo(prev.hi()); span = span.with_lo(prev_span.hi());
} }
} }
prev = Some(pred.span); prev_span = Some(pred.span);
} }
span span
}; };
@ -1686,10 +1687,10 @@ pub fn check_crate(
let mut validator = AstValidator { let mut validator = AstValidator {
sess, sess,
features, features,
extern_mod: None, extern_mod_span: None,
outer_trait_or_trait_impl: None, outer_trait_or_trait_impl: None,
has_proc_macro_decls: false, has_proc_macro_decls: false,
outer_impl_trait: None, outer_impl_trait_span: None,
disallow_tilde_const: Some(TildeConstReason::Item), disallow_tilde_const: Some(TildeConstReason::Item),
extern_mod_safety: None, extern_mod_safety: None,
lint_buffer: lints, lint_buffer: lints,

View file

@ -8,8 +8,9 @@
use std::assert_matches::assert_matches; use std::assert_matches::assert_matches;
use std::borrow::{Borrow, Cow}; use std::borrow::{Borrow, Cow};
use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::{fmt, mem, ptr}; use std::{fmt, ptr};
use rustc_abi::{Align, HasDataLayout, Size}; use rustc_abi::{Align, HasDataLayout, Size};
use rustc_ast::Mutability; use rustc_ast::Mutability;
@ -131,7 +132,7 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
/// This stores whether we are currently doing reads purely for the purpose of validation. /// This stores whether we are currently doing reads purely for the purpose of validation.
/// Those reads do not trigger the machine's hooks for memory reads. /// Those reads do not trigger the machine's hooks for memory reads.
/// Needless to say, this must only be set with great care! /// Needless to say, this must only be set with great care!
validation_in_progress: bool, validation_in_progress: Cell<bool>,
} }
/// A reference to some allocation that was already bounds-checked for the given region /// A reference to some allocation that was already bounds-checked for the given region
@ -158,7 +159,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
alloc_map: M::MemoryMap::default(), alloc_map: M::MemoryMap::default(),
extra_fn_ptr_map: FxIndexMap::default(), extra_fn_ptr_map: FxIndexMap::default(),
dead_alloc_map: FxIndexMap::default(), dead_alloc_map: FxIndexMap::default(),
validation_in_progress: false, validation_in_progress: Cell::new(false),
} }
} }
@ -715,7 +716,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// We want to call the hook on *all* accesses that involve an AllocId, including zero-sized // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
// accesses. That means we cannot rely on the closure above or the `Some` branch below. We // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
// do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
if !self.memory.validation_in_progress { if !self.memory.validation_in_progress.get() {
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) { if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
M::before_alloc_read(self, alloc_id)?; M::before_alloc_read(self, alloc_id)?;
} }
@ -723,7 +724,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
let range = alloc_range(offset, size); let range = alloc_range(offset, size);
if !self.memory.validation_in_progress { if !self.memory.validation_in_progress.get() {
M::before_memory_read( M::before_memory_read(
self.tcx, self.tcx,
&self.machine, &self.machine,
@ -801,7 +802,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
{ {
let tcx = self.tcx; let tcx = self.tcx;
let validation_in_progress = self.memory.validation_in_progress; let validation_in_progress = self.memory.validation_in_progress.get();
let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
let ptr_and_alloc = Self::check_and_deref_ptr( let ptr_and_alloc = Self::check_and_deref_ptr(
@ -1087,23 +1088,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// ///
/// We do this so Miri's allocation access tracking does not show the validation /// We do this so Miri's allocation access tracking does not show the validation
/// reads as spurious accesses. /// reads as spurious accesses.
pub fn run_for_validation<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { pub fn run_for_validation_mut<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
// This deliberately uses `==` on `bool` to follow the pattern // This deliberately uses `==` on `bool` to follow the pattern
// `assert!(val.replace(new) == old)`. // `assert!(val.replace(new) == old)`.
assert!( assert!(
mem::replace(&mut self.memory.validation_in_progress, true) == false, self.memory.validation_in_progress.replace(true) == false,
"`validation_in_progress` was already set" "`validation_in_progress` was already set"
); );
let res = f(self); let res = f(self);
assert!( assert!(
mem::replace(&mut self.memory.validation_in_progress, false) == true, self.memory.validation_in_progress.replace(false) == true,
"`validation_in_progress` was unset by someone else"
);
res
}
/// Runs the closure in "validation" mode, which means the machine's memory read hooks will be
/// suppressed. Needless to say, this must only be set with great care! Cannot be nested.
///
/// We do this so Miri's allocation access tracking does not show the validation
/// reads as spurious accesses.
pub fn run_for_validation_ref<R>(&self, f: impl FnOnce(&Self) -> R) -> R {
// This deliberately uses `==` on `bool` to follow the pattern
// `assert!(val.replace(new) == old)`.
assert!(
self.memory.validation_in_progress.replace(true) == false,
"`validation_in_progress` was already set"
);
let res = f(self);
assert!(
self.memory.validation_in_progress.replace(false) == true,
"`validation_in_progress` was unset by someone else" "`validation_in_progress` was unset by someone else"
); );
res res
} }
pub(super) fn validation_in_progress(&self) -> bool { pub(super) fn validation_in_progress(&self) -> bool {
self.memory.validation_in_progress self.memory.validation_in_progress.get()
} }
} }
@ -1375,7 +1396,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}; };
let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_alloc = self.get_alloc_raw(src_alloc_id)?;
let src_range = alloc_range(src_offset, size); let src_range = alloc_range(src_offset, size);
assert!(!self.memory.validation_in_progress, "we can't be copying during validation"); assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
// For the overlapping case, it is crucial that we trigger the read hook // For the overlapping case, it is crucial that we trigger the read hook
// before the write hook -- the aliasing model cares about the order. // before the write hook -- the aliasing model cares about the order.
M::before_memory_read( M::before_memory_read(

View file

@ -1322,7 +1322,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty);
// Run the visitor. // Run the visitor.
self.run_for_validation(|ecx| { self.run_for_validation_mut(|ecx| {
let reset_padding = reset_provenance_and_padding && { let reset_padding = reset_provenance_and_padding && {
// Check if `val` is actually stored in memory. If not, padding is not even // Check if `val` is actually stored in memory. If not, padding is not even
// represented and we need not reset it. // represented and we need not reset it.

View file

@ -632,10 +632,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{ {
let tcx = self.tcx; let tcx = self.tcx;
trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
self.typeck_results
.borrow_mut()
.treat_byte_string_as_slice
.insert(lt.hir_id.local_id);
pat_ty = pat_ty =
Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8)); Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8));
} }

View file

@ -81,9 +81,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
wbcx.typeck_results.used_trait_imports = used_trait_imports; wbcx.typeck_results.used_trait_imports = used_trait_imports;
wbcx.typeck_results.treat_byte_string_as_slice =
mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice);
debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results);
self.tcx.arena.alloc(wbcx.typeck_results) self.tcx.arena.alloc(wbcx.typeck_results)

View file

@ -89,6 +89,7 @@ rustc_query_append!(define_dep_nodes![
/// We use this to create a forever-red node. /// We use this to create a forever-red node.
[] fn Red() -> (), [] fn Red() -> (),
[] fn SideEffect() -> (), [] fn SideEffect() -> (),
[] fn AnonZeroDeps() -> (),
[] fn TraitSelect() -> (), [] fn TraitSelect() -> (),
[] fn CompileCodegenUnit() -> (), [] fn CompileCodegenUnit() -> (),
[] fn CompileMonoItem() -> (), [] fn CompileMonoItem() -> (),

View file

@ -53,6 +53,7 @@ impl Deps for DepsType {
const DEP_KIND_NULL: DepKind = dep_kinds::Null; const DEP_KIND_NULL: DepKind = dep_kinds::Null;
const DEP_KIND_RED: DepKind = dep_kinds::Red; const DEP_KIND_RED: DepKind = dep_kinds::Red;
const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect; const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect;
const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps;
const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1;
} }

View file

@ -197,12 +197,6 @@ pub struct TypeckResults<'tcx> {
/// formatting modified file tests/ui/coroutine/retain-resume-ref.rs /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs
pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>,
/// We sometimes treat byte string literals (which are of type `&[u8; N]`)
/// as `&[u8]`, depending on the pattern in which they are used.
/// This hashset records all instances where we behave
/// like this to allow `const_to_pat` to reliably handle this situation.
pub treat_byte_string_as_slice: ItemLocalSet,
/// Contains the data for evaluating the effect of feature `capture_disjoint_fields` /// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
/// on closure size. /// on closure size.
pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>, pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
@ -237,7 +231,6 @@ impl<'tcx> TypeckResults<'tcx> {
closure_fake_reads: Default::default(), closure_fake_reads: Default::default(),
rvalue_scopes: Default::default(), rvalue_scopes: Default::default(),
coroutine_stalled_predicates: Default::default(), coroutine_stalled_predicates: Default::default(),
treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(), closure_size_eval: Default::default(),
offset_of_data: Default::default(), offset_of_data: Default::default(),
} }

View file

@ -58,25 +58,13 @@ struct ConstToPat<'tcx> {
span: Span, span: Span,
id: hir::HirId, id: hir::HirId,
treat_byte_string_as_slice: bool,
c: ty::Const<'tcx>, c: ty::Const<'tcx>,
} }
impl<'tcx> ConstToPat<'tcx> { impl<'tcx> ConstToPat<'tcx> {
fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
trace!(?pat_ctxt.typeck_results.hir_owner); trace!(?pat_ctxt.typeck_results.hir_owner);
ConstToPat { ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
tcx: pat_ctxt.tcx,
typing_env: pat_ctxt.typing_env,
span,
id,
treat_byte_string_as_slice: pat_ctxt
.typeck_results
.treat_byte_string_as_slice
.contains(&id.local_id),
c,
}
} }
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
@ -108,8 +96,6 @@ impl<'tcx> ConstToPat<'tcx> {
uv: ty::UnevaluatedConst<'tcx>, uv: ty::UnevaluatedConst<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> Box<Pat<'tcx>> { ) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice);
// It's not *technically* correct to be revealing opaque types here as borrowcheck has // It's not *technically* correct to be revealing opaque types here as borrowcheck has
// not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
// during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
@ -307,21 +293,8 @@ impl<'tcx> ConstToPat<'tcx> {
ty, ty,
); );
} else { } else {
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
// matching against references, you can only use byte string literals.
// The typechecker has a special case for byte string literals, by treating them
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
// has no negative effects on pattern matching, even if we're actually matching on
// arrays.
let pointee_ty = match *pointee_ty.kind() {
ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
Ty::new_slice(tcx, elem_ty)
}
_ => *pointee_ty,
};
// References have the same valtree representation as their pointee. // References have the same valtree representation as their pointee.
let subpattern = self.valtree_to_pat(cv, pointee_ty); PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
PatKind::Deref { subpattern }
} }
} }
}, },

View file

@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{self as hir, RangeEnd}; use rustc_hir::{self as hir, LangItem, RangeEnd};
use rustc_index::Idx; use rustc_index::Idx;
use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::thir::{ use rustc_middle::thir::{
@ -130,7 +130,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// Lower the endpoint into a temporary `PatKind` that will then be // Lower the endpoint into a temporary `PatKind` that will then be
// deconstructed to obtain the constant value and other data. // deconstructed to obtain the constant value and other data.
let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr); let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr, None);
// Unpeel any ascription or inline-const wrapper nodes. // Unpeel any ascription or inline-const wrapper nodes.
loop { loop {
@ -294,7 +294,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
hir::PatKind::Never => PatKind::Never, hir::PatKind::Never => PatKind::Never,
hir::PatKind::Expr(value) => self.lower_pat_expr(value), hir::PatKind::Expr(value) => self.lower_pat_expr(value, Some(ty)),
hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
@ -630,7 +630,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`) /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`)
/// - Inline const blocks (e.g. `const { 1 + 1 }`) /// - Inline const blocks (e.g. `const { 1 + 1 }`)
/// - Literals, possibly negated (e.g. `-128u8`, `"hello"`) /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> { fn lower_pat_expr(
&mut self,
expr: &'tcx hir::PatExpr<'tcx>,
pat_ty: Option<Ty<'tcx>>,
) -> PatKind<'tcx> {
let (lit, neg) = match &expr.kind { let (lit, neg) = match &expr.kind {
hir::PatExprKind::Path(qpath) => { hir::PatExprKind::Path(qpath) => {
return self.lower_path(qpath, expr.hir_id, expr.span).kind; return self.lower_path(qpath, expr.hir_id, expr.span).kind;
@ -641,7 +645,31 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
hir::PatExprKind::Lit { lit, negated } => (lit, *negated), hir::PatExprKind::Lit { lit, negated } => (lit, *negated),
}; };
let ct_ty = self.typeck_results.node_type(expr.hir_id); // We handle byte string literal patterns by using the pattern's type instead of the
// literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,
// the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
// pattern's type means we'll properly translate it to a slice reference pattern. This works
// because slices and arrays have the same valtree representation.
// HACK: As an exception, use the literal's type if `pat_ty` is `String`; this can happen if
// `string_deref_patterns` is enabled. There's a special case for that when lowering to MIR.
// FIXME(deref_patterns): This hack won't be necessary once `string_deref_patterns` is
// superseded by a more general implementation of deref patterns.
let ct_ty = match pat_ty {
Some(pat_ty)
if let ty::Adt(def, _) = *pat_ty.kind()
&& self.tcx.is_lang_item(def.did(), LangItem::String) =>
{
if !self.tcx.features().string_deref_patterns() {
span_bug!(
expr.span,
"matching on `String` went through without enabling string_deref_patterns"
);
}
self.typeck_results.node_type(expr.hir_id)
}
Some(pat_ty) => pat_ty,
None => self.typeck_results.node_type(expr.hir_id),
};
let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg };
let constant = self.tcx.at(expr.span).lit_to_const(lit_input); let constant = self.tcx.at(expr.span).lit_to_const(lit_input);
self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind

View file

@ -2960,13 +2960,30 @@ impl<'a> Parser<'a> {
let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic(); let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
this.eat_incorrect_doc_comment_for_param_type(); this.eat_incorrect_doc_comment_for_param_type();
let mut ty = this.parse_ty_for_param(); let mut ty = this.parse_ty_for_param();
if ty.is_ok()
&& this.token != token::Comma if let Ok(t) = &ty {
&& this.token != token::CloseDelim(Delimiter::Parenthesis) // Check for trailing angle brackets
{ if let TyKind::Path(_, Path { segments, .. }) = &t.kind {
// This wasn't actually a type, but a pattern looking like a type, if let Some(segment) = segments.last() {
// so we are going to rollback and re-parse for recovery. if let Some(guar) =
ty = this.unexpected_any(); this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)])
{
return Ok((
dummy_arg(segment.ident, guar),
Trailing::No,
UsePreAttrPos::No,
));
}
}
}
if this.token != token::Comma
&& this.token != token::CloseDelim(Delimiter::Parenthesis)
{
// This wasn't actually a type, but a pattern looking like a type,
// so we are going to rollback and re-parse for recovery.
ty = this.unexpected_any();
}
} }
match ty { match ty {
Ok(ty) => { Ok(ty) => {
@ -2977,6 +2994,7 @@ impl<'a> Parser<'a> {
} }
// If this is a C-variadic argument and we hit an error, return the error. // If this is a C-variadic argument and we hit an error, return the error.
Err(err) if this.token == token::DotDotDot => return Err(err), Err(err) if this.token == token::DotDotDot => return Err(err),
Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err),
// Recover from attempting to parse the argument as a type without pattern. // Recover from attempting to parse the argument as a type without pattern.
Err(err) => { Err(err) => {
err.cancel(); err.cancel();

View file

@ -870,6 +870,17 @@ macro_rules! define_queries {
} }
} }
pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindStruct<'tcx> {
DepKindStruct {
is_anon: true,
is_eval_always: false,
fingerprint_style: FingerprintStyle::Opaque,
force_from_dep_node: Some(|_, _, _| bug!("cannot force an anon node")),
try_load_from_on_disk_cache: None,
name: &"AnonZeroDeps",
}
}
pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> { pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> {
DepKindStruct { DepKindStruct {
is_anon: true, is_anon: true,

View file

@ -50,7 +50,7 @@ rustc_index::newtype_index! {
rustc_data_structures::static_assert_size!(Option<DepNodeIndex>, 4); rustc_data_structures::static_assert_size!(Option<DepNodeIndex>, 4);
impl DepNodeIndex { impl DepNodeIndex {
const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO; const SINGLETON_ZERO_DEPS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1); pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
} }
@ -140,13 +140,13 @@ impl<D: Deps> DepGraph<D> {
let colors = DepNodeColorMap::new(prev_graph_node_count); let colors = DepNodeColorMap::new(prev_graph_node_count);
// Instantiate a dependy-less node only once for anonymous queries. // Instantiate a node with zero dependencies only once for anonymous queries.
let _green_node_index = current.alloc_node( let _green_node_index = current.alloc_node(
DepNode { kind: D::DEP_KIND_NULL, hash: current.anon_id_seed.into() }, DepNode { kind: D::DEP_KIND_ANON_ZERO_DEPS, hash: current.anon_id_seed.into() },
EdgesVec::new(), EdgesVec::new(),
Fingerprint::ZERO, Fingerprint::ZERO,
); );
assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE); assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE);
// Instantiate a dependy-less red node only once for anonymous queries. // Instantiate a dependy-less red node only once for anonymous queries.
let red_node_index = current.alloc_node( let red_node_index = current.alloc_node(
@ -407,7 +407,7 @@ impl<D: Deps> DepGraphData<D> {
// going to be (i.e. equal to the precomputed // going to be (i.e. equal to the precomputed
// `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
// a `StableHasher` and sending the node through interning. // a `StableHasher` and sending the node through interning.
DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE
} }
1 => { 1 => {
// When there is only one dependency, don't bother creating a node. // When there is only one dependency, don't bother creating a node.

View file

@ -111,6 +111,9 @@ pub trait Deps {
/// We use this to create a side effect node. /// We use this to create a side effect node.
const DEP_KIND_SIDE_EFFECT: DepKind; const DEP_KIND_SIDE_EFFECT: DepKind;
/// We use this to create the anon node with zero dependencies.
const DEP_KIND_ANON_ZERO_DEPS: DepKind;
/// This is the highest value a `DepKind` can have. It's used during encoding to /// This is the highest value a `DepKind` can have. It's used during encoding to
/// pack information into the unused bits. /// pack information into the unused bits.
const DEP_KIND_MAX: u16; const DEP_KIND_MAX: u16;

View file

@ -717,7 +717,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
// The program didn't actually do a read, so suppress the memory access hooks. // The program didn't actually do a read, so suppress the memory access hooks.
// This is also a very special exception where we just ignore an error -- if this read // This is also a very special exception where we just ignore an error -- if this read
// was UB e.g. because the memory is uninitialized, we don't want to know! // was UB e.g. because the memory is uninitialized, we don't want to know!
let old_val = this.run_for_validation(|this| this.read_scalar(dest)).discard_err(); let old_val = this.run_for_validation_mut(|this| this.read_scalar(dest)).discard_err();
this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
this.validate_atomic_store(dest, atomic)?; this.validate_atomic_store(dest, atomic)?;
this.buffered_atomic_write(val, dest, atomic, old_val) this.buffered_atomic_write(val, dest, atomic, old_val)

View file

@ -0,0 +1,53 @@
//! Tests that arrays and slices in constants aren't interchangeable when used as patterns.
#[derive(PartialEq, Eq)]
struct SomeStruct<T: ?Sized>(T);
const BSTR_SIZED: &'static [u8; 3] = b"012";
const BSTR_UNSIZED: &'static [u8] = BSTR_SIZED;
const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED);
const STRUCT_UNSIZED: &'static SomeStruct<[u8]> = STRUCT_SIZED;
fn type_mismatches() {
// Test that array consts can't be used where a slice pattern is expected. This helps ensure
// that `const_to_pat` won't produce irrefutable `thir::PatKind::Array` patterns when matching
// on slices, which would result in missing length checks.
// See also `tests/ui/match/pattern-deref-miscompile.rs`, which tests that byte string literal
// patterns check slices' length appropriately when matching on slices.
match BSTR_UNSIZED {
BSTR_SIZED => {}
//~^ ERROR: mismatched types
_ => {}
}
match STRUCT_UNSIZED {
STRUCT_SIZED => {}
//~^ ERROR: mismatched types
_ => {}
}
// Test that slice consts can't be used where an array pattern is expected.
match BSTR_UNSIZED {
BSTR_SIZED => {}
//~^ ERROR: mismatched types
_ => {}
}
// If the types matched here, this would still error, since unsized structs aren't permitted in
// constant patterns. See the `invalid_patterns` test below.
match STRUCT_UNSIZED {
STRUCT_SIZED => {}
//~^ ERROR: mismatched types
_ => {}
}
}
fn invalid_patterns() {
// Test that unsized structs containing slices can't be used as patterns.
// See `tests/ui/consts/issue-87046.rs` for an example with `str`.
match STRUCT_UNSIZED {
STRUCT_UNSIZED => {}
//~^ ERROR: cannot use unsized non-slice type `SomeStruct<[u8]>` in constant patterns
_ => {}
}
}
fn main() {}

View file

@ -0,0 +1,84 @@
error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:18:9
|
LL | const BSTR_SIZED: &'static [u8; 3] = b"012";
| ---------------------------------- constant defined here
...
LL | match BSTR_UNSIZED {
| ------------ this expression has type `&[u8]`
LL | BSTR_SIZED => {}
| ^^^^^^^^^^
| |
| expected `&[u8]`, found `&[u8; 3]`
| `BSTR_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_bstr_sized`
|
= note: expected reference `&[u8]`
found reference `&'static [u8; 3]`
error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:23:9
|
LL | const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED);
| ------------------------------------------------ constant defined here
...
LL | match STRUCT_UNSIZED {
| -------------- this expression has type `&SomeStruct<[u8]>`
LL | STRUCT_SIZED => {}
| ^^^^^^^^^^^^
| |
| expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>`
| `STRUCT_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_struct_sized`
|
= note: expected reference `&SomeStruct<[u8]>`
found reference `&'static SomeStruct<[u8; 3]>`
error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:30:9
|
LL | const BSTR_SIZED: &'static [u8; 3] = b"012";
| ---------------------------------- constant defined here
...
LL | match BSTR_UNSIZED {
| ------------ this expression has type `&[u8]`
LL | BSTR_SIZED => {}
| ^^^^^^^^^^
| |
| expected `&[u8]`, found `&[u8; 3]`
| `BSTR_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_bstr_sized`
|
= note: expected reference `&[u8]`
found reference `&'static [u8; 3]`
error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:37:9
|
LL | const STRUCT_SIZED: &'static SomeStruct<[u8; 3]> = &SomeStruct(*BSTR_SIZED);
| ------------------------------------------------ constant defined here
...
LL | match STRUCT_UNSIZED {
| -------------- this expression has type `&SomeStruct<[u8]>`
LL | STRUCT_SIZED => {}
| ^^^^^^^^^^^^
| |
| expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>`
| `STRUCT_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_struct_sized`
|
= note: expected reference `&SomeStruct<[u8]>`
found reference `&'static SomeStruct<[u8; 3]>`
error: cannot use unsized non-slice type `SomeStruct<[u8]>` in constant patterns
--> $DIR/arrays-and-slices.rs:47:9
|
LL | const STRUCT_UNSIZED: &'static SomeStruct<[u8]> = STRUCT_SIZED;
| ----------------------------------------------- constant defined here
...
LL | STRUCT_UNSIZED => {}
| ^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -43,6 +43,21 @@ LL - #[coverage = "off"]
LL + #[coverage(on)] LL + #[coverage(on)]
| |
error: malformed `coverage` attribute input
--> $DIR/name-value.rs:26:1
|
LL | #[coverage = "off"]
| ^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
|
LL - #[coverage = "off"]
LL + #[coverage(on)]
|
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/name-value.rs:29:5 --> $DIR/name-value.rs:29:5
| |
@ -59,7 +74,7 @@ LL + #[coverage(on)]
| |
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/name-value.rs:26:1 --> $DIR/name-value.rs:35:1
| |
LL | #[coverage = "off"] LL | #[coverage = "off"]
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
@ -104,7 +119,7 @@ LL + #[coverage(on)]
| |
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/name-value.rs:35:1 --> $DIR/name-value.rs:50:1
| |
LL | #[coverage = "off"] LL | #[coverage = "off"]
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
@ -148,21 +163,6 @@ LL - #[coverage = "off"]
LL + #[coverage(on)] LL + #[coverage(on)]
| |
error: malformed `coverage` attribute input
--> $DIR/name-value.rs:50:1
|
LL | #[coverage = "off"]
| ^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[coverage = "off"]
LL + #[coverage(off)]
|
LL - #[coverage = "off"]
LL + #[coverage(on)]
|
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/name-value.rs:64:1 --> $DIR/name-value.rs:64:1
| |

View file

@ -37,6 +37,19 @@ LL | #[coverage(off)]
LL | #[coverage(on)] LL | #[coverage(on)]
| ++++ | ++++
error: malformed `coverage` attribute input
--> $DIR/word-only.rs:26:1
|
LL | #[coverage]
| ^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/word-only.rs:29:5 --> $DIR/word-only.rs:29:5
| |
@ -51,7 +64,7 @@ LL | #[coverage(on)]
| ++++ | ++++
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/word-only.rs:26:1 --> $DIR/word-only.rs:35:1
| |
LL | #[coverage] LL | #[coverage]
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -90,7 +103,7 @@ LL | #[coverage(on)]
| ++++ | ++++
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/word-only.rs:35:1 --> $DIR/word-only.rs:50:1
| |
LL | #[coverage] LL | #[coverage]
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -128,19 +141,6 @@ LL | #[coverage(off)]
LL | #[coverage(on)] LL | #[coverage(on)]
| ++++ | ++++
error: malformed `coverage` attribute input
--> $DIR/word-only.rs:50:1
|
LL | #[coverage]
| ^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++
error: malformed `coverage` attribute input error: malformed `coverage` attribute input
--> $DIR/word-only.rs:64:1 --> $DIR/word-only.rs:64:1
| |

View file

@ -0,0 +1,8 @@
// Regression test for the parser wrongfully suggesting turbofish syntax in below syntax errors
type One = for<'a> fn(Box<dyn Send + 'a);
//~^ ERROR: expected one of `+`, `,`, or `>`, found `)`
type Two = for<'a> fn(Box<dyn Send + 'a>>);
//~^ ERROR: unmatched angle bracket
fn main() {}

View file

@ -0,0 +1,25 @@
error: expected one of `+`, `,`, or `>`, found `)`
--> $DIR/bad-turbofish-hints-issue-121901.rs:3:40
|
LL | type One = for<'a> fn(Box<dyn Send + 'a);
| ^ expected one of `+`, `,`, or `>`
|
help: you might have meant to end the type parameters here
|
LL | type One = for<'a> fn(Box<dyn Send + 'a>);
| +
error: unmatched angle bracket
--> $DIR/bad-turbofish-hints-issue-121901.rs:5:41
|
LL | type Two = for<'a> fn(Box<dyn Send + 'a>>);
| ^
|
help: remove extra angle bracket
|
LL - type Two = for<'a> fn(Box<dyn Send + 'a>>);
LL + type Two = for<'a> fn(Box<dyn Send + 'a>);
|
error: aborting due to 2 previous errors

View file

@ -1190,8 +1190,8 @@ docs = [
"@ehuss", "@ehuss",
"@GuillaumeGomez", "@GuillaumeGomez",
] ]
codegen = [ codegen = [
"@dianqk",
"@saethlin", "@saethlin",
"@workingjubilee", "@workingjubilee",
] ]