diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
commit | 4547b622d8d29df964fa2914213088b148c498fc (patch) | |
tree | 9fc6b25f3c3add6b745be9a2400a6e96140046e9 /compiler/rustc_hir | |
parent | Releasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz rustc-4547b622d8d29df964fa2914213088b148c498fc.zip |
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
78 files changed, 4229 insertions, 3768 deletions
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 44335b7f4..c89e7eb75 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -39,6 +39,7 @@ macro_rules! arena_types { [] param: rustc_hir::Param<'tcx>, [] pat: rustc_hir::Pat<'tcx>, [] path: rustc_hir::Path<'tcx>, + [] use_path: rustc_hir::UsePath<'tcx>, [] path_segment: rustc_hir::PathSegment<'tcx>, [] poly_trait_ref: rustc_hir::PolyTraitRef<'tcx>, [] qpath: rustc_hir::QPath<'tcx>, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 4ef4aad90..149cf4ece 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -28,8 +28,6 @@ pub enum CtorKind { Fn, /// Constructor constant automatically created by a unit struct/variant. Const, - /// Unusable name in value namespace created by a struct variant. - Fictive, } /// An attribute that is not a macro; e.g., `#[inline]` or `#[rustfmt::skip]`. @@ -132,13 +130,9 @@ impl DefKind { DefKind::Variant => "variant", DefKind::Ctor(CtorOf::Variant, CtorKind::Fn) => "tuple variant", DefKind::Ctor(CtorOf::Variant, CtorKind::Const) => "unit variant", - DefKind::Ctor(CtorOf::Variant, CtorKind::Fictive) => "struct variant", DefKind::Struct => "struct", DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) => "tuple struct", DefKind::Ctor(CtorOf::Struct, CtorKind::Const) => "unit struct", - DefKind::Ctor(CtorOf::Struct, CtorKind::Fictive) => { - panic!("impossible struct constructor") - } DefKind::OpaqueTy => "opaque type", DefKind::ImplTraitPlaceholder => "opaque type in trait", DefKind::TyAlias => "type alias", @@ -562,19 +556,11 @@ impl<T> PerNS<Option<T>> { } impl CtorKind { - pub fn from_ast(vdata: &ast::VariantData) -> CtorKind { - match *vdata { - ast::VariantData::Tuple(..) => CtorKind::Fn, - ast::VariantData::Unit(..) => CtorKind::Const, - ast::VariantData::Struct(..) => CtorKind::Fictive, - } - } - - pub fn from_hir(vdata: &hir::VariantData<'_>) -> CtorKind { + pub fn from_ast(vdata: &ast::VariantData) -> Option<(CtorKind, NodeId)> { match *vdata { - hir::VariantData::Tuple(..) => CtorKind::Fn, - hir::VariantData::Unit(..) => CtorKind::Const, - hir::VariantData::Struct(..) => CtorKind::Fictive, + ast::VariantData::Tuple(_, node_id) => Some((CtorKind::Fn, node_id)), + ast::VariantData::Unit(node_id) => Some((CtorKind::Const, node_id)), + ast::VariantData::Struct(..) => None, } } } diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index d85ac960f..dd37efb69 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -368,10 +368,6 @@ impl Definitions { LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) } } - pub fn iter_local_def_id(&self) -> impl Iterator<Item = LocalDefId> + '_ { - self.table.def_path_hashes.indices().map(|local_def_index| LocalDefId { local_def_index }) - } - #[inline(always)] pub fn local_def_path_hash_to_def_id( &self, @@ -389,6 +385,10 @@ impl Definitions { pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { &self.table.def_path_hash_to_index } + + pub fn num_definitions(&self) -> usize { + self.table.def_path_hashes.len() + } } #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ef00c1ffc..8bc022e1e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -29,15 +29,16 @@ use std::fmt; #[derive(Debug, Copy, Clone, Encodable, HashStable_Generic)] pub struct Lifetime { pub hir_id: HirId, - pub span: Span, /// Either "`'a`", referring to a named lifetime definition, - /// or "``" (i.e., `kw::Empty`), for elision placeholders. + /// `'_` referring to an anonymous lifetime (either explicitly `'_` or `&type`), + /// or "``" (i.e., `kw::Empty`) when appearing in path. /// - /// HIR lowering inserts these placeholders in type paths that - /// refer to type definitions needing lifetime parameters, - /// `&T` and `&mut T`, and trait objects without `... + 'a`. - pub name: LifetimeName, + /// See `Lifetime::suggestion_position` for practical use. + pub ident: Ident, + + /// Semantics of this lifetime. + pub res: LifetimeName, } #[derive(Debug, Clone, PartialEq, Eq, Encodable, Hash, Copy)] @@ -88,7 +89,7 @@ impl ParamName { #[derive(HashStable_Generic)] pub enum LifetimeName { /// User-given names or fresh (synthetic) names. - Param(LocalDefId, ParamName), + Param(LocalDefId), /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the @@ -116,25 +117,6 @@ pub enum LifetimeName { } impl LifetimeName { - pub fn ident(&self) -> Ident { - match *self { - LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), - LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime), - LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), - LifetimeName::Param(_, param_name) => param_name.ident(), - } - } - - pub fn is_anonymous(&self) -> bool { - match *self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Infer - | LifetimeName::Param(_, ParamName::Fresh) - | LifetimeName::Error => true, - LifetimeName::Static | LifetimeName::Param(..) => false, - } - } - pub fn is_elided(&self) -> bool { match self { LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, @@ -146,34 +128,54 @@ impl LifetimeName { LifetimeName::Error | LifetimeName::Param(..) | LifetimeName::Static => false, } } - - fn is_static(&self) -> bool { - self == &LifetimeName::Static - } - - pub fn normalize_to_macros_2_0(&self) -> LifetimeName { - match *self { - LifetimeName::Param(def_id, param_name) => { - LifetimeName::Param(def_id, param_name.normalize_to_macros_2_0()) - } - lifetime_name => lifetime_name, - } - } } impl fmt::Display for Lifetime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.name.ident().fmt(f) + if self.ident.name != kw::Empty { self.ident.name.fmt(f) } else { "'_".fmt(f) } } } +pub enum LifetimeSuggestionPosition { + /// The user wrote `'a` or `'_`. + Normal, + /// The user wrote `&type` or `&mut type`. + Ampersand, + /// The user wrote `Path` and omitted the `<'_>`. + ElidedPath, + /// The user wrote `Path<T>`, and omitted the `'_,`. + ElidedPathArgument, + /// The user wrote `dyn Trait` and omitted the `+ '_`. + ObjectDefault, +} + impl Lifetime { pub fn is_elided(&self) -> bool { - self.name.is_elided() + self.res.is_elided() + } + + pub fn is_anonymous(&self) -> bool { + self.ident.name == kw::Empty || self.ident.name == kw::UnderscoreLifetime + } + + pub fn suggestion_position(&self) -> (LifetimeSuggestionPosition, Span) { + if self.ident.name == kw::Empty { + if self.ident.span.is_empty() { + (LifetimeSuggestionPosition::ElidedPathArgument, self.ident.span) + } else { + (LifetimeSuggestionPosition::ElidedPath, self.ident.span.shrink_to_hi()) + } + } else if self.res == LifetimeName::ImplicitObjectLifetimeDefault { + (LifetimeSuggestionPosition::ObjectDefault, self.ident.span) + } else if self.ident.span.is_empty() { + (LifetimeSuggestionPosition::Ampersand, self.ident.span) + } else { + (LifetimeSuggestionPosition::Normal, self.ident.span) + } } pub fn is_static(&self) -> bool { - self.name.is_static() + self.res == LifetimeName::Static } } @@ -181,14 +183,17 @@ impl Lifetime { /// `std::cmp::PartialEq`. It's represented as a sequence of identifiers, /// along with a bunch of supporting information. #[derive(Debug, HashStable_Generic)] -pub struct Path<'hir> { +pub struct Path<'hir, R = Res> { pub span: Span, /// The resolution for the path. - pub res: Res, + pub res: R, /// The segments in the path: the things separated by `::`. pub segments: &'hir [PathSegment<'hir>], } +/// Up to three resolutions for type, value and macro namespaces. +pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>; + impl Path<'_> { pub fn is_global(&self) -> bool { !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot @@ -267,7 +272,7 @@ pub enum GenericArg<'hir> { impl GenericArg<'_> { pub fn span(&self) -> Span { match self { - GenericArg::Lifetime(l) => l.span, + GenericArg::Lifetime(l) => l.ident.span, GenericArg::Type(t) => t.span, GenericArg::Const(c) => c.span, GenericArg::Infer(i) => i.span, @@ -284,7 +289,7 @@ impl GenericArg<'_> { } pub fn is_synthetic(&self) -> bool { - matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty()) + matches!(self, GenericArg::Lifetime(lifetime) if lifetime.ident == Ident::empty()) } pub fn descr(&self) -> &'static str { @@ -388,6 +393,8 @@ impl<'hir> GenericArgs<'hir> { } #[inline] + /// This function returns the number of type and const generic params. + /// It should only be used for diagnostics. pub fn num_generic_params(&self) -> usize { self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count() } @@ -444,7 +451,7 @@ impl GenericBound<'_> { match self { GenericBound::Trait(t, ..) => t.span, GenericBound::LangItemTrait(_, span, ..) => *span, - GenericBound::Outlives(l) => l.span, + GenericBound::Outlives(l) => l.ident.span, } } } @@ -485,6 +492,7 @@ pub enum GenericParamKind<'hir> { #[derive(Debug, HashStable_Generic)] pub struct GenericParam<'hir> { pub hir_id: HirId, + pub def_id: LocalDefId, pub name: ParamName, pub span: Span, pub pure_wrt_drop: bool, @@ -557,6 +565,19 @@ impl<'hir> Generics<'hir> { } /// If there are generic parameters, return where to introduce a new one. + pub fn span_for_lifetime_suggestion(&self) -> Option<Span> { + if let Some(first) = self.params.first() + && self.span.contains(first.span) + { + // `fn foo<A>(t: impl Trait)` + // ^ suggest `'a, ` here + Some(first.span.shrink_to_lo()) + } else { + None + } + } + + /// If there are generic parameters, return where to introduce a new one. pub fn span_for_param_suggestion(&self) -> Option<Span> { if self.params.iter().any(|p| self.span.contains(p.span)) { // `fn foo<A>(t: impl Trait)` @@ -762,10 +783,7 @@ pub struct WhereRegionPredicate<'hir> { impl<'hir> WhereRegionPredicate<'hir> { /// Returns `true` if `param_def_id` matches the `lifetime` of this predicate. pub fn is_param_bound(&self, param_def_id: LocalDefId) -> bool { - match self.lifetime.name { - LifetimeName::Param(id, _) => id == param_def_id, - _ => false, - } + self.lifetime.res == LifetimeName::Param(param_def_id) } } @@ -809,7 +827,7 @@ impl<'tcx> AttributeMap<'tcx> { pub struct OwnerNodes<'tcx> { /// Pre-computed hash of the full HIR. pub hash_including_bodies: Fingerprint, - /// Pre-computed hash of the item signature, sithout recursing into the body. + /// Pre-computed hash of the item signature, without recursing into the body. pub hash_without_bodies: Fingerprint, /// Full HIR for the current owner. // The zeroth node's parent should never be accessed: the owner's parent is computed by the @@ -919,12 +937,16 @@ pub struct Crate<'hir> { #[derive(Debug, HashStable_Generic)] pub struct Closure<'hir> { + pub def_id: LocalDefId, pub binder: ClosureBinder, pub capture_clause: CaptureBy, pub bound_generic_params: &'hir [GenericParam<'hir>], pub fn_decl: &'hir FnDecl<'hir>, pub body: BodyId, + /// The span of the declaration block: 'move |...| -> ...' pub fn_decl_span: Span, + /// The span of the argument block `|...|` + pub fn_arg_span: Option<Span>, pub movability: Option<Movability>, } @@ -965,8 +987,8 @@ pub struct Pat<'hir> { pub hir_id: HirId, pub kind: PatKind<'hir>, pub span: Span, - // Whether to use default binding modes. - // At present, this is false only for destructuring assignment. + /// Whether to use default binding modes. + /// At present, this is false only for destructuring assignment. pub default_binding_modes: bool, } @@ -1074,7 +1096,7 @@ impl fmt::Display for RangeEnd { pub struct DotDotPos(u32); impl DotDotPos { - // Panics if n >= u32::MAX. + /// Panics if n >= u32::MAX. pub fn new(n: Option<usize>) -> Self { match n { Some(n) => { @@ -1510,9 +1532,9 @@ pub enum AsyncGeneratorKind { impl fmt::Display for AsyncGeneratorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { - AsyncGeneratorKind::Block => "`async` block", - AsyncGeneratorKind::Closure => "`async` closure body", - AsyncGeneratorKind::Fn => "`async fn` body", + AsyncGeneratorKind::Block => "async block", + AsyncGeneratorKind::Closure => "async closure body", + AsyncGeneratorKind::Fn => "async fn body", }) } } @@ -1613,7 +1635,7 @@ pub enum ArrayLen { impl ArrayLen { pub fn hir_id(&self) -> HirId { match self { - &ArrayLen::Infer(hir_id, _) | &ArrayLen::Body(AnonConst { hir_id, body: _ }) => hir_id, + &ArrayLen::Infer(hir_id, _) | &ArrayLen::Body(AnonConst { hir_id, .. }) => hir_id, } } } @@ -1625,10 +1647,11 @@ impl ArrayLen { /// explicit discriminant values for enum variants. /// /// You can check if this anon const is a default in a const param -/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_hir_id(..)` +/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)` #[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)] pub struct AnonConst { pub hir_id: HirId, + pub def_id: LocalDefId, pub body: BodyId, } @@ -1677,10 +1700,10 @@ impl Expr<'_> { } } - // Whether this looks like a place expr, without checking for deref - // adjustments. - // This will return `true` in some potentially surprising cases such as - // `CONSTANT.field`. + /// Whether this looks like a place expr, without checking for deref + /// adjustments. + /// This will return `true` in some potentially surprising cases such as + /// `CONSTANT.field`. pub fn is_syntactic_place_expr(&self) -> bool { self.is_place_expr(|_| true) } @@ -1821,7 +1844,7 @@ impl Expr<'_> { } } - // To a first-order approximation, is this a pattern + /// To a first-order approximation, is this a pattern? pub fn is_approximately_pattern(&self) -> bool { match &self.kind { ExprKind::Box(_) @@ -2143,11 +2166,11 @@ impl fmt::Display for LoopIdError { #[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)] pub struct Destination { - // This is `Some(_)` iff there is an explicit user-specified `label + /// This is `Some(_)` iff there is an explicit user-specified 'label pub label: Option<Label>, - // These errors are caught and then reported during the diagnostics pass in - // librustc_passes/loops.rs + /// These errors are caught and then reported during the diagnostics pass in + /// `librustc_passes/loops.rs` pub target_id: Result<HirId, LoopIdError>, } @@ -2318,7 +2341,7 @@ pub enum ImplItemKind<'hir> { Type(&'hir Ty<'hir>), } -// The name of the associated type for `Fn` return types. +/// The name of the associated type for `Fn` return types. pub const FN_OUTPUT_NAME: Symbol = sym::Output; /// Bind a type to an associated type (i.e., `A = Foo`). @@ -2414,7 +2437,7 @@ impl<'hir> Ty<'hir> { pub fn peel_refs(&self) -> &Self { let mut final_ty = self; while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { - final_ty = &ty; + final_ty = ty; } final_ty } @@ -2683,6 +2706,8 @@ pub struct FnDecl<'hir> { pub c_variadic: bool, /// Does the function have an implicit self? pub implicit_self: ImplicitSelfKind, + /// Is lifetime elision allowed. + pub lifetime_elision_allowed: bool, } /// Represents what type of implicit self a function has, if any. @@ -2715,6 +2740,12 @@ pub enum IsAsync { NotAsync, } +impl IsAsync { + pub fn is_async(self) -> bool { + self == IsAsync::Async + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] pub enum Defaultness { Default { has_value: bool }, @@ -2796,7 +2827,8 @@ pub struct Variant<'hir> { /// Name of the variant. pub ident: Ident, /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`). - pub id: HirId, + pub hir_id: HirId, + pub def_id: LocalDefId, /// Fields and constructor id of the variant. pub data: VariantData<'hir>, /// Explicit discriminant (e.g., `Foo = 1`). @@ -2863,6 +2895,7 @@ pub struct FieldDef<'hir> { pub vis_span: Span, pub ident: Ident, pub hir_id: HirId, + pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, } @@ -2884,11 +2917,11 @@ pub enum VariantData<'hir> { /// A tuple variant. /// /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. - Tuple(&'hir [FieldDef<'hir>], HirId), + Tuple(&'hir [FieldDef<'hir>], HirId, LocalDefId), /// A unit variant. /// /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`. - Unit(HirId), + Unit(HirId, LocalDefId), } impl<'hir> VariantData<'hir> { @@ -2900,13 +2933,30 @@ impl<'hir> VariantData<'hir> { } } - /// Return the `HirId` of this variant's constructor, if it has one. - pub fn ctor_hir_id(&self) -> Option<HirId> { + pub fn ctor(&self) -> Option<(CtorKind, HirId, LocalDefId)> { match *self { - VariantData::Struct(_, _) => None, - VariantData::Tuple(_, hir_id) | VariantData::Unit(hir_id) => Some(hir_id), + VariantData::Tuple(_, hir_id, def_id) => Some((CtorKind::Fn, hir_id, def_id)), + VariantData::Unit(hir_id, def_id) => Some((CtorKind::Const, hir_id, def_id)), + VariantData::Struct(..) => None, } } + + #[inline] + pub fn ctor_kind(&self) -> Option<CtorKind> { + self.ctor().map(|(kind, ..)| kind) + } + + /// Return the `HirId` of this variant's constructor, if it has one. + #[inline] + pub fn ctor_hir_id(&self) -> Option<HirId> { + self.ctor().map(|(_, hir_id, _)| hir_id) + } + + /// Return the `LocalDefId` of this variant's constructor, if it has one. + #[inline] + pub fn ctor_def_id(&self) -> Option<LocalDefId> { + self.ctor().map(|(.., def_id)| def_id) + } } // The bodies for items are stored "out of line", in a separate @@ -3024,7 +3074,7 @@ pub enum ItemKind<'hir> { /// or just /// /// `use foo::bar::baz;` (with `as baz` implicitly on the right). - Use(&'hir Path<'hir>, UseKind), + Use(&'hir UsePath<'hir>, UseKind), /// A `static` item. Static(&'hir Ty<'hir>, Mutability, BodyId), @@ -3217,7 +3267,7 @@ pub enum ForeignItemKind<'hir> { /// A variable captured by a closure. #[derive(Debug, Copy, Clone, Encodable, HashStable_Generic)] pub struct Upvar { - // First span where it is accessed (there can be multiple). + /// First span where it is accessed (there can be multiple). pub span: Span, } @@ -3423,7 +3473,7 @@ impl<'hir> Node<'hir> { | Node::Variant(Variant { ident, .. }) | Node::Item(Item { ident, .. }) | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident), - Node::Lifetime(lt) => Some(lt.name.ident()), + Node::Lifetime(lt) => Some(lt.ident), Node::GenericParam(p) => Some(p.name.ident()), Node::TypeBinding(b) => Some(b.ident), Node::Param(..) @@ -3530,7 +3580,7 @@ impl<'hir> Node<'hir> { /// Get the fields for the tuple-constructor, /// if this node is a tuple constructor, otherwise None pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> { - if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None } + if let Node::Ctor(&VariantData::Tuple(fields, _, _)) = self { Some(fields) } else { None } } } @@ -3546,7 +3596,7 @@ mod size_asserts { static_assert_size!(FnDecl<'_>, 40); static_assert_size!(ForeignItem<'_>, 72); static_assert_size!(ForeignItemKind<'_>, 40); - static_assert_size!(GenericArg<'_>, 24); + static_assert_size!(GenericArg<'_>, 32); static_assert_size!(GenericBound<'_>, 48); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 80); @@ -3564,9 +3614,16 @@ mod size_asserts { static_assert_size!(Res, 12); static_assert_size!(Stmt<'_>, 32); static_assert_size!(StmtKind<'_>, 16); + // tidy-alphabetical-end + // FIXME: move the tidy directive to the end after the next bootstrap bump + #[cfg(bootstrap)] static_assert_size!(TraitItem<'_>, 88); + #[cfg(not(bootstrap))] + static_assert_size!(TraitItem<'_>, 80); + #[cfg(bootstrap)] static_assert_size!(TraitItemKind<'_>, 48); + #[cfg(not(bootstrap))] + static_assert_size!(TraitItemKind<'_>, 40); static_assert_size!(Ty<'_>, 48); static_assert_size!(TyKind<'_>, 32); - // tidy-alphabetical-end } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 752f760ea..060f40919 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -1,5 +1,5 @@ -use crate::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_ID}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_span::{def_id::DefPathHash, HashStableContext}; use std::fmt; @@ -22,6 +22,18 @@ impl OwnerId { } } +impl rustc_index::vec::Idx for OwnerId { + #[inline] + fn new(idx: usize) -> Self { + OwnerId { def_id: LocalDefId { local_def_index: DefIndex::from_usize(idx) } } + } + + #[inline] + fn index(self) -> usize { + self.def_id.local_def_index.as_usize() + } +} + impl<CTX: HashStableContext> HashStable<CTX> for OwnerId { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { @@ -104,7 +116,7 @@ impl Ord for HirId { impl PartialOrd for HirId { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - Some(self.cmp(&other)) + Some(self.cmp(other)) } } @@ -134,6 +146,10 @@ impl ItemLocalId { pub const INVALID: ItemLocalId = ItemLocalId::MAX; } +// Safety: Ord is implement as just comparing the LocalItemId's numerical +// values and these are not changed by (de-)serialization. +unsafe impl StableOrd for ItemLocalId {} + /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. pub const CRATE_HIR_ID: HirId = HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) }; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index be77e6fd3..938ace2c7 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -358,13 +358,16 @@ pub trait Visitor<'v>: Sized { fn visit_where_predicate(&mut self, predicate: &'v WherePredicate<'v>) { walk_where_predicate(self, predicate) } + fn visit_fn_ret_ty(&mut self, ret_ty: &'v FnRetTy<'v>) { + walk_fn_ret_ty(self, ret_ty) + } fn visit_fn_decl(&mut self, fd: &'v FnDecl<'v>) { walk_fn_decl(self, fd) } fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: HirId) { walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) { + fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) { walk_use(self, path, hir_id) } fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) { @@ -410,12 +413,7 @@ pub trait Visitor<'v>: Sized { walk_inf(self, inf); } fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) { - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(&ct.value), - GenericArg::Infer(inf) => self.visit_infer(inf), - } + walk_generic_arg(self, generic_arg); } fn visit_lifetime(&mut self, lifetime: &'v Lifetime) { walk_lifetime(self, lifetime) @@ -424,7 +422,7 @@ pub trait Visitor<'v>: Sized { fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) { walk_qpath(self, qpath, id) } - fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'v>, _id: HirId) { walk_path(self, path) } fn visit_path_segment(&mut self, path_segment: &'v PathSegment<'v>) { @@ -448,66 +446,9 @@ pub trait Visitor<'v>: Sized { } } -pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) { - visitor.visit_id(mod_hir_id); - for &item_id in module.item_ids { - visitor.visit_nested_item(item_id); - } -} - -pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) { - walk_list!(visitor, visit_param, body.params); - visitor.visit_expr(&body.value); -} - -pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { - // Intentionally visiting the expr first - the initialization expr - // dominates the local's definition. - walk_list!(visitor, visit_expr, &local.init); - visitor.visit_id(local.hir_id); - visitor.visit_pat(&local.pat); - if let Some(els) = local.els { - visitor.visit_block(els); - } - walk_list!(visitor, visit_ty, &local.ty); -} - -pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) { - visitor.visit_name(ident.name); -} - -pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) { - visitor.visit_ident(label.ident); -} - -pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) { - visitor.visit_id(lifetime.hir_id); - match lifetime.name { - LifetimeName::Param(_, ParamName::Plain(ident)) => { - visitor.visit_ident(ident); - } - LifetimeName::Param(_, ParamName::Fresh) - | LifetimeName::Param(_, ParamName::Error) - | LifetimeName::Static - | LifetimeName::Error - | LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Infer => {} - } -} - -pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v PolyTraitRef<'v>) { - walk_list!(visitor, visit_generic_param, trait_ref.bound_generic_params); - visitor.visit_trait_ref(&trait_ref.trait_ref); -} - -pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef<'v>) { - visitor.visit_id(trait_ref.hir_ref_id); - visitor.visit_path(&trait_ref.path, trait_ref.hir_ref_id) -} - pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) { visitor.visit_id(param.hir_id); - visitor.visit_pat(¶m.pat); + visitor.visit_pat(param.pat); } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { @@ -529,7 +470,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { } ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn( FnKind::ItemFn(item.ident, generics, sig.header), - &sig.decl, + sig.decl, body_id, item.span, item.hir_id(), @@ -601,142 +542,80 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { } } -pub fn walk_inline_asm<'v, V: Visitor<'v>>(visitor: &mut V, asm: &'v InlineAsm<'v>, id: HirId) { - for (op, op_sp) in asm.operands { - match op { - InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - visitor.visit_expr(expr) - } - InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - visitor.visit_expr(expr); - } - } - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - visitor.visit_expr(in_expr); - if let Some(out_expr) = out_expr { - visitor.visit_expr(out_expr); - } - } - InlineAsmOperand::Const { anon_const, .. } - | InlineAsmOperand::SymFn { anon_const, .. } => visitor.visit_anon_const(anon_const), - InlineAsmOperand::SymStatic { path, .. } => visitor.visit_qpath(path, id, *op_sp), - } - } -} - -pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) { - visitor.visit_id(hir_id); - visitor.visit_path(path, hir_id); +pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) { + walk_list!(visitor, visit_param, body.params); + visitor.visit_expr(body.value); } -pub fn walk_enum_def<'v, V: Visitor<'v>>( - visitor: &mut V, - enum_definition: &'v EnumDef<'v>, - item_id: HirId, -) { - visitor.visit_id(item_id); - walk_list!(visitor, visit_variant, enum_definition.variants); +pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) { + visitor.visit_name(ident.name); } -pub fn walk_variant<'v, V: Visitor<'v>>(visitor: &mut V, variant: &'v Variant<'v>) { - visitor.visit_ident(variant.ident); - visitor.visit_id(variant.id); - visitor.visit_variant_data(&variant.data); - walk_list!(visitor, visit_anon_const, &variant.disr_expr); +pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) { + visitor.visit_id(mod_hir_id); + for &item_id in module.item_ids { + visitor.visit_nested_item(item_id); + } } -pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { - visitor.visit_id(typ.hir_id); +pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) { + visitor.visit_id(foreign_item.hir_id()); + visitor.visit_ident(foreign_item.ident); - match typ.kind { - TyKind::Slice(ref ty) => visitor.visit_ty(ty), - TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), - TyKind::Rptr(ref lifetime, ref mutable_type) => { - visitor.visit_lifetime(lifetime); - visitor.visit_ty(&mutable_type.ty) - } - TyKind::Never => {} - TyKind::Tup(tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); - } - TyKind::BareFn(ref function_declaration) => { - walk_list!(visitor, visit_generic_param, function_declaration.generic_params); - visitor.visit_fn_decl(&function_declaration.decl); - } - TyKind::Path(ref qpath) => { - visitor.visit_qpath(qpath, typ.hir_id, typ.span); - } - TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { - visitor.visit_nested_item(item_id); - walk_list!(visitor, visit_generic_arg, lifetimes); - } - TyKind::Array(ref ty, ref length) => { - visitor.visit_ty(ty); - visitor.visit_array_length(length) - } - TyKind::TraitObject(bounds, ref lifetime, _syntax) => { - for bound in bounds { - visitor.visit_poly_trait_ref(bound); + match foreign_item.kind { + ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => { + visitor.visit_generics(generics); + visitor.visit_fn_decl(function_declaration); + for ¶m_name in param_names { + visitor.visit_ident(param_name); } - visitor.visit_lifetime(lifetime); } - TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), - TyKind::Infer | TyKind::Err => {} + ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ), + ForeignItemKind::Type => (), } } -pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) { - visitor.visit_id(inf.hir_id); -} - -pub fn walk_qpath<'v, V: Visitor<'v>>(visitor: &mut V, qpath: &'v QPath<'v>, id: HirId) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - walk_list!(visitor, visit_ty, maybe_qself); - visitor.visit_path(path, id) - } - QPath::TypeRelative(ref qself, ref segment) => { - visitor.visit_ty(qself); - visitor.visit_path_segment(segment); - } - QPath::LangItem(..) => {} +pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { + // Intentionally visiting the expr first - the initialization expr + // dominates the local's definition. + walk_list!(visitor, visit_expr, &local.init); + visitor.visit_id(local.hir_id); + visitor.visit_pat(local.pat); + if let Some(els) = local.els { + visitor.visit_block(els); } + walk_list!(visitor, visit_ty, &local.ty); } -pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) { - for segment in path.segments { - visitor.visit_path_segment(segment); - } +pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) { + visitor.visit_id(block.hir_id); + walk_list!(visitor, visit_stmt, block.stmts); + walk_list!(visitor, visit_expr, &block.expr); } -pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V, segment: &'v PathSegment<'v>) { - visitor.visit_ident(segment.ident); - visitor.visit_id(segment.hir_id); - if let Some(ref args) = segment.args { - visitor.visit_generic_args(args); +pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) { + visitor.visit_id(statement.hir_id); + match statement.kind { + StmtKind::Local(ref local) => visitor.visit_local(local), + StmtKind::Item(item) => visitor.visit_nested_item(item), + StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => { + visitor.visit_expr(expression) + } } } -pub fn walk_generic_args<'v, V: Visitor<'v>>(visitor: &mut V, generic_args: &'v GenericArgs<'v>) { - walk_list!(visitor, visit_generic_arg, generic_args.args); - walk_list!(visitor, visit_assoc_type_binding, generic_args.bindings); -} - -pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( - visitor: &mut V, - type_binding: &'v TypeBinding<'v>, -) { - visitor.visit_id(type_binding.hir_id); - visitor.visit_ident(type_binding.ident); - visitor.visit_generic_args(type_binding.gen_args); - match type_binding.kind { - TypeBindingKind::Equality { ref term } => match term { - Term::Ty(ref ty) => visitor.visit_ty(ty), - Term::Const(ref c) => visitor.visit_anon_const(c), - }, - TypeBindingKind::Constraint { bounds } => walk_list!(visitor, visit_param_bound, bounds), +pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { + visitor.visit_id(arm.hir_id); + visitor.visit_pat(arm.pat); + if let Some(ref g) = arm.guard { + match g { + Guard::If(ref e) => visitor.visit_expr(e), + Guard::IfLet(ref l) => { + visitor.visit_let_expr(l); + } + } } + visitor.visit_expr(arm.body); } pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { @@ -781,36 +660,186 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'v>) { visitor.visit_id(field.hir_id); visitor.visit_ident(field.ident); - visitor.visit_pat(&field.pat) + visitor.visit_pat(field.pat) } -pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) { - visitor.visit_id(foreign_item.hir_id()); - visitor.visit_ident(foreign_item.ident); +pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen) { + match len { + &ArrayLen::Infer(hir_id, _span) => visitor.visit_id(hir_id), + ArrayLen::Body(c) => visitor.visit_anon_const(c), + } +} - match foreign_item.kind { - ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => { - visitor.visit_generics(generics); - visitor.visit_fn_decl(function_declaration); - for ¶m_name in param_names { - visitor.visit_ident(param_name); - } +pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) { + visitor.visit_id(constant.hir_id); + visitor.visit_nested_body(constant.body); +} + +pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { + visitor.visit_id(expression.hir_id); + match expression.kind { + ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression), + ExprKind::Array(subexpressions) => { + walk_list!(visitor, visit_expr, subexpressions); } - ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ), - ForeignItemKind::Type => (), + ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), + ExprKind::Repeat(ref element, ref count) => { + visitor.visit_expr(element); + visitor.visit_array_length(count) + } + ExprKind::Struct(ref qpath, fields, ref optional_base) => { + visitor.visit_qpath(qpath, expression.hir_id, expression.span); + walk_list!(visitor, visit_expr_field, fields); + walk_list!(visitor, visit_expr, optional_base); + } + ExprKind::Tup(subexpressions) => { + walk_list!(visitor, visit_expr, subexpressions); + } + ExprKind::Call(ref callee_expression, arguments) => { + visitor.visit_expr(callee_expression); + walk_list!(visitor, visit_expr, arguments); + } + ExprKind::MethodCall(ref segment, receiver, arguments, _) => { + visitor.visit_path_segment(segment); + visitor.visit_expr(receiver); + walk_list!(visitor, visit_expr, arguments); + } + ExprKind::Binary(_, ref left_expression, ref right_expression) => { + visitor.visit_expr(left_expression); + visitor.visit_expr(right_expression) + } + ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { + visitor.visit_expr(subexpression) + } + ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { + visitor.visit_expr(subexpression); + visitor.visit_ty(typ) + } + ExprKind::DropTemps(ref subexpression) => { + visitor.visit_expr(subexpression); + } + ExprKind::Let(ref let_expr) => visitor.visit_let_expr(let_expr), + ExprKind::If(ref cond, ref then, ref else_opt) => { + visitor.visit_expr(cond); + visitor.visit_expr(then); + walk_list!(visitor, visit_expr, else_opt); + } + ExprKind::Loop(ref block, ref opt_label, _, _) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } + ExprKind::Match(ref subexpression, arms, _) => { + visitor.visit_expr(subexpression); + walk_list!(visitor, visit_arm, arms); + } + ExprKind::Closure(&Closure { + def_id: _, + binder: _, + bound_generic_params, + fn_decl, + body, + capture_clause: _, + fn_decl_span: _, + fn_arg_span: _, + movability: _, + }) => { + walk_list!(visitor, visit_generic_param, bound_generic_params); + visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id) + } + ExprKind::Block(ref block, ref opt_label) => { + walk_list!(visitor, visit_label, opt_label); + visitor.visit_block(block); + } + ExprKind::Assign(ref lhs, ref rhs, _) => { + visitor.visit_expr(rhs); + visitor.visit_expr(lhs) + } + ExprKind::AssignOp(_, ref left_expression, ref right_expression) => { + visitor.visit_expr(right_expression); + visitor.visit_expr(left_expression); + } + ExprKind::Field(ref subexpression, ident) => { + visitor.visit_expr(subexpression); + visitor.visit_ident(ident); + } + ExprKind::Index(ref main_expression, ref index_expression) => { + visitor.visit_expr(main_expression); + visitor.visit_expr(index_expression) + } + ExprKind::Path(ref qpath) => { + visitor.visit_qpath(qpath, expression.hir_id, expression.span); + } + ExprKind::Break(ref destination, ref opt_expr) => { + walk_list!(visitor, visit_label, &destination.label); + walk_list!(visitor, visit_expr, opt_expr); + } + ExprKind::Continue(ref destination) => { + walk_list!(visitor, visit_label, &destination.label); + } + ExprKind::Ret(ref optional_expression) => { + walk_list!(visitor, visit_expr, optional_expression); + } + ExprKind::InlineAsm(ref asm) => { + visitor.visit_inline_asm(asm, expression.hir_id); + } + ExprKind::Yield(ref subexpression, _) => { + visitor.visit_expr(subexpression); + } + ExprKind::Lit(_) | ExprKind::Err => {} } } -pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) { - match *bound { - GenericBound::Trait(ref typ, _modifier) => { - visitor.visit_poly_trait_ref(typ); +pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) { + // match the visit order in walk_local + visitor.visit_expr(let_expr.init); + visitor.visit_id(let_expr.hir_id); + visitor.visit_pat(let_expr.pat); + walk_list!(visitor, visit_ty, let_expr.ty); +} + +pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) { + visitor.visit_id(field.hir_id); + visitor.visit_ident(field.ident); + visitor.visit_expr(field.expr) +} + +pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { + visitor.visit_id(typ.hir_id); + + match typ.kind { + TyKind::Slice(ref ty) => visitor.visit_ty(ty), + TyKind::Ptr(ref mutable_type) => visitor.visit_ty(mutable_type.ty), + TyKind::Rptr(ref lifetime, ref mutable_type) => { + visitor.visit_lifetime(lifetime); + visitor.visit_ty(mutable_type.ty) } - GenericBound::LangItemTrait(_, _span, hir_id, args) => { - visitor.visit_id(hir_id); - visitor.visit_generic_args(args); + TyKind::Never => {} + TyKind::Tup(tuple_element_types) => { + walk_list!(visitor, visit_ty, tuple_element_types); } - GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + TyKind::BareFn(ref function_declaration) => { + walk_list!(visitor, visit_generic_param, function_declaration.generic_params); + visitor.visit_fn_decl(function_declaration.decl); + } + TyKind::Path(ref qpath) => { + visitor.visit_qpath(qpath, typ.hir_id, typ.span); + } + TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + visitor.visit_nested_item(item_id); + walk_list!(visitor, visit_generic_arg, lifetimes); + } + TyKind::Array(ref ty, ref length) => { + visitor.visit_ty(ty); + visitor.visit_array_length(length) + } + TyKind::TraitObject(bounds, ref lifetime, _syntax) => { + for bound in bounds { + visitor.visit_poly_trait_ref(bound); + } + visitor.visit_lifetime(lifetime); + } + TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), + TyKind::Infer | TyKind::Err => {} } } @@ -875,25 +904,16 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( } } -pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) { - if let FnRetTy::Return(ref output_ty) = *ret_ty { - visitor.visit_ty(output_ty) - } -} - pub fn walk_fn_decl<'v, V: Visitor<'v>>(visitor: &mut V, function_declaration: &'v FnDecl<'v>) { for ty in function_declaration.inputs { visitor.visit_ty(ty) } - walk_fn_ret_ty(visitor, &function_declaration.output) + visitor.visit_fn_ret_ty(&function_declaration.output) } -pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'v>) { - match function_kind { - FnKind::ItemFn(_, generics, ..) => { - visitor.visit_generics(generics); - } - FnKind::Closure | FnKind::Method(..) => {} +pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) { + if let FnRetTy::Return(ref output_ty) = *ret_ty { + visitor.visit_ty(output_ty) } } @@ -910,13 +930,30 @@ pub fn walk_fn<'v, V: Visitor<'v>>( visitor.visit_nested_body(body_id) } +pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'v>) { + match function_kind { + FnKind::ItemFn(_, generics, ..) => { + visitor.visit_generics(generics); + } + FnKind::Closure | FnKind::Method(..) => {} + } +} + +pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v UsePath<'v>, hir_id: HirId) { + visitor.visit_id(hir_id); + let UsePath { segments, ref res, span } = *path; + for &res in res { + visitor.visit_path(&Path { segments, res, span }, hir_id); + } +} + pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) { // N.B., deliberately force a compilation error if/when new fields are added. let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item; let hir_id = trait_item.hir_id(); visitor.visit_ident(ident); - visitor.visit_generics(&generics); - visitor.visit_defaultness(&defaultness); + visitor.visit_generics(generics); + visitor.visit_defaultness(defaultness); match *kind { TraitItemKind::Const(ref ty, default) => { visitor.visit_id(hir_id); @@ -925,13 +962,13 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai } TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { visitor.visit_id(hir_id); - visitor.visit_fn_decl(&sig.decl); + visitor.visit_fn_decl(sig.decl); for ¶m_name in param_names { visitor.visit_ident(param_name); } } TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => { - visitor.visit_fn(FnKind::Method(ident, sig), &sig.decl, body_id, span, hir_id); + visitor.visit_fn(FnKind::Method(ident, sig), sig.decl, body_id, span, hir_id); } TraitItemKind::Type(bounds, ref default) => { visitor.visit_id(hir_id); @@ -973,7 +1010,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt ImplItemKind::Fn(ref sig, body_id) => { visitor.visit_fn( FnKind::Method(impl_item.ident, sig), - &sig.decl, + sig.decl, body_id, impl_item.span, impl_item.hir_id(), @@ -1004,6 +1041,29 @@ pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &' visitor.visit_associated_item_kind(kind); } +pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef<'v>) { + visitor.visit_id(trait_ref.hir_ref_id); + visitor.visit_path(trait_ref.path, trait_ref.hir_ref_id) +} + +pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) { + match *bound { + GenericBound::Trait(ref typ, _modifier) => { + visitor.visit_poly_trait_ref(typ); + } + GenericBound::LangItemTrait(_, _span, hir_id, args) => { + visitor.visit_id(hir_id); + visitor.visit_generic_args(args); + } + GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + } +} + +pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v PolyTraitRef<'v>) { + walk_list!(visitor, visit_generic_param, trait_ref.bound_generic_params); + visitor.visit_trait_ref(&trait_ref.trait_ref); +} + pub fn walk_struct_def<'v, V: Visitor<'v>>( visitor: &mut V, struct_definition: &'v VariantData<'v>, @@ -1015,176 +1075,94 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>( pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) { visitor.visit_id(field.hir_id); visitor.visit_ident(field.ident); - visitor.visit_ty(&field.ty); + visitor.visit_ty(field.ty); } -pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) { - visitor.visit_id(block.hir_id); - walk_list!(visitor, visit_stmt, block.stmts); - walk_list!(visitor, visit_expr, &block.expr); +pub fn walk_enum_def<'v, V: Visitor<'v>>( + visitor: &mut V, + enum_definition: &'v EnumDef<'v>, + item_id: HirId, +) { + visitor.visit_id(item_id); + walk_list!(visitor, visit_variant, enum_definition.variants); } -pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) { - visitor.visit_id(statement.hir_id); - match statement.kind { - StmtKind::Local(ref local) => visitor.visit_local(local), - StmtKind::Item(item) => visitor.visit_nested_item(item), - StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => { - visitor.visit_expr(expression) - } - } +pub fn walk_variant<'v, V: Visitor<'v>>(visitor: &mut V, variant: &'v Variant<'v>) { + visitor.visit_ident(variant.ident); + visitor.visit_id(variant.hir_id); + visitor.visit_variant_data(&variant.data); + walk_list!(visitor, visit_anon_const, &variant.disr_expr); } -pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen) { - match len { - &ArrayLen::Infer(hir_id, _span) => visitor.visit_id(hir_id), - ArrayLen::Body(c) => visitor.visit_anon_const(c), - } +pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) { + visitor.visit_ident(label.ident); } -pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) { - visitor.visit_id(constant.hir_id); - visitor.visit_nested_body(constant.body); +pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) { + visitor.visit_id(inf.hir_id); } -pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) { - // match the visit order in walk_local - visitor.visit_expr(let_expr.init); - visitor.visit_id(let_expr.hir_id); - visitor.visit_pat(let_expr.pat); - walk_list!(visitor, visit_ty, let_expr.ty); +pub fn walk_generic_arg<'v, V: Visitor<'v>>(visitor: &mut V, generic_arg: &'v GenericArg<'v>) { + match generic_arg { + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Type(ty) => visitor.visit_ty(ty), + GenericArg::Const(ct) => visitor.visit_anon_const(&ct.value), + GenericArg::Infer(inf) => visitor.visit_infer(inf), + } } -pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) { - visitor.visit_id(field.hir_id); - visitor.visit_ident(field.ident); - visitor.visit_expr(&field.expr) +pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) { + visitor.visit_id(lifetime.hir_id); + visitor.visit_ident(lifetime.ident); } -pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { - visitor.visit_id(expression.hir_id); - match expression.kind { - ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression), - ExprKind::Array(subexpressions) => { - walk_list!(visitor, visit_expr, subexpressions); - } - ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), - ExprKind::Repeat(ref element, ref count) => { - visitor.visit_expr(element); - visitor.visit_array_length(count) - } - ExprKind::Struct(ref qpath, fields, ref optional_base) => { - visitor.visit_qpath(qpath, expression.hir_id, expression.span); - walk_list!(visitor, visit_expr_field, fields); - walk_list!(visitor, visit_expr, optional_base); - } - ExprKind::Tup(subexpressions) => { - walk_list!(visitor, visit_expr, subexpressions); - } - ExprKind::Call(ref callee_expression, arguments) => { - visitor.visit_expr(callee_expression); - walk_list!(visitor, visit_expr, arguments); +pub fn walk_qpath<'v, V: Visitor<'v>>(visitor: &mut V, qpath: &'v QPath<'v>, id: HirId) { + match *qpath { + QPath::Resolved(ref maybe_qself, ref path) => { + walk_list!(visitor, visit_ty, maybe_qself); + visitor.visit_path(path, id) } - ExprKind::MethodCall(ref segment, receiver, arguments, _) => { + QPath::TypeRelative(ref qself, ref segment) => { + visitor.visit_ty(qself); visitor.visit_path_segment(segment); - visitor.visit_expr(receiver); - walk_list!(visitor, visit_expr, arguments); - } - ExprKind::Binary(_, ref left_expression, ref right_expression) => { - visitor.visit_expr(left_expression); - visitor.visit_expr(right_expression) - } - ExprKind::AddrOf(_, _, ref subexpression) | ExprKind::Unary(_, ref subexpression) => { - visitor.visit_expr(subexpression) - } - ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { - visitor.visit_expr(subexpression); - visitor.visit_ty(typ) - } - ExprKind::DropTemps(ref subexpression) => { - visitor.visit_expr(subexpression); - } - ExprKind::Let(ref let_expr) => visitor.visit_let_expr(let_expr), - ExprKind::If(ref cond, ref then, ref else_opt) => { - visitor.visit_expr(cond); - visitor.visit_expr(then); - walk_list!(visitor, visit_expr, else_opt); - } - ExprKind::Loop(ref block, ref opt_label, _, _) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_block(block); - } - ExprKind::Match(ref subexpression, arms, _) => { - visitor.visit_expr(subexpression); - walk_list!(visitor, visit_arm, arms); - } - ExprKind::Closure(&Closure { - binder: _, - bound_generic_params, - fn_decl, - body, - capture_clause: _, - fn_decl_span: _, - movability: _, - }) => { - walk_list!(visitor, visit_generic_param, bound_generic_params); - visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id) - } - ExprKind::Block(ref block, ref opt_label) => { - walk_list!(visitor, visit_label, opt_label); - visitor.visit_block(block); - } - ExprKind::Assign(ref lhs, ref rhs, _) => { - visitor.visit_expr(rhs); - visitor.visit_expr(lhs) - } - ExprKind::AssignOp(_, ref left_expression, ref right_expression) => { - visitor.visit_expr(right_expression); - visitor.visit_expr(left_expression); - } - ExprKind::Field(ref subexpression, ident) => { - visitor.visit_expr(subexpression); - visitor.visit_ident(ident); - } - ExprKind::Index(ref main_expression, ref index_expression) => { - visitor.visit_expr(main_expression); - visitor.visit_expr(index_expression) - } - ExprKind::Path(ref qpath) => { - visitor.visit_qpath(qpath, expression.hir_id, expression.span); - } - ExprKind::Break(ref destination, ref opt_expr) => { - walk_list!(visitor, visit_label, &destination.label); - walk_list!(visitor, visit_expr, opt_expr); - } - ExprKind::Continue(ref destination) => { - walk_list!(visitor, visit_label, &destination.label); - } - ExprKind::Ret(ref optional_expression) => { - walk_list!(visitor, visit_expr, optional_expression); } - ExprKind::InlineAsm(ref asm) => { - visitor.visit_inline_asm(asm, expression.hir_id); - } - ExprKind::Yield(ref subexpression, _) => { - visitor.visit_expr(subexpression); - } - ExprKind::Lit(_) | ExprKind::Err => {} + QPath::LangItem(..) => {} } } -pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { - visitor.visit_id(arm.hir_id); - visitor.visit_pat(&arm.pat); - if let Some(ref g) = arm.guard { - match g { - Guard::If(ref e) => visitor.visit_expr(e), - Guard::IfLet(ref l) => { - visitor.visit_let_expr(l); - } - } +pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &Path<'v>) { + for segment in path.segments { + visitor.visit_path_segment(segment); + } +} + +pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V, segment: &'v PathSegment<'v>) { + visitor.visit_ident(segment.ident); + visitor.visit_id(segment.hir_id); + if let Some(ref args) = segment.args { + visitor.visit_generic_args(args); + } +} + +pub fn walk_generic_args<'v, V: Visitor<'v>>(visitor: &mut V, generic_args: &'v GenericArgs<'v>) { + walk_list!(visitor, visit_generic_arg, generic_args.args); + walk_list!(visitor, visit_assoc_type_binding, generic_args.bindings); +} + +pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( + visitor: &mut V, + type_binding: &'v TypeBinding<'v>, +) { + visitor.visit_id(type_binding.hir_id); + visitor.visit_ident(type_binding.ident); + visitor.visit_generic_args(type_binding.gen_args); + match type_binding.kind { + TypeBindingKind::Equality { ref term } => match term { + Term::Ty(ref ty) => visitor.visit_ty(ty), + Term::Const(ref c) => visitor.visit_anon_const(c), + }, + TypeBindingKind::Constraint { bounds } => walk_list!(visitor, visit_param_bound, bounds), } - visitor.visit_expr(&arm.body); } pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) { @@ -1198,3 +1176,27 @@ pub fn walk_defaultness<'v, V: Visitor<'v>>(_: &mut V, _: &'v Defaultness) { // the right thing to do, should content be added in the future, // would be to walk it. } + +pub fn walk_inline_asm<'v, V: Visitor<'v>>(visitor: &mut V, asm: &'v InlineAsm<'v>, id: HirId) { + for (op, op_sp) in asm.operands { + match op { + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + visitor.visit_expr(expr) + } + InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + visitor.visit_expr(expr); + } + } + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + visitor.visit_expr(in_expr); + if let Some(out_expr) = out_expr { + visitor.visit_expr(out_expr); + } + } + InlineAsmOperand::Const { anon_const, .. } + | InlineAsmOperand::SymFn { anon_const, .. } => visitor.visit_anon_const(anon_const), + InlineAsmOperand::SymStatic { path, .. } => visitor.visit_qpath(path, id, *op_sp), + } + } +} diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index ca615a491..038509031 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -12,35 +12,56 @@ use crate::errors::LangItemError; use crate::{MethodKind, Target}; use rustc_ast as ast; -use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable_Generic; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::sync::LazyLock; - -pub enum LangItemGroup { - Op, - Fn, +/// All of the language items, defined or not. +/// Defined lang items can come from the current crate or its dependencies. +#[derive(HashStable_Generic, Debug)] +pub struct LanguageItems { + /// Mappings from lang items to their possibly found [`DefId`]s. + /// The index corresponds to the order in [`LangItem`]. + items: [Option<DefId>; std::mem::variant_count::<LangItem>()], + /// Lang items that were not found during collection. + pub missing: Vec<LangItem>, } -const NUM_GROUPS: usize = 2; +impl LanguageItems { + /// Construct an empty collection of lang items and no missing ones. + pub fn new() -> Self { + Self { items: [None; std::mem::variant_count::<LangItem>()], missing: Vec::new() } + } + + pub fn get(&self, item: LangItem) -> Option<DefId> { + self.items[item as usize] + } + + pub fn set(&mut self, item: LangItem, def_id: DefId) { + self.items[item as usize] = Some(def_id); + } + + /// Requires that a given `LangItem` was bound and returns the corresponding `DefId`. + /// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`, + /// returns an error encapsulating the `LangItem`. + pub fn require(&self, it: LangItem) -> Result<DefId, LangItemError> { + self.get(it).ok_or_else(|| LangItemError(it)) + } -macro_rules! expand_group { - () => { - None - }; - ($group:expr) => { - Some($group) - }; + pub fn iter<'a>(&'a self) -> impl Iterator<Item = (LangItem, DefId)> + 'a { + self.items + .iter() + .enumerate() + .filter_map(|(i, id)| id.map(|id| (LangItem::from_u32(i as u32).unwrap(), id))) + } } // The actual lang items defined come at the end of this file in one handy table. // So you probably just want to nip down to the end. macro_rules! language_item_table { ( - $( $(#[$attr:meta])* $variant:ident $($group:expr)?, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* + $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* ) => { enum_from_u32! { @@ -66,66 +87,36 @@ macro_rules! language_item_table { } } - /// The [group](LangItemGroup) that this lang item belongs to, - /// or `None` if it doesn't belong to a group. - pub fn group(self) -> Option<LangItemGroup> { - use LangItemGroup::*; - match self { - $( LangItem::$variant => expand_group!($($group)*), )* + /// Opposite of [`LangItem::name`] + pub fn from_name(name: Symbol) -> Option<Self> { + match name { + $( $module::$name => Some(LangItem::$variant), )* + _ => None, } } - pub fn required_generics(&self) -> GenericRequirement { + /// Returns the name of the `LangItem` enum variant. + // This method is used by Clippy for internal lints. + pub fn variant_name(self) -> &'static str { match self { - $( LangItem::$variant => $generics, )* + $( LangItem::$variant => stringify!($variant), )* } } - } - - /// All of the language items, defined or not. - /// Defined lang items can come from the current crate or its dependencies. - #[derive(HashStable_Generic, Debug)] - pub struct LanguageItems { - /// Mappings from lang items to their possibly found [`DefId`]s. - /// The index corresponds to the order in [`LangItem`]. - pub items: Vec<Option<DefId>>, - /// Lang items that were not found during collection. - pub missing: Vec<LangItem>, - /// Mapping from [`LangItemGroup`] discriminants to all - /// [`DefId`]s of lang items in that group. - pub groups: [Vec<DefId>; NUM_GROUPS], - } - impl LanguageItems { - /// Construct an empty collection of lang items and no missing ones. - pub fn new() -> Self { - fn init_none(_: LangItem) -> Option<DefId> { None } - const EMPTY: Vec<DefId> = Vec::new(); - - Self { - items: vec![$(init_none(LangItem::$variant)),*], - missing: Vec::new(), - groups: [EMPTY; NUM_GROUPS], + pub fn target(self) -> Target { + match self { + $( LangItem::$variant => $target, )* } } - /// Returns the mappings to the possibly found `DefId`s for each lang item. - pub fn items(&self) -> &[Option<DefId>] { - &*self.items - } - - /// Requires that a given `LangItem` was bound and returns the corresponding `DefId`. - /// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`, - /// returns an error encapsulating the `LangItem`. - pub fn require(&self, it: LangItem) -> Result<DefId, LangItemError> { - self.items[it as usize].ok_or_else(|| LangItemError(it)) - } - - /// Returns the [`DefId`]s of all lang items in a group. - pub fn group(&self, group: LangItemGroup) -> &[DefId] { - self.groups[group as usize].as_ref() + pub fn required_generics(&self) -> GenericRequirement { + match self { + $( LangItem::$variant => $generics, )* + } } + } + impl LanguageItems { $( #[doc = concat!("Returns the [`DefId`] of the `", stringify!($name), "` lang item if it is defined.")] pub fn $method(&self) -> Option<DefId> { @@ -133,15 +124,6 @@ macro_rules! language_item_table { } )* } - - /// A mapping from the name of the lang item to its order and the form it must be of. - pub static ITEM_REFS: LazyLock<FxIndexMap<Symbol, (usize, Target)>> = LazyLock::new(|| { - let mut item_refs = FxIndexMap::default(); - $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )* - item_refs - }); - -// End of the macro } } @@ -152,14 +134,12 @@ impl<CTX> HashStable<CTX> for LangItem { } /// Extracts the first `lang = "$name"` out of a list of attributes. -/// The attributes `#[panic_handler]` and `#[alloc_error_handler]` -/// are also extracted out when found. +/// The `#[panic_handler]` attribute is also extracted out when found. pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { attrs.iter().find_map(|attr| { Some(match attr { _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), - _ if attr.has_name(sym::alloc_error_handler) => (sym::oom, attr.span), _ => return None, }) }) @@ -196,30 +176,30 @@ language_item_table! { TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); - Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1); - Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); - Mul(Op), sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); - Div(Op), sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1); - Rem(Op), sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); - Neg(Op), sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); - Not(Op), sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0); - BitXor(Op), sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); - BitAnd(Op), sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); - BitOr(Op), sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); - Shl(Op), sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); - Shr(Op), sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); - AddAssign(Op), sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); - SubAssign(Op), sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); - MulAssign(Op), sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); - DivAssign(Op), sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); - RemAssign(Op), sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitXorAssign(Op), sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitAndAssign(Op), sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitOrAssign(Op), sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShlAssign(Op), sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShrAssign(Op), sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); - Index(Op), sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1); - IndexMut(Op), sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); + Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1); + Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); + Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); + Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1); + Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); + Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); + Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0); + BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); + BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); + BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); + Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); + Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); + AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); + SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); + MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); + DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); + RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); + Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1); + IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; @@ -229,9 +209,9 @@ language_item_table! { DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; - Fn(Fn), kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut(Fn), sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce(Fn), sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; @@ -241,8 +221,8 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; - PartialEq(Op), sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd(Op), sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. @@ -266,7 +246,6 @@ language_item_table! { ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); - Oom, sym::oom, oom, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); @@ -299,10 +278,16 @@ language_item_table! { TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + PointerSized, sym::pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0); + + Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; - FromGenerator, sym::from_generator, from_generator_fn, Target::Fn, GenericRequirement::None; + // FIXME(swatinem): the following lang items are used for async lowering and + // should become obsolete eventually. + ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; + IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None; GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None; FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; @@ -331,6 +316,8 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + + String, sym::String, string, Target::Struct, GenericRequirement::None; } pub enum GenericRequirement { @@ -338,3 +325,34 @@ pub enum GenericRequirement { Minimum(usize), Exact(usize), } + +pub static FN_TRAITS: &'static [LangItem] = &[LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]; + +pub static OPERATORS: &'static [LangItem] = &[ + LangItem::Add, + LangItem::Sub, + LangItem::Mul, + LangItem::Div, + LangItem::Rem, + LangItem::Neg, + LangItem::Not, + LangItem::BitXor, + LangItem::BitAnd, + LangItem::BitOr, + LangItem::Shl, + LangItem::Shr, + LangItem::AddAssign, + LangItem::SubAssign, + LangItem::MulAssign, + LangItem::DivAssign, + LangItem::RemAssign, + LangItem::BitXorAssign, + LangItem::BitAndAssign, + LangItem::BitOrAssign, + LangItem::ShlAssign, + LangItem::ShrAssign, + LangItem::Index, + LangItem::IndexMut, + LangItem::PartialEq, + LangItem::PartialOrd, +]; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 1c4aa420c..98d967cc0 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,10 +5,11 @@ #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(const_btree_len)] -#![feature(once_cell)] +#![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] +#![feature(variant_count)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 0c1819bb0..6e2fbf96c 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -130,10 +130,7 @@ impl hir::Pat<'_> { pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> { let mut result = None; self.each_binding(|annotation, _, _, _| match annotation { - hir::BindingAnnotation::REF => match result { - None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not), - _ => {} - }, + hir::BindingAnnotation::REF if result.is_none() => result = Some(hir::Mutability::Not), hir::BindingAnnotation::REF_MUT => result = Some(hir::Mutability::Mut), _ => {} }); diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 4636d5152..d47911509 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -1,5 +1,7 @@ use crate::definitions::{DefKey, DefPathData, DisambiguatedDefPathData}; use rustc_span::def_id::{DefPathHash, StableCrateId}; +use rustc_span::edition::Edition; +use rustc_span::{create_session_if_not_set_then, Symbol}; #[test] fn def_path_hash_depends_on_crate_id() { @@ -11,26 +13,28 @@ fn def_path_hash_depends_on_crate_id() { // the crate by changing the crate disambiguator (e.g. via bumping the // crate's version number). - let id0 = StableCrateId::new("foo", false, vec!["1".to_string()]); - let id1 = StableCrateId::new("foo", false, vec!["2".to_string()]); + create_session_if_not_set_then(Edition::Edition2024, |_| { + let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()]); + let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()]); - let h0 = mk_test_hash(id0); - let h1 = mk_test_hash(id1); + let h0 = mk_test_hash(id0); + let h1 = mk_test_hash(id1); - assert_ne!(h0.stable_crate_id(), h1.stable_crate_id()); - assert_ne!(h0.local_hash(), h1.local_hash()); + assert_ne!(h0.stable_crate_id(), h1.stable_crate_id()); + assert_ne!(h0.local_hash(), h1.local_hash()); - fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash { - let parent_hash = DefPathHash::new(stable_crate_id, 0); + fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash { + let parent_hash = DefPathHash::new(stable_crate_id, 0); - let key = DefKey { - parent: None, - disambiguated_data: DisambiguatedDefPathData { - data: DefPathData::CrateRoot, - disambiguator: 0, - }, - }; + let key = DefKey { + parent: None, + disambiguated_data: DisambiguatedDefPathData { + data: DefPathData::CrateRoot, + disambiguator: 0, + }, + }; - key.compute_stable_hash(parent_hash) - } + key.compute_stable_hash(parent_hash) + } + }) } diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index da9c9c121..0cc50c6dd 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -1,53 +1,30 @@ //! Validity checking for weak lang items -use crate::def_id::DefId; -use crate::{lang_items, LangItem, LanguageItems}; +use crate::LangItem; -use rustc_ast as ast; -use rustc_data_structures::fx::FxIndexMap; use rustc_span::symbol::{sym, Symbol}; -use std::sync::LazyLock; - macro_rules! weak_lang_items { - ($($name:ident, $item:ident, $sym:ident;)*) => ( - -pub static WEAK_ITEMS_REFS: LazyLock<FxIndexMap<Symbol, LangItem>> = LazyLock::new(|| { - let mut map = FxIndexMap::default(); - $(map.insert(sym::$name, LangItem::$item);)* - map -}); - -pub static WEAK_ITEMS_SYMBOLS: LazyLock<FxIndexMap<LangItem, Symbol>> = LazyLock::new(|| { - let mut map = FxIndexMap::default(); - $(map.insert(LangItem::$item, sym::$sym);)* - map -}); - -pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> -{ - lang_items::extract(attrs).and_then(|(name, _)| { - $(if name == sym::$name { - Some(sym::$sym) - } else)* { - None + ($($item:ident, $sym:ident;)*) => { + pub static WEAK_LANG_ITEMS: &[LangItem] = &[$(LangItem::$item,)*]; + + impl LangItem { + pub fn is_weak(self) -> bool { + matches!(self, $(LangItem::$item)|*) + } + + pub fn link_name(self) -> Option<Symbol> { + match self { + $( LangItem::$item => Some(sym::$sym),)* + _ => None, + } + } } - }) -} - -impl LanguageItems { - pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool { - let did = Some(item_def_id); - - $(self.$name() == did)||* } } -) } - weak_lang_items! { - panic_impl, PanicImpl, rust_begin_unwind; - eh_personality, EhPersonality, rust_eh_personality; - eh_catch_typeinfo, EhCatchTypeinfo, rust_eh_catch_typeinfo; - oom, Oom, rust_oom; + PanicImpl, rust_begin_unwind; + EhPersonality, rust_eh_personality; + EhCatchTypeinfo, rust_eh_catch_typeinfo; } diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index a9152bdc5..e6465d641 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -177,11 +177,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .all_traits() .filter(|trait_def_id| { let viz = self.tcx().visibility(*trait_def_id); - if let Some(def_id) = self.item_def_id() { - viz.is_accessible_from(def_id, self.tcx()) - } else { - viz.is_visible_locally() - } + let def_id = self.item_def_id(); + viz.is_accessible_from(def_id, self.tcx()) }) .collect(); diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 47915b4bd..f64d65cc6 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -11,7 +11,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, }; @@ -83,9 +82,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Def(DefKind::TyParam, src_def_id) => { if let Some(param_local_id) = param.def_id.as_local() { let param_name = tcx.hir().ty_param_name(param_local_id); - let infcx = tcx.infer_ctxt().build(); - let param_type = - infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)); + let param_type = tcx.type_of(param.def_id); if param_type.is_suggestable(tcx, false) { err.span_suggestion( tcx.def_span(src_def_id), diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 38f195dab..78d204d47 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -23,7 +23,6 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; -use rustc_hir::lang_items::LangItem; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; @@ -55,7 +54,7 @@ pub struct PathSeg(pub DefId, pub usize); pub trait AstConv<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; - fn item_def_id(&self) -> Option<DefId>; + fn item_def_id(&self) -> DefId; /// Returns predicates in scope of the form `X: Foo<T>`, where `X` /// is a type parameter `X` with the given id `def_id` and T @@ -110,13 +109,16 @@ pub trait AstConv<'tcx> { ) -> Ty<'tcx>; /// Normalize an associated type coming from the user. + /// + /// This should only be used by astconv. Use `FnCtxt::normalize` + /// or `ObligationCtxt::normalize` in downstream crates. fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; /// Invoked when we encounter an error from some prior pass /// (e.g., resolve) that is translated into a ty-error. This is /// used to help suppress derived errors typeck might otherwise /// report. - fn set_tainted_by_errors(&self); + fn set_tainted_by_errors(&self, e: ErrorGuaranteed); fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); } @@ -242,14 +244,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } None => { - self.re_infer(def, lifetime.span).unwrap_or_else(|| { + self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| { debug!(?lifetime, "unelided lifetime in signature"); // This indicates an illegal lifetime // elision. `resolve_lifetime` should have // reported an error in this case -- but if // not, let's error out. - tcx.sess.delay_span_bug(lifetime.span, "unelided lifetime in signature"); + tcx.sess.delay_span_bug(lifetime.ident.span, "unelided lifetime in signature"); // Supply some dummy value. We don't have an // `re_error`, annoyingly, so use `'static`. @@ -275,7 +277,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment.args(), item_segment.infer_args, None, - None, + ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); @@ -325,7 +327,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option<Ty<'tcx>>, - constness: Option<ty::BoundConstness>, + constness: ty::BoundConstness, ) -> (SubstsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -345,7 +347,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert!(self_ty.is_some()); } } else { - assert!(self_ty.is_none() && parent_substs.is_empty()); + assert!(self_ty.is_none()); } let arg_count = Self::check_generic_arg_count( @@ -433,7 +435,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Const::from_opt_const_arg_anon_const( tcx, ty::WithOptConstParam { - did: tcx.hir().local_def_id(ct.value.hir_id), + did: ct.value.def_id, const_param_did: Some(param.def_id), }, ) @@ -501,6 +503,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); + if ty.references_error() { + return tcx.const_error(ty).into(); + } if !infer_args && has_default { tcx.bound_const_param_default(param.def_id) .subst(tcx, substs.unwrap()) @@ -536,7 +541,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &mut substs_ctx, ); - if let Some(ty::BoundConstness::ConstIfConst) = constness + if let ty::BoundConstness::ConstIfConst = constness && generics.has_self && !tcx.has_attr(def_id, sym::const_trait) { tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } ); @@ -568,8 +573,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into()) } hir::Term::Const(ref c) => { - let local_did = self.tcx().hir().local_def_id(c.hir_id); - let c = Const::from_anon_const(self.tcx(), local_did); + let c = Const::from_anon_const(self.tcx(), c.def_id); ConvertedBindingKind::Equality(c.into()) } }, @@ -609,7 +613,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment.args(), item_segment.infer_args, None, - None, + ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { @@ -639,7 +643,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty, trait_ref.path.segments.last().unwrap(), true, - Some(constness), + constness, ) } @@ -666,7 +670,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { args, infer_args, Some(self_ty), - Some(constness), + constness, ); let tcx = self.tcx(); @@ -796,7 +800,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'_>, is_impl: bool, - constness: Option<ty::BoundConstness>, + constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { let (substs, _) = self.create_substs_for_ast_trait_ref( span, @@ -820,7 +824,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, is_impl: bool, - constness: Option<ty::BoundConstness>, + constness: ty::BoundConstness, ) -> (SubstsRef<'tcx>, GenericArgCountResult) { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); @@ -849,12 +853,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .is_some() } - // Sets `implicitly_sized` to true on `Bounds` if necessary + /// Sets `implicitly_sized` to true on `Bounds` if necessary pub(crate) fn add_implicitly_sized<'hir>( &self, bounds: &mut Bounds<'hir>, ast_bounds: &'hir [hir::GenericBound<'hir>], - self_ty_where_predicates: Option<(hir::HirId, &'hir [hir::WherePredicate<'hir>])>, + self_ty_where_predicates: Option<(LocalDefId, &'hir [hir::WherePredicate<'hir>])>, span: Span, ) { let tcx = self.tcx(); @@ -874,19 +878,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; search_bounds(ast_bounds); if let Some((self_ty, where_clause)) = self_ty_where_predicates { - let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id(); for clause in where_clause { if let hir::WherePredicate::BoundPredicate(pred) = clause { - if pred.is_param_bound(self_ty_def_id) { + if pred.is_param_bound(self_ty.to_def_id()) { search_bounds(pred.bounds); } } } } - let sized_def_id = tcx.lang_items().require(LangItem::Sized); + let sized_def_id = tcx.lang_items().sized_trait(); match (&sized_def_id, unbound) { - (Ok(sized_def_id), Some(tpb)) + (Some(sized_def_id), Some(tpb)) if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) => { // There was in fact a `?Sized` bound, return without doing anything @@ -906,7 +909,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // There was no `?Sized` bound; add implicitly sized if `Sized` is available. } } - if sized_def_id.is_err() { + if sized_def_id.is_none() { // No lang item for `Sized`, so we can't add it as a bound. return; } @@ -961,9 +964,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::GenericBound::Outlives(lifetime) => { let region = self.ast_region_to_region(lifetime, None); - bounds - .region_bounds - .push((ty::Binder::bind_with_vars(region, bound_vars), lifetime.span)); + bounds.region_bounds.push(( + ty::Binder::bind_with_vars(region, bound_vars), + lifetime.ident.span, + )); } } } @@ -1199,7 +1203,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (_, _) => { let got = if let Some(_) = term.ty() { "type" } else { "constant" }; let expected = def_kind.descr(assoc_item_def_id); - tcx.sess + let reported = tcx + .sess .struct_span_err( binding.span, &format!("expected {expected} bound, found {got}"), @@ -1210,11 +1215,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) .emit(); term = match def_kind { - hir::def::DefKind::AssocTy => tcx.ty_error().into(), + hir::def::DefKind::AssocTy => { + tcx.ty_error_with_guaranteed(reported).into() + } hir::def::DefKind::AssocConst => tcx - .const_error( + .const_error_with_guaranteed( tcx.bound_type_of(assoc_item_def_id) .subst(tcx, projection_ty.skip_binder().substs), + reported, ) .into(), _ => unreachable!(), @@ -1332,8 +1340,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|&(trait_ref, _, _)| trait_ref.def_id()) .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) .map(|trait_ref| tcx.def_span(trait_ref)); - tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - return tcx.ty_error(); + let reported = + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + return tcx.ty_error_with_guaranteed(reported); } // Check that there are no gross object safety violations; @@ -1343,14 +1352,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let object_safety_violations = astconv_object_safety_violations(tcx, item.trait_ref().def_id()); if !object_safety_violations.is_empty() { - report_object_safety_error( + let reported = report_object_safety_error( tcx, span, item.trait_ref().def_id(), &object_safety_violations, ) .emit(); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported); } } @@ -1373,7 +1382,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { let pred = bound_predicate.rebind(pred); associated_types.entry(span).or_default().extend( tcx.associated_items(pred.def_id()) @@ -1382,7 +1391,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|item| item.def_id), ); } - ty::PredicateKind::Projection(pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be substituted with a // `trait_object_dummy_self`, so check for that. @@ -1812,7 +1821,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Check if we have an enum variant. let mut variant_resolution = None; - if let ty::Adt(adt_def, _) = qself_ty.kind() { + if let ty::Adt(adt_def, adt_substs) = qself_ty.kind() { if adt_def.is_enum() { let variant_def = adt_def .variants() @@ -1908,6 +1917,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } + + // see if we can satisfy using an inherent associated type + for &impl_ in tcx.inherent_impls(adt_def.did()) { + let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else { + continue; + }; + let item_substs = self.create_substs_for_associated_item( + span, + assoc_ty_did, + assoc_segment, + adt_substs, + ); + let ty = tcx.bound_type_of(assoc_ty_did).subst(tcx, item_substs); + let ty = self.normalize_ty(span, ty); + return Ok((ty, DefKind::AssocTy, assoc_ty_did)); + } } // Find the type of the associated item, and the trait where the associated @@ -1977,7 +2002,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } err.emit() - } else if let Some(reported) = qself_ty.error_reported() { + } else if let Err(reported) = qself_ty.error_reported() { reported } else { // Don't print `TyErr` to the user. @@ -1993,37 +2018,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; let trait_did = bound.def_id(); - let (assoc_ident, def_scope) = - tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id); - - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `filter_by_name_and_kind`. - let item = tcx.associated_items(trait_did).in_definition_order().find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - }); - // Assume that if it's not matched, there must be a const defined with the same name - // but it was used in a type position. - let Some(item) = item else { + let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did) else { + // Assume that if it's not matched, there must be a const defined with the same name + // but it was used in a type position. let msg = format!("found associated const `{assoc_ident}` when type was expected"); let guar = tcx.sess.struct_span_err(span, &msg).emit(); return Err(guar); }; - let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); + let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); let ty = self.normalize_ty(span, ty); - let kind = DefKind::AssocTy; - if !item.visibility(tcx).is_accessible_from(def_scope, tcx) { - let kind = kind.descr(item.def_id); - let msg = format!("{} `{}` is private", kind, assoc_ident); - tcx.sess - .struct_span_err(span, &msg) - .span_label(span, &format!("private {}", kind)) - .emit(); - } - tcx.check_stability(item.def_id, Some(hir_ref_id), span, None); - if let Some(variant_def_id) = variant_resolution { tcx.struct_span_lint_hir( AMBIGUOUS_ASSOCIATED_ITEMS, @@ -2042,7 +2047,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(kind, item.def_id, " also"); + could_refer_to(DefKind::AssocTy, assoc_ty_did, " also"); lint.span_suggestion( span, @@ -2055,7 +2060,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, ); } - Ok((ty, kind, item.def_id)) + Ok((ty, DefKind::AssocTy, assoc_ty_did)) + } + + fn lookup_assoc_ty( + &self, + ident: Ident, + block: hir::HirId, + span: Span, + scope: DefId, + ) -> Option<DefId> { + let tcx = self.tcx(); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); + + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead + // of calling `find_by_name_and_kind`. + let item = tcx.associated_items(scope).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == ident + })?; + + let kind = DefKind::AssocTy; + if !item.visibility(tcx).is_accessible_from(def_scope, tcx) { + let kind = kind.descr(item.def_id); + let msg = format!("{kind} `{ident}` is private"); + let def_span = self.tcx().def_span(item.def_id); + tcx.sess + .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624)) + .span_label(span, &format!("private {kind}")) + .span_label(def_span, &format!("{kind} defined here")) + .emit(); + } + tcx.check_stability(item.def_id, Some(block), span, None); + + Some(item.def_id) } fn qpath_to_ty( @@ -2080,17 +2118,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); - let parent_def_id = def_id - .and_then(|def_id| { - def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) - }) + let parent_def_id = def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); // If the trait in segment is the same as the trait defining the item, // use the `<Self as ..>` syntax in the error. - let is_part_of_self_trait_constraints = def_id == Some(trait_def_id); + let is_part_of_self_trait_constraints = def_id == trait_def_id; let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { @@ -2099,13 +2134,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "Type" }; - self.report_ambiguous_associated_type( + let reported = self.report_ambiguous_associated_type( span, type_name, &path_str, item_segment.ident.name, ); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported) }; debug!("qpath_to_ty: self_type={:?}", self_ty); @@ -2116,7 +2151,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty, trait_segment, false, - Some(constness), + constness, ); let item_substs = self.create_substs_for_associated_item( @@ -2365,7 +2400,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { path_segs } - // Check a type `Path` and convert it to a `Ty`. + /// Check a type `Path` and convert it to a `Ty`. pub fn res_to_ty( &self, opt_self_ty: Option<Ty<'tcx>>, @@ -2383,7 +2418,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { match path.res { Res::Def(DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder, did) => { // Check for desugared `impl Trait`. - assert!(ty::is_impl_trait_defn(tcx, did).is_none()); + assert!(tcx.is_type_alias_impl_trait(did)); let item_segment = path.segments.split_last().unwrap(); self.prohibit_generics(item_segment.1.iter(), |err| { err.note("`impl Trait` types can't have type parameters"); @@ -2547,8 +2582,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { err.span_note(impl_.self_ty.span, "not a concrete type"); } - err.emit(); - tcx.ty_error() + tcx.ty_error_with_guaranteed(err.emit()) } else { self.normalize_ty(span, ty) } @@ -2596,8 +2630,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } Res::Err => { - self.set_tainted_by_errors(); - self.tcx().ty_error() + let e = self + .tcx() + .sess + .delay_span_bug(path.span, "path with `Res:Err` but no error emitted"); + self.set_tainted_by_errors(e); + self.tcx().ty_error_with_guaranteed(e) } _ => span_bug!(span, "unexpected resolution: {:?}", path.res), } @@ -2687,7 +2725,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &GenericArgs::none(), true, None, - None, + ty::BoundConstness::NotConst, ); EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id))) .subst(tcx, substs) @@ -2696,8 +2734,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let length = match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span), hir::ArrayLen::Body(constant) => { - let length_def_id = tcx.hir().local_def_id(constant.hir_id); - ty::Const::from_anon_const(tcx, length_def_id) + ty::Const::from_anon_const(tcx, constant.def_id) } }; @@ -2705,7 +2742,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.normalize_ty(ast_ty.span, array_ty) } hir::TyKind::Typeof(ref e) => { - let ty_erased = tcx.type_of(tcx.hir().local_def_id(e.hir_id)); + let ty_erased = tcx.type_of(e.def_id); let ty = tcx.fold_regions(ty_erased, |r, _| { if r.is_erased() { tcx.lifetimes.re_static } else { r } }); @@ -2750,35 +2787,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let substs = InternalSubsts::for_item(tcx, def_id, |param, _| { if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) { // Our own parameters are the resolved lifetimes. - if let GenericParamDefKind::Lifetime = param.kind { - if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] { - self.ast_region_to_region(lifetime, None).into() - } else { - bug!() - } - } else { - bug!() - } + let GenericParamDefKind::Lifetime { .. } = param.kind else { bug!() }; + let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { bug!() }; + self.ast_region_to_region(lifetime, None).into() } else { - match param.kind { - // For RPIT (return position impl trait), only lifetimes - // mentioned in the impl Trait predicate are captured by - // the opaque type, so the lifetime parameters from the - // parent item need to be replaced with `'static`. - // - // For `impl Trait` in the types of statics, constants, - // locals and type aliases. These capture all parent - // lifetimes, so they can use their identity subst. - GenericParamDefKind::Lifetime - if matches!( - origin, - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) - ) => - { - tcx.lifetimes.re_static.into() - } - _ => tcx.mk_param_from_def(param), - } + tcx.mk_param_from_def(param) } }); debug!("impl_trait_ty_to_ty: substs={:?}", substs); @@ -2955,6 +2968,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty)) } + #[instrument(level = "trace", skip(self, generate_err))] fn validate_late_bound_regions( &self, constrained_regions: FxHashSet<ty::BoundRegionKind>, @@ -2963,7 +2977,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) { for br in referenced_regions.difference(&constrained_regions) { let br_name = match *br { - ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) | ty::BrEnv => { + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) | ty::BrEnv => { "an anonymous lifetime".to_string() } ty::BrNamed(_, name) => format!("lifetime `{}`", name), @@ -2971,7 +2985,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut err = generate_err(&br_name); - if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) = *br { + if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) = *br { // The only way for an anonymous lifetime to wind up // in the return type but **also** be unconstrained is // if it only appears in "associated types" in the @@ -2996,7 +3010,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn compute_object_lifetime_bound( &self, span: Span, - existential_predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Option<ty::Region<'tcx>> // if None, use the default { let tcx = self.tcx(); diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 6a28bb16a..3e3544ce6 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -60,13 +60,10 @@ impl<'tcx> Bounds<'tcx> { { // If it could be sized, and is, add the `Sized` predicate. let sized_predicate = self.implicitly_sized.and_then(|span| { - tcx.lang_items().sized_trait().map(move |sized| { - let trait_ref = ty::Binder::dummy(ty::TraitRef { - def_id: sized, - substs: tcx.mk_substs_trait(param_ty, &[]), - }); - (trait_ref.without_const().to_predicate(tcx), span) - }) + // FIXME: use tcx.at(span).mk_trait_ref(LangItem::Sized) here? This may make no-core code harder to write. + let sized = tcx.lang_items().sized_trait()?; + let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(sized, [param_ty])); + Some((trait_ref.without_const().to_predicate(tcx), span)) }); let region_preds = self.region_bounds.iter().map(move |&(region_bound, span)| { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index b70ac0205..fc0ca6209 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,4 +1,5 @@ use crate::check::intrinsicck::InlineAsmCtxt; +use crate::errors::LinkageType; use super::compare_method::check_type_bounds; use super::compare_method::{compare_impl_method, compare_ty_impl}; @@ -6,10 +7,11 @@ use super::*; use rustc_attr as attr; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{ItemKind, Node, PathSegment}; +use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; @@ -19,13 +21,12 @@ use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{ - self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, -}; +use rustc_middle::ty::{self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -75,7 +76,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_simd(tcx, span, def_id); } - check_transparent(tcx, span, def); + check_transparent(tcx, def); check_packed(tcx, span, def); } @@ -83,7 +84,7 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - check_transparent(tcx, span, def); + check_transparent(tcx, def); check_union_fields(tcx, span, def_id); check_packed(tcx, span, def); } @@ -230,7 +231,9 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id()); let span = tcx.def_span(item.owner_id.def_id); - check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span); + if !tcx.features().impl_trait_projections { + check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span); + } if tcx.type_of(item.owner_id.def_id).references_error() { return; } @@ -239,6 +242,7 @@ fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { } check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin); } + /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result /// in "inheriting lifetimes". #[instrument(level = "debug", skip(tcx, span))] @@ -250,39 +254,11 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( let item = tcx.hir().expect_item(def_id); debug!(?item, ?span); - struct FoundParentLifetime; - struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics); - impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> { - type BreakTy = FoundParentLifetime; - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - debug!("FindParentLifetimeVisitor: r={:?}", r); - if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r { - if index < self.0.parent_count as u32 { - return ControlFlow::Break(FoundParentLifetime); - } else { - return ControlFlow::CONTINUE; - } - } - - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ConstKind::Unevaluated(..) = c.kind() { - // FIXME(#72219) We currently don't detect lifetimes within substs - // which would violate this check. Even though the particular substitution is not used - // within the const, this should still be fixed. - return ControlFlow::CONTINUE; - } - c.super_visit_with(self) - } - } - struct ProhibitOpaqueVisitor<'tcx> { tcx: TyCtxt<'tcx>, opaque_identity_ty: Ty<'tcx>, - generics: &'tcx ty::Generics, + parent_count: u32, + references_parent_regions: bool, selftys: Vec<(Span, Option<String>)>, } @@ -290,12 +266,25 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); + debug!(?t, "root_visit_ty"); if t == self.opaque_identity_ty { ControlFlow::CONTINUE } else { - t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics)) - .map_break(|FoundParentLifetime| t) + t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |region| { + if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *region + && index < self.parent_count + { + self.references_parent_regions= true; + } + }, + }); + if self.references_parent_regions { + ControlFlow::Break(t) + } else { + ControlFlow::CONTINUE + } } } } @@ -328,15 +317,20 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( if let ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), + in_trait, .. }) = item.kind { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + let opaque_identity_ty = if in_trait { + tcx.mk_projection(def_id.to_def_id(), substs) + } else { + tcx.mk_opaque(def_id.to_def_id(), substs) + }; let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx.mk_opaque( - def_id.to_def_id(), - InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), - ), - generics: tcx.generics_of(def_id), + opaque_identity_ty, + parent_count: tcx.generics_of(def_id).parent_count as u32, + references_parent_regions: false, tcx, selftys: vec![], }; @@ -344,10 +338,6 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( .explicit_item_bounds(def_id) .iter() .try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor)); - debug!( - "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}", - prohibit_opaque, visitor.opaque_identity_ty, visitor.generics - ); if let Some(ty) = prohibit_opaque.break_value() { visitor.visit_item(&item); @@ -358,15 +348,16 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( _ => unreachable!(), }; - let mut err = struct_span_err!( - tcx.sess, + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::impl_trait_projections, span, - E0760, - "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", - if is_async { "async fn" } else { "impl Trait" }, + &format!( + "`{}` return type cannot contain a projection or `Self` that references \ + lifetimes from a parent scope", + if is_async { "async fn" } else { "impl Trait" }, + ), ); - for (span, name) in visitor.selftys { err.span_suggestion( span, @@ -450,8 +441,8 @@ fn check_opaque_meets_bounds<'tcx>( let misc_cause = traits::ObligationCause::misc(span, hir_id); - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_ty) { - Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), + match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) { + Ok(()) => {} Err(ty_err) => { tcx.sess.delay_span_bug( span, @@ -463,15 +454,14 @@ fn check_opaque_meets_bounds<'tcx>( // Additionally require the hidden type to be well-formed with only the generics of the opaque type. // Defining use functions may have more bounds than the opaque type, which is ok, as long as the // hidden type is well formed even without those bounds. - let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into())).to_predicate(tcx); - ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into())); + ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate)); // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); } match origin { // Checked when type checking the function containing them. @@ -489,6 +479,36 @@ fn check_opaque_meets_bounds<'tcx>( let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); } +fn is_enum_of_nonnullable_ptr<'tcx>( + tcx: TyCtxt<'tcx>, + adt_def: AdtDef<'tcx>, + substs: SubstsRef<'tcx>, +) -> bool { + if adt_def.repr().inhibit_enum_layout_opt() { + return false; + } + + let [var_one, var_two] = &adt_def.variants().raw[..] else { + return false; + }; + let (([], [field]) | ([field], [])) = (&var_one.fields[..], &var_two.fields[..]) else { + return false; + }; + matches!(field.ty(tcx, substs).kind(), ty::FnPtr(..) | ty::Ref(..)) +} + +fn check_static_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { + if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() { + if match tcx.type_of(def_id).kind() { + ty::RawPtr(_) => false, + ty::Adt(adt_def, substs) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *substs), + _ => true, + } { + tcx.sess.emit_err(LinkageType { span: tcx.def_span(def_id) }); + } + } +} + fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { debug!( "check_item_type(it.def_id={:?}, it.name={})", @@ -501,16 +521,13 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { tcx.ensure().typeck(id.owner_id.def_id); maybe_check_static_with_link_section(tcx, id.owner_id.def_id); check_static_inhabited(tcx, id.owner_id.def_id); + check_static_linkage(tcx, id.owner_id.def_id); } DefKind::Const => { tcx.ensure().typeck(id.owner_id.def_id); } DefKind::Enum => { - let item = tcx.hir().item(id); - let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { - return; - }; - check_enum(tcx, &enum_definition.variants, item.owner_id.def_id); + check_enum(tcx, id.owner_id.def_id); } DefKind::Fn => {} // entirely within check_item_body DefKind::Impl => { @@ -642,6 +659,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { } hir::ForeignItemKind::Static(..) => { check_static_inhabited(tcx, def_id); + check_static_linkage(tcx, def_id); } _ => {} } @@ -659,7 +677,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id()); + let _ = OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id()); } pub(super) fn check_specialization_validity<'tcx>( @@ -1026,7 +1044,7 @@ pub(super) fn check_packed_inner( None } -pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) { +pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { if !adt.repr().transparent() { return; } @@ -1035,14 +1053,14 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD feature_err( &tcx.sess.parse_sess, sym::transparent_unions, - sp, + tcx.def_span(adt.did()), "transparent unions are unstable", ) .emit(); } if adt.variants().len() != 1 { - bad_variant_count(tcx, adt, sp, adt.did()); + bad_variant_count(tcx, adt, tcx.def_span(adt.did()), adt.did()); if adt.variants().is_empty() { // Don't bother checking the fields. No variants (and thus no fields) exist. return; @@ -1103,7 +1121,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); let non_zst_count = non_zst_fields.clone().count(); if non_zst_count >= 2 { - bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); + bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did())); } let incompatible_zst_fields = field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); @@ -1143,12 +1161,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD } #[allow(trivial_numeric_casts)] -fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) { +fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); - let sp = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - if vs.is_empty() { + if def.variants().is_empty() { if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() { struct_span_err!( tcx.sess, @@ -1156,7 +1173,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L E0084, "unsupported representation for zero-variant enum" ) - .span_label(sp, "zero-variant enum") + .span_label(tcx.def_span(def_id), "zero-variant enum") .emit(); } } @@ -1167,88 +1184,96 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L feature_err( &tcx.sess.parse_sess, sym::repr128, - sp, + tcx.def_span(def_id), "repr with 128-bit type is unstable", ) .emit(); } } - for v in vs { - if let Some(ref e) = v.disr_expr { - tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); + for v in def.variants() { + if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr { + tcx.ensure().typeck(discr_def_id.expect_local()); } } - if tcx.adt_def(def_id).repr().int.is_none() { - let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); + if def.repr().int.is_none() { + let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const)); + let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_)); - let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); - let has_non_units = vs.iter().any(|var| !is_unit(var)); - let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); - let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); + let has_non_units = def.variants().iter().any(|var| !is_unit(var)); + let disr_units = def.variants().iter().any(|var| is_unit(&var) && has_disr(&var)); + let disr_non_unit = def.variants().iter().any(|var| !is_unit(&var) && has_disr(&var)); if disr_non_unit || (disr_units && has_non_units) { - let mut err = - struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); + let mut err = struct_span_err!( + tcx.sess, + tcx.def_span(def_id), + E0732, + "`#[repr(inttype)]` must be specified" + ); err.emit(); } } - detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); - - check_transparent(tcx, sp, def); + detect_discriminant_duplicate(tcx, def); + check_transparent(tcx, def); } /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal -fn detect_discriminant_duplicate<'tcx>( - tcx: TyCtxt<'tcx>, - mut discrs: Vec<(VariantIdx, Discr<'tcx>)>, - vs: &'tcx [hir::Variant<'tcx>], - self_span: Span, -) { +fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. // Here `idx` refers to the order of which the discriminant appears, and its index in `vs` - let report = |dis: Discr<'tcx>, idx: usize, err: &mut Diagnostic| { - let var = &vs[idx]; // HIR for the duplicate discriminant - let (span, display_discr) = match var.disr_expr { - Some(ref expr) => { + let report = |dis: Discr<'tcx>, idx, err: &mut Diagnostic| { + let var = adt.variant(idx); // HIR for the duplicate discriminant + let (span, display_discr) = match var.discr { + ty::VariantDiscr::Explicit(discr_def_id) => { // In the case the discriminant is both a duplicate and overflowed, let the user know - if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind + if let hir::Node::AnonConst(expr) = tcx.hir().get_by_def_id(discr_def_id.expect_local()) + && let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node && *lit_value != dis.val { - (tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)")) - // Otherwise, format the value as-is + (tcx.def_span(discr_def_id), format!("`{dis}` (overflowed from `{lit_value}`)")) } else { - (tcx.hir().span(expr.hir_id), format!("`{dis}`")) + // Otherwise, format the value as-is + (tcx.def_span(discr_def_id), format!("`{dis}`")) } } - None => { + // This should not happen. + ty::VariantDiscr::Relative(0) => (tcx.def_span(var.def_id), format!("`{dis}`")), + ty::VariantDiscr::Relative(distance_to_explicit) => { // At this point we know this discriminant is a duplicate, and was not explicitly // assigned by the user. Here we iterate backwards to fetch the HIR for the last // explicitly assigned discriminant, and letting the user know that this was the // increment startpoint, and how many steps from there leading to the duplicate - if let Some((n, hir::Variant { span, ident, .. })) = - vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some()) + if let Some(explicit_idx) = + idx.as_u32().checked_sub(distance_to_explicit).map(VariantIdx::from_u32) { - let ve_ident = var.ident; - let n = n + 1; - let sp = if n > 1 { "variants" } else { "variant" }; + let explicit_variant = adt.variant(explicit_idx); + let ve_ident = var.name; + let ex_ident = explicit_variant.name; + let sp = if distance_to_explicit > 1 { "variants" } else { "variant" }; err.span_label( - *span, - format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"), + tcx.def_span(explicit_variant.def_id), + format!( + "discriminant for `{ve_ident}` incremented from this startpoint \ + (`{ex_ident}` + {distance_to_explicit} {sp} later \ + => `{ve_ident}` = {dis})" + ), ); } - (vs[idx].span, format!("`{dis}`")) + (tcx.def_span(var.def_id), format!("`{dis}`")) } }; err.span_label(span, format!("{display_discr} assigned here")); }; + let mut discrs = adt.discriminants(tcx).collect::<Vec<_>>(); + // Here we loop through the discriminants, comparing each discriminant to another. // When a duplicate is detected, we instantiate an error and point to both // initial and duplicate value. The duplicate discriminant is then discarded by swapping @@ -1257,29 +1282,29 @@ fn detect_discriminant_duplicate<'tcx>( // style as we are mutating `discrs` on the fly). let mut i = 0; while i < discrs.len() { - let hir_var_i_idx = discrs[i].0.index(); + let var_i_idx = discrs[i].0; let mut error: Option<DiagnosticBuilder<'_, _>> = None; let mut o = i + 1; while o < discrs.len() { - let hir_var_o_idx = discrs[o].0.index(); + let var_o_idx = discrs[o].0; if discrs[i].1.val == discrs[o].1.val { let err = error.get_or_insert_with(|| { let mut ret = struct_span_err!( tcx.sess, - self_span, + tcx.def_span(adt.did()), E0081, "discriminant value `{}` assigned more than once", discrs[i].1, ); - report(discrs[i].1, hir_var_i_idx, &mut ret); + report(discrs[i].1, var_i_idx, &mut ret); ret }); - report(discrs[o].1, hir_var_o_idx, err); + report(discrs[o].1, var_o_idx, err); // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty discrs[o] = *discrs.last().unwrap(); diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 32f66b06f..db150ebf0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -1,7 +1,7 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, LocalDefId}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -9,13 +9,12 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ - self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + self, DefIdTree, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; @@ -50,11 +49,11 @@ pub(crate) fn compare_impl_method<'tcx>( return; } - if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) { + if let Err(_) = compare_number_of_generics(tcx, impl_m, trait_m, trait_item_span, false) { return; } - if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) { + if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m, false) { return; } @@ -68,8 +67,14 @@ pub(crate) fn compare_impl_method<'tcx>( return; } - if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) - { + if let Err(_) = compare_predicate_entailment( + tcx, + impl_m, + impl_m_span, + trait_m, + impl_trait_ref, + CheckImpliedWfMode::Check, + ) { return; } } @@ -143,10 +148,11 @@ pub(crate) fn compare_impl_method<'tcx>( #[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))] fn compare_predicate_entailment<'tcx>( tcx: TyCtxt<'tcx>, - impl_m: &AssocItem, + impl_m: &ty::AssocItem, impl_m_span: Span, - trait_m: &AssocItem, + trait_m: &ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, + check_implied_wf: CheckImpliedWfMode, ) -> Result<(), ErrorGuaranteed> { let trait_to_impl_substs = impl_trait_ref.substs; @@ -156,8 +162,7 @@ fn compare_predicate_entailment<'tcx>( // FIXME(@lcnr): remove that after removing `cause.body_id` from // obligations. let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); - // We sometimes modify the span further down. - let mut cause = ObligationCause::new( + let cause = ObligationCause::new( impl_m_span, impl_m_hir_id, ObligationCauseCode::CompareImplItemObligation { @@ -175,13 +180,11 @@ fn compare_predicate_entailment<'tcx>( impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs); debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs); - let impl_m_generics = tcx.generics_of(impl_m.def_id); - let trait_m_generics = tcx.generics_of(trait_m.def_id); let impl_m_predicates = tcx.predicates_of(impl_m.def_id); let trait_m_predicates = tcx.predicates_of(trait_m.def_id); // Check region bounds. - check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?; + check_region_bounds_on_impl_item(tcx, impl_m, trait_m, false)?; // Create obligations for each predicate declared by the impl // definition in the context of the trait's parameter @@ -220,14 +223,11 @@ fn compare_predicate_entailment<'tcx>( debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause, predicate); + let predicate = ocx.normalize(&normalize_cause, param_env, predicate); - ocx.register_obligations(obligations); let cause = ObligationCause::new( span, impl_m_hir_id, @@ -237,7 +237,7 @@ fn compare_predicate_entailment<'tcx>( kind: impl_m.kind, }, ); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } // We now need to check that the signature of the impl method is @@ -256,17 +256,17 @@ fn compare_predicate_entailment<'tcx>( // Compute placeholder form of impl and trait method tys. let tcx = infcx.tcx; - let mut wf_tys = FxHashSet::default(); + let mut wf_tys = FxIndexSet::default(); - let impl_sig = infcx.replace_bound_vars_with_fresh_vars( + let unnormalized_impl_sig = infcx.replace_bound_vars_with_fresh_vars( impl_m_span, infer::HigherRankedType, tcx.fn_sig(impl_m.def_id), ); + let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig)); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + let impl_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty); debug!("compare_impl_method: impl_fty={:?}", impl_fty); let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); @@ -276,7 +276,7 @@ fn compare_predicate_entailment<'tcx>( // we have to do this before normalization, since the normalized ty may // not contain the input parameters. See issue #87748. wf_tys.extend(trait_sig.inputs_and_output.iter()); - let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); + let trait_sig = ocx.normalize(&norm_cause, param_env, trait_sig); // We also have to add the normalized trait signature // as we don't normalize during implied bounds computation. wf_tys.extend(trait_sig.inputs_and_output.iter()); @@ -290,147 +290,126 @@ fn compare_predicate_entailment<'tcx>( // type would be more appropriate. In other places we have a `Vec<Span>` // corresponding to their `Vec<Predicate>`, but we don't have that here. // Fixing this would improve the output of test `issue-83765.rs`. - let mut result = infcx - .at(&cause, param_env) - .sup(trait_fty, impl_fty) - .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); - - // HACK(RPITIT): #101614. When we are trying to infer the hidden types for - // RPITITs, we need to equate the output tys instead of just subtyping. If - // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes - // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets - // fixed up to `ReEmpty`, and which is certainly not what we want. - if trait_fty.has_infer_types() { - result = result.and_then(|()| { - infcx - .at(&cause, param_env) - .eq(trait_sig.output(), impl_sig.output()) - .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)) - }); - } + let result = ocx.sup(&cause, param_env, trait_fty, impl_fty); if let Err(terr) = result { - debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - - let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); - - cause.span = impl_err_span; - - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible type for trait", - trait_m.name - ); - match &terr { - TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => - { - let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {ty}"), - }; - - // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the - // span points only at the type `Box<Self`>, but we want to cover the whole - // argument pattern and type. - let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, body) => tcx - .hir() - .body_param_names(body) - .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) - .next() - .unwrap_or(impl_err_span), - _ => bug!("{:?} is not a method", impl_m), - }; - - diag.span_suggestion( - span, - "change the self-receiver type to match the trait", - sugg, - Applicability::MachineApplicable, - ); - } - TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { - if trait_sig.inputs().len() == *i { - // Suggestion to change output type. We do not suggest in `async` functions - // to avoid complex logic or incorrect output. - match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) - if sig.header.asyncness == hir::IsAsync::NotAsync => - { - let msg = "change the output type to match the trait"; - let ap = Applicability::MachineApplicable; - match sig.decl.output { - hir::FnRetTy::DefaultReturn(sp) => { - let sugg = format!("-> {} ", trait_sig.output()); - diag.span_suggestion_verbose(sp, msg, sugg, ap); - } - hir::FnRetTy::Return(hir_ty) => { - let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); - } - }; - } - _ => {} - }; - } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( - impl_err_span, - "change the parameter type to match the trait", - trait_ty, - Applicability::MachineApplicable, - ); - } - } - _ => {} - } + debug!(?terr, "sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - infcx.err_ctxt().note_type_err( - &mut diag, - &cause, - trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), + let emitted = report_trait_method_mismatch( + &infcx, + cause, terr, - false, - false, + (trait_m, trait_fty), + (impl_m, impl_fty), + trait_sig, + impl_trait_ref, ); + return Err(emitted); + } - return Err(diag.emit()); + if check_implied_wf == CheckImpliedWfMode::Check { + // We need to check that the impl's args are well-formed given + // the hybrid param-env (impl + trait method where-clauses). + ocx.register_obligation(traits::Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())), + )); } + let emit_implied_wf_lint = || { + infcx.tcx.struct_span_lint_hir( + rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, + impl_m_hir_id, + infcx.tcx.def_span(impl_m.def_id), + "impl method assumes more implied bounds than the corresponding trait method", + |lint| lint, + ); + }; // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); - return Err(reported); + match check_implied_wf { + CheckImpliedWfMode::Check => { + return compare_predicate_entailment( + tcx, + impl_m, + impl_m_span, + trait_m, + impl_trait_ref, + CheckImpliedWfMode::Skip, + ) + .map(|()| { + // If the skip-mode was successful, emit a lint. + emit_implied_wf_lint(); + }); + } + CheckImpliedWfMode::Skip => { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + return Err(reported); + } + } } // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_environment = OutlivesEnvironment::with_bounds( + let outlives_env = OutlivesEnvironment::with_bounds( param_env, Some(infcx), - infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()), ); - infcx.check_region_obligations_and_report_errors( - impl_m.def_id.expect_local(), - &outlives_environment, + infcx.process_registered_region_obligations( + outlives_env.region_bound_pairs(), + outlives_env.param_env, ); + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + // FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT + // becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors` + match check_implied_wf { + CheckImpliedWfMode::Check => { + return compare_predicate_entailment( + tcx, + impl_m, + impl_m_span, + trait_m, + impl_trait_ref, + CheckImpliedWfMode::Skip, + ) + .map(|()| { + // If the skip-mode was successful, emit a lint. + emit_implied_wf_lint(); + }); + } + CheckImpliedWfMode::Skip => { + if infcx.tainted_by_errors().is_none() { + infcx.err_ctxt().report_region_errors(impl_m.def_id.expect_local(), &errors); + } + return Err(tcx + .sess + .delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted")); + } + } + } Ok(()) } +#[derive(Debug, PartialEq, Eq)] +enum CheckImpliedWfMode { + /// Checks implied well-formedness of the impl method. If it fails, we will + /// re-check with `Skip`, and emit a lint if it succeeds. + Check, + /// Skips checking implied well-formedness of the impl method, but will emit + /// a lint if the `compare_predicate_entailment` succeeded. This means that + /// the reason that we had failed earlier during `Check` was due to the impl + /// having stronger requirements than the trait. + Skip, +} + +#[instrument(skip(tcx), level = "debug", ret)] pub fn collect_trait_impl_trait_tys<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -440,6 +419,11 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap(); let param_env = tcx.param_env(def_id); + // First, check a few of the same thing as `compare_impl_method`, just so we don't ICE during substitutions later. + compare_number_of_generics(tcx, impl_m, trait_m, tcx.hir().span_if_local(impl_m.def_id), true)?; + compare_generic_param_kinds(tcx, impl_m, trait_m, true)?; + check_region_bounds_on_impl_item(tcx, impl_m, trait_m, true)?; + let trait_to_impl_substs = impl_trait_ref.substs; let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); @@ -464,9 +448,10 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let infcx = &tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(infcx); + // Normalize the impl signature with fresh variables for lifetime inference. let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); let impl_sig = ocx.normalize( - norm_cause.clone(), + &norm_cause, param_env, infcx.replace_bound_vars_with_fresh_vars( return_span, @@ -476,6 +461,10 @@ pub fn collect_trait_impl_trait_tys<'tcx>( ); let impl_return_ty = impl_sig.output(); + // Normalize the trait signature with liberated bound vars, passing it through + // the ImplTraitInTraitCollector, which gathers all of the RPITITs and replaces + // them with inference variables. + // We will use these inference variables to collect the hidden types of RPITITs. let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); let unnormalized_trait_sig = tcx .liberate_late_bound_regions( @@ -483,17 +472,15 @@ pub fn collect_trait_impl_trait_tys<'tcx>( tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), ) .fold_with(&mut collector); - let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig); + let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig); let trait_return_ty = trait_sig.output(); - let wf_tys = FxHashSet::from_iter( + let wf_tys = FxIndexSet::from_iter( unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()), ); - match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) { - Ok(infer::InferOk { value: (), obligations }) => { - ocx.register_obligations(obligations); - } + match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) { + Ok(()) => {} Err(terr) => { let mut diag = struct_span_err!( tcx.sess, @@ -521,23 +508,32 @@ pub fn collect_trait_impl_trait_tys<'tcx>( } } + debug!(?trait_sig, ?impl_sig, "equating function signatures"); + + let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); + let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + // Unify the whole function signature. We need to do this to fully infer // the lifetimes of the return type, but do this after unifying just the // return types, since we want to avoid duplicating errors from // `compare_predicate_entailment`. - match infcx - .at(&cause, param_env) - .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig))) - { - Ok(infer::InferOk { value: (), obligations }) => { - ocx.register_obligations(obligations); - } + match ocx.eq(&cause, param_env, trait_fty, impl_fty) { + Ok(()) => {} Err(terr) => { - let guar = tcx.sess.delay_span_bug( - return_span, - format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + // This function gets called during `compare_predicate_entailment` when normalizing a + // signature that contains RPITIT. When the method signatures don't match, we have to + // emit an error now because `compare_predicate_entailment` will not report the error + // when normalization fails. + let emitted = report_trait_method_mismatch( + infcx, + cause, + terr, + (trait_m, trait_fty), + (impl_m, impl_fty), + trait_sig, + impl_trait_ref, ); - return Err(guar); + return Err(emitted); } } @@ -545,7 +541,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>( // RPITs. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); return Err(reported); } @@ -597,7 +593,13 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let num_trait_substs = trait_to_impl_substs.len(); let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len(); let ty = tcx.fold_regions(ty, |region, _| { - let (ty::ReFree(_) | ty::ReEarlyBound(_)) = region.kind() else { return region; }; + match region.kind() { + // Remap all free regions, which correspond to late-bound regions in the function. + ty::ReFree(_) => {} + // Remap early-bound regions as long as they don't come from the `impl` itself. + ty::ReEarlyBound(ebr) if tcx.parent(ebr.def_id) != impl_m.container_id(tcx) => {} + _ => return region, + } let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind()) else { tcx @@ -618,11 +620,11 @@ pub fn collect_trait_impl_trait_tys<'tcx>( collected_tys.insert(def_id, ty); } Err(err) => { - tcx.sess.delay_span_bug( + let reported = tcx.sess.delay_span_bug( return_span, format!("could not fully resolve: {ty} => {err:?}"), ); - collected_tys.insert(def_id, tcx.ty_error()); + collected_tys.insert(def_id, tcx.ty_error_with_guaranteed(reported)); } } } @@ -675,12 +677,13 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { for (pred, pred_span) in self.tcx().bound_explicit_item_bounds(proj.item_def_id).subst_iter_copied(self.tcx(), proj.substs) { let pred = pred.fold_with(self); let pred = self.ocx.normalize( - ObligationCause::misc(self.span, self.body_id), + &ObligationCause::misc(self.span, self.body_id), self.param_env, pred, ); self.ocx.register_obligation(traits::Obligation::new( + self.tcx(), ObligationCause::new( self.span, self.body_id, @@ -697,16 +700,121 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { } } +fn report_trait_method_mismatch<'tcx>( + infcx: &InferCtxt<'tcx>, + mut cause: ObligationCause<'tcx>, + terr: TypeError<'tcx>, + (trait_m, trait_fty): (&ty::AssocItem, Ty<'tcx>), + (impl_m, impl_fty): (&ty::AssocItem, Ty<'tcx>), + trait_sig: ty::FnSig<'tcx>, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> ErrorGuaranteed { + let tcx = infcx.tcx; + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); + + let mut diag = struct_span_err!( + tcx.sess, + impl_err_span, + E0053, + "method `{}` has an incompatible type for trait", + trait_m.name + ); + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {ty}"), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the + // span points only at the type `Box<Self`>, but we want to cover the whole + // argument pattern and type. + let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, _) if !sig.header.asyncness.is_async() => { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { + diag.span_suggestion( + impl_err_span, + "change the parameter type to match the trait", + trait_ty, + Applicability::MachineApplicable, + ); + } + } + _ => {} + } + + cause.span = impl_err_span; + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_err_span.map(|sp| (sp, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_fty.into(), + found: impl_fty.into(), + })), + terr, + false, + false, + ); + + return diag.emit(); +} + fn check_region_bounds_on_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_m: &ty::AssocItem, trait_m: &ty::AssocItem, - trait_generics: &ty::Generics, - impl_generics: &ty::Generics, + delay: bool, ) -> Result<(), ErrorGuaranteed> { - let trait_params = trait_generics.own_counts().lifetimes; + let impl_generics = tcx.generics_of(impl_m.def_id); let impl_params = impl_generics.own_counts().lifetimes; + let trait_generics = tcx.generics_of(trait_m.def_id); + let trait_params = trait_generics.own_counts().lifetimes; + debug!( "check_region_bounds_on_impl_item: \ trait_generics={:?} \ @@ -729,23 +837,56 @@ fn check_region_bounds_on_impl_item<'tcx>( .get_generics(impl_m.def_id.expect_local()) .expect("expected impl item to have generics or else we can't compare them") .span; - let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() { - Some( - tcx.hir() - .get_generics(local_def_id) - .expect("expected trait item to have generics or else we can't compare them") - .span, - ) - } else { - None - }; - let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { - span, - item_kind: assoc_item_kind_str(impl_m), - ident: impl_m.ident(tcx), - generics_span, - }); + let mut generics_span = None; + let mut bounds_span = vec![]; + let mut where_span = None; + if let Some(trait_node) = tcx.hir().get_if_local(trait_m.def_id) + && let Some(trait_generics) = trait_node.generics() + { + generics_span = Some(trait_generics.span); + // FIXME: we could potentially look at the impl's bounds to not point at bounds that + // *are* present in the impl. + for p in trait_generics.predicates { + if let hir::WherePredicate::BoundPredicate(pred) = p { + for b in pred.bounds { + if let hir::GenericBound::Outlives(lt) = b { + bounds_span.push(lt.ident.span); + } + } + } + } + if let Some(impl_node) = tcx.hir().get_if_local(impl_m.def_id) + && let Some(impl_generics) = impl_node.generics() + { + let mut impl_bounds = 0; + for p in impl_generics.predicates { + if let hir::WherePredicate::BoundPredicate(pred) = p { + for b in pred.bounds { + if let hir::GenericBound::Outlives(_) = b { + impl_bounds += 1; + } + } + } + } + if impl_bounds == bounds_span.len() { + bounds_span = vec![]; + } else if impl_generics.has_where_clause_predicates { + where_span = Some(impl_generics.where_clause_span); + } + } + } + let reported = tcx + .sess + .create_err(LifetimesOrBoundsMismatchOnTrait { + span, + item_kind: assoc_item_kind_str(impl_m), + ident: impl_m.ident(tcx), + generics_span, + bounds_span, + where_span, + }) + .emit_unless(delay); return Err(reported); } @@ -891,9 +1032,9 @@ fn compare_self_type<'tcx>( fn compare_number_of_generics<'tcx>( tcx: TyCtxt<'tcx>, impl_: &ty::AssocItem, - _impl_span: Span, trait_: &ty::AssocItem, trait_span: Option<Span>, + delay: bool, ) -> Result<(), ErrorGuaranteed> { let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts(); let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts(); @@ -1023,7 +1164,7 @@ fn compare_number_of_generics<'tcx>( err.span_label(*span, "`impl Trait` introduces an implicit type parameter"); } - let reported = err.emit(); + let reported = err.emit_unless(delay); err_occurred = Some(reported); } } @@ -1275,6 +1416,7 @@ fn compare_generic_param_kinds<'tcx>( tcx: TyCtxt<'tcx>, impl_item: &ty::AssocItem, trait_item: &ty::AssocItem, + delay: bool, ) -> Result<(), ErrorGuaranteed> { assert_eq!(impl_item.kind, trait_item.kind); @@ -1332,7 +1474,7 @@ fn compare_generic_param_kinds<'tcx>( err.span_label(impl_header_span, ""); err.span_label(param_impl_span, make_param_message("found", param_impl)); - let reported = err.emit(); + let reported = err.emit_unless(delay); return Err(reported); } } @@ -1381,18 +1523,15 @@ pub(crate) fn raw_compare_const_impl<'tcx>( ); // There is no "body" here, so just pass dummy id. - let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); debug!("compare_const_impl: impl_ty={:?}", impl_ty); - let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty); + let trait_ty = ocx.normalize(&cause, param_env, trait_ty); debug!("compare_const_impl: trait_ty={:?}", trait_ty); - let err = infcx - .at(&cause, param_env) - .sup(trait_ty, impl_ty) - .map(|ok| ocx.register_infer_ok_obligations(ok)); + let err = ocx.sup(&cause, param_env, trait_ty, impl_ty); if let Err(terr) = err { debug!( @@ -1441,7 +1580,7 @@ pub(crate) fn raw_compare_const_impl<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false)); + return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None)); } // FIXME return `ErrorReported` if region obligations error? @@ -1461,9 +1600,9 @@ pub(crate) fn compare_ty_impl<'tcx>( debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref); let _: Result<(), ErrorGuaranteed> = (|| { - compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?; + compare_number_of_generics(tcx, impl_ty, trait_ty, trait_item_span, false)?; - compare_generic_param_kinds(tcx, impl_ty, trait_ty)?; + compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?; let sp = tcx.def_span(impl_ty.def_id); compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?; @@ -1485,18 +1624,10 @@ fn compare_type_predicate_entailment<'tcx>( let trait_to_impl_substs = impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs); - let impl_ty_generics = tcx.generics_of(impl_ty.def_id); - let trait_ty_generics = tcx.generics_of(trait_ty.def_id); let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id); let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id); - check_region_bounds_on_impl_item( - tcx, - impl_ty, - trait_ty, - &trait_ty_generics, - &impl_ty_generics, - )?; + check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?; let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs); @@ -1533,14 +1664,11 @@ fn compare_type_predicate_entailment<'tcx>( debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); - assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) { let cause = ObligationCause::misc(span, impl_ty_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, cause, predicate); + let predicate = ocx.normalize(&cause, param_env, predicate); let cause = ObligationCause::new( span, @@ -1551,15 +1679,14 @@ fn compare_type_predicate_entailment<'tcx>( kind: impl_ty.kind, }, ); - ocx.register_obligations(obligations); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); return Err(reported); } @@ -1665,13 +1792,10 @@ pub fn check_type_bounds<'tcx>( GenericParamDefKind::Const { .. } => { let bound_var = ty::BoundVariableKind::Const; bound_vars.push(bound_var); - tcx.mk_const(ty::ConstS { - ty: tcx.type_of(param.def_id), - kind: ty::ConstKind::Bound( - ty::INNERMOST, - ty::BoundVar::from_usize(bound_vars.len() - 1), - ), - }) + tcx.mk_const( + ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(bound_vars.len() - 1)), + tcx.type_of(param.def_id), + ) .into() } }); @@ -1737,7 +1861,6 @@ pub fn check_type_bounds<'tcx>( let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local()); - let mut selcx = traits::SelectionContext::new(&infcx); let normalize_cause = ObligationCause::new( impl_ty_span, impl_ty_hir_id, @@ -1760,29 +1883,24 @@ pub fn check_type_bounds<'tcx>( .subst_iter_copied(tcx, rebased_substs) .map(|(concrete_ty_bound, span)| { debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound) + traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) }) .collect(); debug!("check_type_bounds: item_bounds={:?}", obligations); for mut obligation in util::elaborate_obligations(tcx, obligations) { - let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( - &mut selcx, - normalize_param_env, - normalize_cause.clone(), - obligation.predicate, - ); + let normalized_predicate = + ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate); debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); obligation.predicate = normalized_predicate; - ocx.register_obligations(obligations); ocx.register_obligation(obligation); } // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); return Err(reported); } diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index a74016e22..d6e3ddb0a 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -183,19 +183,27 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let predicate = predicate.kind(); let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { - (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() - } - (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { - relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() - } + ( + ty::PredicateKind::Clause(ty::Clause::Trait(a)), + ty::PredicateKind::Clause(ty::Clause::Trait(b)), + ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), + ( + ty::PredicateKind::Clause(ty::Clause::Projection(a)), + ty::PredicateKind::Clause(ty::Clause::Projection(b)), + ) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(), ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), ( - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty_a, + lt_a, + ))), + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty_b, + lt_b, + ))), ) => { relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok() && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok() @@ -225,9 +233,10 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( result } -// This is an implementation of the TypeRelation trait with the -// aim of simply comparing for equality (without side-effects). -// It is not intended to be used anywhere else other than here. +/// This is an implementation of the [`TypeRelation`] trait with the +/// aim of simply comparing for equality (without side-effects). +/// +/// It is not intended to be used anywhere else other than here. pub(crate) struct SimpleEqRelation<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -244,6 +253,10 @@ impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { self.tcx } + fn intercrate(&self) -> bool { + false + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { self.param_env } @@ -256,6 +269,10 @@ impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { true } + fn mark_ambiguous(&mut self) { + bug!() + } + fn relate_with_variance<T: Relate<'tcx>>( &mut self, _: ty::Variance, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 609095c9c..69e54b41d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -134,15 +134,18 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds( - [ty::BoundVariableKind::Region(ty::BrAnon(0)), ty::BoundVariableKind::Region(ty::BrEnv)] - .iter() - .copied(), + [ + ty::BoundVariableKind::Region(ty::BrAnon(0, None)), + ty::BoundVariableKind::Region(ty::BrEnv), + ] + .iter() + .copied(), ); let mk_va_list_ty = |mutbl| { tcx.lang_items().va_list().map(|did| { let region = tcx.mk_region(ty::ReLateBound( ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }, + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0, None) }, )); let env_region = tcx.mk_region(ty::ReLateBound( ty::INNERMOST, @@ -364,7 +367,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ); let discriminant_def_id = assoc_items[0]; - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0, None) }; ( 1, vec![ @@ -418,7 +422,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), sym::raw_eq => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0, None) }; let param_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)); (1, vec![param_ty; 2], tcx.types.bool) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 2e7b10257..29255472a 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -159,7 +159,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { // the consumer's responsibility to ensure all bytes that have been read // have defined values. if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) - && alloc.inner().provenance().len() != 0 + && alloc.inner().provenance().ptrs().len() != 0 { let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ @@ -309,7 +309,7 @@ fn bounds_from_generic_predicates<'tcx>( debug!("predicate {:?}", predicate); let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); if Some(def_id) != tcx.lang_items().sized_trait() { @@ -318,7 +318,7 @@ fn bounds_from_generic_predicates<'tcx>( entry.push(trait_predicate.def_id()); } } - ty::PredicateKind::Projection(projection_pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(projection_pred)) => { projections.push(bound_predicate.rebind(projection_pred)); } _ => {} diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index ff32329e4..b315ebad4 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -241,17 +241,46 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // scopes, meaning that temporaries cannot outlive them. // This ensures fixed size stacks. hir::ExprKind::Binary( - source_map::Spanned { node: hir::BinOpKind::And, .. }, - _, - ref r, - ) - | hir::ExprKind::Binary( - source_map::Spanned { node: hir::BinOpKind::Or, .. }, - _, + source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + ref l, ref r, ) => { - // For shortcircuiting operators, mark the RHS as a terminating - // scope since it only executes conditionally. + // expr is a short circuiting operator (|| or &&). As its + // functionality can't be overridden by traits, it always + // processes bool sub-expressions. bools are Copy and thus we + // can drop any temporaries in evaluation (read) order + // (with the exception of potentially failing let expressions). + // We achieve this by enclosing the operands in a terminating + // scope, both the LHS and the RHS. + + // We optimize this a little in the presence of chains. + // Chains like a && b && c get lowered to AND(AND(a, b), c). + // In here, b and c are RHS, while a is the only LHS operand in + // that chain. This holds true for longer chains as well: the + // leading operand is always the only LHS operand that is not a + // binop itself. Putting a binop like AND(a, b) into a + // terminating scope is not useful, thus we only put the LHS + // into a terminating scope if it is not a binop. + + let terminate_lhs = match l.kind { + // let expressions can create temporaries that live on + hir::ExprKind::Let(_) => false, + // binops already drop their temporaries, so there is no + // need to put them into a terminating scope. + // This is purely an optimization to reduce the number of + // terminating scopes. + hir::ExprKind::Binary( + source_map::Spanned { + node: hir::BinOpKind::And | hir::BinOpKind::Or, .. + }, + .., + ) => false, + // otherwise: mark it as terminating + _ => true, + }; + if terminate_lhs { + terminating(l.hir_id.local_id); + } // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries // should live beyond the immediate expression diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index a23575004..b065ace6b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1,7 +1,7 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; use hir::def::DefKind; use rustc_ast as ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -14,13 +14,14 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::query::Providers; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitor, + self, AdtKind, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitor, }; use rustc_middle::ty::{GenericArgKind, InternalSubsts}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::spec::abi::Abi; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; @@ -52,12 +53,14 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.ocx.infcx.tcx } + // Convenience function to normalize during wfcheck. This performs + // `ObligationCtxt::normalize`, but provides a nice `ObligationCauseCode`. fn normalize<T>(&self, span: Span, loc: Option<WellFormedLoc>, value: T) -> T where T: TypeFoldable<'tcx>, { self.ocx.normalize( - ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)), + &ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)), self.param_env, value, ) @@ -74,9 +77,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { // for a type to be WF, we do not need to check if const trait predicates satisfy. let param_env = self.param_env.without_const(); self.ocx.register_obligation(traits::Obligation::new( + self.tcx(), cause, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), )); } } @@ -104,7 +108,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( f(&mut wfcx); let errors = wfcx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); return; } @@ -116,7 +120,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( } fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { - let node = tcx.hir().expect_owner(def_id); + let node = tcx.hir().owner(def_id); match node { hir::OwnerNode::Crate(_) => {} hir::OwnerNode::Item(item) => check_item(tcx, item), @@ -218,19 +222,16 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { hir::ItemKind::Const(ty, ..) => { check_item_type(tcx, def_id, ty.span, false); } - hir::ItemKind::Struct(ref struct_def, ref ast_generics) => { - check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); - + hir::ItemKind::Struct(_, ref ast_generics) => { + check_type_defn(tcx, item, false); check_variances_for_type_defn(tcx, item, ast_generics); } - hir::ItemKind::Union(ref struct_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); - + hir::ItemKind::Union(_, ref ast_generics) => { + check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, ast_generics); } - hir::ItemKind::Enum(ref enum_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |wfcx| wfcx.enum_variants(enum_def)); - + hir::ItemKind::Enum(_, ref ast_generics) => { + check_type_defn(tcx, item, true); check_variances_for_type_defn(tcx, item, ast_generics); } hir::ItemKind::Trait(..) => { @@ -414,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe .iter() .copied() .collect::<Vec<_>>(), - &FxHashSet::default(), + &FxIndexSet::default(), gat_def_id.def_id, gat_generics, ) @@ -463,12 +464,16 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe let mut unsatisfied_bounds: Vec<_> = required_bounds .into_iter() .filter(|clause| match clause.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => { - !region_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) - } - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { - !ty_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( + a, + b, + ))) => { + !region_known_to_outlive(tcx, gat_hir, param_env, &FxIndexSet::default(), a, b) } + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + a, + b, + ))) => !ty_known_to_outlive(tcx, gat_hir, param_env, &FxIndexSet::default(), a, b), _ => bug!("Unexpected PredicateKind"), }) .map(|clause| clause.to_string()) @@ -549,7 +554,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( param_env: ty::ParamEnv<'tcx>, item_hir: hir::HirId, to_check: T, - wf_tys: &FxHashSet<Ty<'tcx>>, + wf_tys: &FxIndexSet<Ty<'tcx>>, gat_def_id: LocalDefId, gat_generics: &'tcx ty::Generics, ) -> Option<FxHashSet<ty::Predicate<'tcx>>> { @@ -600,8 +605,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( })); // The predicate we expect to see. (In our example, // `Self: 'me`.) - let clause = - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param)); + let clause = ty::PredicateKind::Clause(ty::Clause::TypeOutlives( + ty::OutlivesPredicate(ty_param, region_param), + )); let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); bounds.insert(clause); } @@ -637,9 +643,8 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( name: region_b_param.name, })); // The predicate we expect to see. - let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( - region_a_param, - region_b_param, + let clause = ty::PredicateKind::Clause(ty::Clause::RegionOutlives( + ty::OutlivesPredicate(region_a_param, region_b_param), )); let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); bounds.insert(clause); @@ -656,7 +661,7 @@ fn ty_known_to_outlive<'tcx>( tcx: TyCtxt<'tcx>, id: hir::HirId, param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxHashSet<Ty<'tcx>>, + wf_tys: &FxIndexSet<Ty<'tcx>>, ty: Ty<'tcx>, region: ty::Region<'tcx>, ) -> bool { @@ -673,7 +678,7 @@ fn region_known_to_outlive<'tcx>( tcx: TyCtxt<'tcx>, id: hir::HirId, param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxHashSet<Ty<'tcx>>, + wf_tys: &FxIndexSet<Ty<'tcx>>, region_a: ty::Region<'tcx>, region_b: ty::Region<'tcx>, ) -> bool { @@ -697,7 +702,7 @@ fn resolve_regions_with_wf_tys<'tcx>( tcx: TyCtxt<'tcx>, id: hir::HirId, param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxHashSet<Ty<'tcx>>, + wf_tys: &FxIndexSet<Ty<'tcx>>, add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because @@ -855,7 +860,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // Const parameters are well formed if their type is structural match. hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { - let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); + let ty = tcx.type_of(param.def_id); if tcx.features().adt_const_params { if let Some(non_structural_match_ty) = @@ -1037,27 +1042,25 @@ fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> { } /// In a type definition, we check that to ensure that the types of the fields are well-formed. -fn check_type_defn<'tcx, F>( - tcx: TyCtxt<'tcx>, - item: &hir::Item<'tcx>, - all_sized: bool, - mut lookup_fields: F, -) where - F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, -{ +fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: bool) { let _ = tcx.representability(item.owner_id.def_id); + let adt_def = tcx.adt_def(item.owner_id); enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { - let variants = lookup_fields(wfcx); - let packed = tcx.adt_def(item.owner_id).repr().packed(); + let variants = adt_def.variants(); + let packed = adt_def.repr().packed(); - for variant in &variants { + for variant in variants.iter() { // All field types must be well-formed. for field in &variant.fields { + let field_id = field.did.expect_local(); + let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id) + else { bug!() }; + let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did)); wfcx.register_wf_obligation( - field.span, - Some(WellFormedLoc::Ty(field.def_id)), - field.ty.into(), + hir_ty.span, + Some(WellFormedLoc::Ty(field_id)), + ty.into(), ) } @@ -1065,7 +1068,7 @@ fn check_type_defn<'tcx, F>( // intermediate types must be sized. let needs_drop_copy = || { packed && { - let ty = variant.fields.last().unwrap().ty; + let ty = tcx.type_of(variant.fields.last().unwrap().did); let ty = tcx.erase_regions(ty); if ty.needs_infer() { tcx.sess @@ -1084,39 +1087,43 @@ fn check_type_defn<'tcx, F>( variant.fields[..variant.fields.len() - unsized_len].iter().enumerate() { let last = idx == variant.fields.len() - 1; + let field_id = field.did.expect_local(); + let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id) + else { bug!() }; + let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did)); wfcx.register_bound( traits::ObligationCause::new( - field.span, + hir_ty.span, wfcx.body_id, traits::FieldSized { adt_kind: match item_adt_kind(&item.kind) { Some(i) => i, None => bug!(), }, - span: field.span, + span: hir_ty.span, last, }, ), wfcx.param_env, - field.ty, + ty, tcx.require_lang_item(LangItem::Sized, None), ); } // Explicit `enum` discriminant values must const-evaluate successfully. - if let Some(discr_def_id) = variant.explicit_discr { + if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr { let cause = traits::ObligationCause::new( tcx.def_span(discr_def_id), wfcx.body_id, traits::MiscObligation, ); wfcx.register_obligation(traits::Obligation::new( + tcx, cause, wfcx.param_env, ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable( - ty::Const::from_anon_const(tcx, discr_def_id), - )) - .to_predicate(tcx), + ty::Const::from_anon_const(tcx, discr_def_id.expect_local()), + )), )); } } @@ -1453,7 +1460,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id wfcx.body_id, traits::ItemObligation(def_id.to_def_id()), ); - traits::Obligation::new(cause, wfcx.param_env, pred) + traits::Obligation::new(tcx, cause, wfcx.param_env, pred) }); let predicates = predicates.0.instantiate_identity(tcx); @@ -1537,23 +1544,50 @@ fn check_fn_or_method<'tcx>( check_where_clauses(wfcx, span, def_id); check_return_position_impl_trait_in_trait_bounds( - tcx, wfcx, def_id, sig.output(), hir_decl.output.span(), ); + + if sig.abi == Abi::RustCall { + let span = tcx.def_span(def_id); + let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None; + let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 }); + // Check that the argument is a tuple + if let Some(ty) = inputs.next() { + wfcx.register_bound( + ObligationCause::new(span, wfcx.body_id, ObligationCauseCode::RustCall), + wfcx.param_env, + *ty, + tcx.require_lang_item(hir::LangItem::Tuple, Some(span)), + ); + } else { + tcx.sess.span_err( + hir_decl.inputs.last().map_or(span, |input| input.span), + "functions with the \"rust-call\" ABI must take a single non-self tuple argument", + ); + } + // No more inputs other than the `self` type and the tuple type + if inputs.next().is_some() { + tcx.sess.span_err( + hir_decl.inputs.last().map_or(span, |input| input.span), + "functions with the \"rust-call\" ABI must take a single non-self tuple argument", + ); + } + } } /// Basically `check_associated_type_bounds`, but separated for now and should be /// deduplicated when RPITITs get lowered into real associated items. +#[tracing::instrument(level = "trace", skip(wfcx))] fn check_return_position_impl_trait_in_trait_bounds<'tcx>( - tcx: TyCtxt<'tcx>, wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_def_id: LocalDefId, fn_output: Ty<'tcx>, span: Span, ) { + let tcx = wfcx.tcx(); if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) && assoc_item.container == ty::AssocItemContainer::TraitContainer { @@ -1563,8 +1597,10 @@ fn check_return_position_impl_trait_in_trait_bounds<'tcx>( && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id() { + let span = tcx.def_span(proj.item_def_id); let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id); let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let bound = ty::EarlyBinder(bound).subst(tcx, proj.substs); let normalized_bound = wfcx.normalize(span, None, bound); traits::wf::predicate_obligations( wfcx.infcx, @@ -1675,14 +1711,13 @@ fn receiver_is_valid<'tcx>( // `self: Self` is always valid. if can_eq_self(receiver_ty) { - if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) { + if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, receiver_ty) { infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); } return true; } - let mut autoderef = - Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty, span); + let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty); // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`. if arbitrary_self_types_enabled { @@ -1692,7 +1727,7 @@ fn receiver_is_valid<'tcx>( // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. autoderef.next(); - let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, None); + let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span)); // Keep dereferencing `receiver_ty` until we get to `self_ty`. loop { @@ -1705,9 +1740,7 @@ fn receiver_is_valid<'tcx>( if can_eq_self(potential_self_ty) { wfcx.register_obligations(autoderef.into_obligations()); - if let Err(err) = - wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty) - { + if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty) { infcx .err_ctxt() .report_mismatched_types(&cause, self_ty, potential_self_ty, err) @@ -1754,13 +1787,9 @@ fn receiver_is_implemented<'tcx>( receiver_ty: Ty<'tcx>, ) -> bool { let tcx = wfcx.tcx(); - let trait_ref = ty::Binder::dummy(ty::TraitRef { - def_id: receiver_trait_def_id, - substs: tcx.mk_substs_trait(receiver_ty, &[]), - }); + let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(receiver_trait_def_id, [receiver_ty])); - let obligation = - traits::Obligation::new(cause, wfcx.param_env, trait_ref.without_const().to_predicate(tcx)); + let obligation = traits::Obligation::new(tcx, cause, wfcx.param_env, trait_ref); if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) { true @@ -1907,6 +1936,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { } let obligation = traits::Obligation::new( + tcx, traits::ObligationCause::new(span, self.body_id, traits::TrivialBound), empty_env, pred, @@ -1925,56 +1955,6 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)); } -/////////////////////////////////////////////////////////////////////////// -// ADT - -// FIXME(eddyb) replace this with getting fields/discriminants through `ty::AdtDef`. -struct AdtVariant<'tcx> { - /// Types of fields in the variant, that must be well-formed. - fields: Vec<AdtField<'tcx>>, - - /// Explicit discriminant of this variant (e.g. `A = 123`), - /// that must evaluate to a constant value. - explicit_discr: Option<LocalDefId>, -} - -struct AdtField<'tcx> { - ty: Ty<'tcx>, - def_id: LocalDefId, - span: Span, -} - -impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { - // FIXME(eddyb) replace this with getting fields through `ty::AdtDef`. - fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> { - let fields = struct_def - .fields() - .iter() - .map(|field| { - let def_id = self.tcx().hir().local_def_id(field.hir_id); - let field_ty = self.tcx().type_of(def_id); - let field_ty = self.normalize(field.ty.span, None, field_ty); - debug!("non_enum_variant: type of field {:?} is {:?}", field, field_ty); - AdtField { ty: field_ty, span: field.ty.span, def_id } - }) - .collect(); - AdtVariant { fields, explicit_discr: None } - } - - fn enum_variants(&self, enum_def: &hir::EnumDef<'_>) -> Vec<AdtVariant<'tcx>> { - enum_def - .variants - .iter() - .map(|variant| AdtVariant { - fields: self.non_enum_variant(&variant.data).fields, - explicit_discr: variant - .disr_expr - .map(|explicit_discr| self.tcx().hir().local_def_id(explicit_discr.hir_id)), - }) - .collect() - } -} - fn error_392( tcx: TyCtxt<'_>, span: Span, diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index d0c317334..5749b0478 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -57,25 +57,6 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { .maybe_unused_extern_crates(()) .iter() .filter(|&&(def_id, _)| { - // The `def_id` here actually was calculated during resolution (at least - // at the time of this writing) and is being shipped to us via a side - // channel of the tcx. There may have been extra expansion phases, - // however, which ended up removing the `def_id` *after* expansion. - // - // As a result we need to verify that `def_id` is indeed still valid for - // our AST and actually present in the HIR map. If it's not there then - // there's safely nothing to warn about, and otherwise we carry on with - // our execution. - // - // Note that if we carry through to the `extern_mod_stmt_cnum` query - // below it'll cause a panic because `def_id` is actually bogus at this - // point in time otherwise. - if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() { - return false; - } - true - }) - .filter(|&&(def_id, _)| { tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b6c91d425..193ecdb16 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -114,7 +114,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { traits::ObligationCause::dummy_with_span(field_ty_span), param_env, ty, - tcx.lang_items().copy_trait().unwrap(), + tcx.require_lang_item(LangItem::Copy, Some(span)), ) { let error_predicate = error.obligation.predicate; // Only note if it's not the root obligation, otherwise it's trivial and @@ -128,11 +128,11 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { .or_default() .push(error.obligation.cause.span); } - if let ty::PredicateKind::Trait(ty::TraitPredicate { + if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive, .. - }) = error_predicate.kind().skip_binder() + })) = error_predicate.kind().skip_binder() { let ty = trait_ref.self_ty(); if let ty::Param(_) = ty.kind() { @@ -315,13 +315,12 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: cause.clone(), dispatch_from_dyn_trait, 0, - field.ty(tcx, substs_a), - &[field.ty(tcx, substs_b).into()], + [field.ty(tcx, substs_a), field.ty(tcx, substs_b)], ) }), ); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); } // Finally, resolve all regions. @@ -371,7 +370,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>, mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { - if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) { + if mt_a.mutbl < mt_b.mutbl { infcx .err_ctxt() .report_mismatched_types( @@ -558,10 +557,10 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn // Register an obligation for `A: Trait<B>`. let cause = traits::ObligationCause::misc(span, impl_hir_id); let predicate = - predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, source, &[target.into()]); + predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]); let errors = traits::fully_solve_obligation(&infcx, predicate); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); } // Finally, resolve all regions. diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index ae9ebe590..1bf3768fe 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -5,10 +5,11 @@ // done by the orphan and overlap modules. Then we build up various // mappings. That mapping code resides here. -use rustc_errors::struct_span_err; +use rustc_errors::{error_code, struct_span_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_span::sym; use rustc_trait_selection::traits; mod builtin; @@ -39,61 +40,26 @@ fn enforce_trait_manually_implementable( impl_def_id: LocalDefId, trait_def_id: DefId, ) { - let did = Some(trait_def_id); - let li = tcx.lang_items(); let impl_header_span = tcx.def_span(impl_def_id); - // Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now. - if did == li.pointee_trait() { - struct_span_err!( + // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]` + if tcx.has_attr(trait_def_id, sym::rustc_deny_explicit_impl) { + let trait_name = tcx.item_name(trait_def_id); + let mut err = struct_span_err!( tcx.sess, impl_header_span, E0322, - "explicit impls for the `Pointee` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Pointee` not allowed") - .emit(); - return; - } - - if did == li.discriminant_kind_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0322, - "explicit impls for the `DiscriminantKind` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `DiscriminantKind` not allowed") - .emit(); - return; - } - - if did == li.sized_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0322, - "explicit impls for the `Sized` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Sized` not allowed") - .emit(); - return; - } - - if did == li.unsize_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0328, - "explicit impls for the `Unsize` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Unsize` not allowed") - .emit(); - return; - } + "explicit impls for the `{trait_name}` trait are not permitted" + ); + err.span_label(impl_header_span, format!("impl of `{trait_name}` not allowed")); + + // Maintain explicit error code for `Unsize`, since it has a useful + // explanation about using `CoerceUnsized` instead. + if Some(trait_def_id) == tcx.lang_items().unsize_trait() { + err.code(error_code!(E0328)); + } - if tcx.features().unboxed_closures { - // the feature gate allows all Fn traits + err.emit(); return; } diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index bb45c3823..cc5114dba 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, DelayDm}; use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; use rustc_middle::ty::{ @@ -23,9 +22,7 @@ pub(crate) fn orphan_check_impl( impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(err) = trait_ref.error_reported() { - return Err(err); - } + trait_ref.error_reported()?; let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); if tcx.trait_is_auto(trait_ref.def_id) { @@ -49,58 +46,6 @@ fn do_orphan_check_impl<'tcx>( let sp = tcx.def_span(def_id); let tr = impl_.of_trait.as_ref().unwrap(); - // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, - // and #84660 where it would otherwise allow unsoundness. - if trait_ref.has_opaque_types() { - trace!("{:#?}", item); - // First we find the opaque type in question. - for ty in trait_ref.substs { - for ty in ty.walk() { - let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue }; - let ty::Opaque(def_id, _) = *ty.kind() else { continue }; - trace!(?def_id); - - // Then we search for mentions of the opaque type's type alias in the HIR - struct SpanFinder<'tcx> { - sp: Span, - def_id: DefId, - tcx: TyCtxt<'tcx>, - } - impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> { - #[instrument(level = "trace", skip(self, _id))] - fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { - // You can't mention an opaque type directly, so we look for type aliases - if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res { - // And check if that type alias's type contains the opaque type we're looking for - for arg in self.tcx.type_of(def_id).walk() { - if let GenericArgKind::Type(ty) = arg.unpack() { - if let ty::Opaque(def_id, _) = *ty.kind() { - if def_id == self.def_id { - // Finally we update the span to the mention of the type alias - self.sp = path.span; - return; - } - } - } - } - } - hir::intravisit::walk_path(self, path) - } - } - - let mut visitor = SpanFinder { sp, def_id, tcx }; - hir::intravisit::walk_item(&mut visitor, item); - let reported = tcx - .sess - .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait") - .span_note(tcx.def_span(def_id), "type alias impl trait defined here") - .emit(); - return Err(reported); - } - } - span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") - } - match traits::orphan_check(tcx, item.owner_id.to_def_id()) { Ok(()) => {} Err(err) => emit_orphan_check_error( @@ -347,7 +292,7 @@ fn emit_newtype_suggestion_for_raw_ptr( diag: &mut Diagnostic, ) { if !self_ty.needs_subst() { - let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" }; + let mut_key = ptr_ty.mutbl.prefix_str(); let msg_sugg = "consider introducing a new wrapper type".to_owned(); let sugg = vec![ ( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 346d2e2fc..1183a26d5 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -24,18 +24,16 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; -use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::weak_lang_items; -use rustc_hir::{GenericParamKind, Node}; +use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; +use rustc_hir::{lang_items, GenericParamKind, LangItem, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::ReprOptions; -use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, ToPredicate, Ty, TyCtxt}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -84,6 +82,7 @@ pub fn provide(providers: &mut Providers) { asm_target_features, collect_mod_item_types, should_inherit_track_caller, + is_type_alias_impl_trait, ..*providers }; } @@ -291,18 +290,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Type { default: Some(_), .. } => { - let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.ensure().type_of(def_id); + self.tcx.ensure().type_of(param.def_id); } hir::GenericParamKind::Type { .. } => {} hir::GenericParamKind::Const { default, .. } => { - let def_id = self.tcx.hir().local_def_id(param.hir_id); - self.tcx.ensure().type_of(def_id); + self.tcx.ensure().type_of(param.def_id); if let Some(default) = default { - let default_def_id = self.tcx.hir().local_def_id(default.hir_id); // need to store default and type of default - self.tcx.ensure().type_of(default_def_id); - self.tcx.ensure().const_param_default(def_id); + self.tcx.ensure().type_of(default.def_id); + self.tcx.ensure().const_param_default(param.def_id); } } } @@ -311,9 +307,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure { .. } = expr.kind { - let def_id = self.tcx.hir().local_def_id(expr.hir_id); - self.tcx.ensure().generics_of(def_id); + if let hir::ExprKind::Closure(closure) = expr.kind { + self.tcx.ensure().generics_of(closure.def_id); + self.tcx.ensure().codegen_fn_attrs(closure.def_id); // We do not call `type_of` for closures here as that // depends on typecheck and would therefore hide // any further errors in case one typeck fails. @@ -379,8 +375,8 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { self.tcx } - fn item_def_id(&self) -> Option<DefId> { - Some(self.item_def_id) + fn item_def_id(&self) -> DefId { + self.item_def_id } fn get_type_parameter_bounds( @@ -512,8 +508,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { } _ => {} } - err.emit(); - self.tcx().ty_error() + self.tcx().ty_error_with_guaranteed(err.emit()) } } @@ -522,7 +517,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty } - fn set_tainted_by_errors(&self) { + fn set_tainted_by_errors(&self, _: ErrorGuaranteed) { // There's no obvious place to track this, so just let it go. } @@ -587,8 +582,12 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().type_of(item.owner_id); tcx.ensure().predicates_of(item.owner_id); match item.kind { - hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.owner_id), + hir::ForeignItemKind::Fn(..) => { + tcx.ensure().codegen_fn_attrs(item.owner_id); + tcx.ensure().fn_sig(item.owner_id) + } hir::ForeignItemKind::Static(..) => { + tcx.ensure().codegen_fn_attrs(item.owner_id); let mut visitor = HirPlaceholderCollector::default(); visitor.visit_foreign_item(item); placeholder_type_error( @@ -604,11 +603,11 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } } - hir::ItemKind::Enum(ref enum_definition, _) => { + hir::ItemKind::Enum(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - convert_enum_variant_types(tcx, def_id.to_def_id(), enum_definition.variants); + convert_enum_variant_types(tcx, def_id.to_def_id()); } hir::ItemKind::Impl { .. } => { tcx.ensure().generics_of(def_id); @@ -633,23 +632,16 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().predicates_of(def_id); for f in struct_def.fields() { - let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure().generics_of(f.def_id); + tcx.ensure().type_of(f.def_id); + tcx.ensure().predicates_of(f.def_id); } - if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { - convert_variant_ctor(tcx, ctor_hir_id); + if let Some(ctor_def_id) = struct_def.ctor_def_id() { + convert_variant_ctor(tcx, ctor_def_id); } } - // Desugared from `impl Trait`, so visited by the function's return type. - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), - .. - }) => {} - // Don't call `type_of` on opaque types, since that depends on type // checking function bodies. `check_item_type` ensures that it's called // instead. @@ -657,27 +649,33 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); tcx.ensure().explicit_item_bounds(def_id); + tcx.ensure().item_bounds(def_id); } - hir::ItemKind::TyAlias(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) => { + + hir::ItemKind::TyAlias(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - match it.kind { - hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id), - hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id), - hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => { - if !is_suggestable_infer_ty(ty) { - let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_item(it); - placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); - } - } - _ => (), + } + + hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + if !is_suggestable_infer_ty(ty) { + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_item(it); + placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); } } + + hir::ItemKind::Fn(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + tcx.ensure().fn_sig(def_id); + tcx.ensure().codegen_fn_attrs(def_id); + } } } @@ -688,6 +686,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { match trait_item.kind { hir::TraitItemKind::Fn(..) => { + tcx.ensure().codegen_fn_attrs(def_id); tcx.ensure().type_of(def_id); tcx.ensure().fn_sig(def_id); } @@ -737,6 +736,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { let impl_item = tcx.hir().impl_item(impl_item_id); match impl_item.kind { hir::ImplItemKind::Fn(..) => { + tcx.ensure().codegen_fn_attrs(def_id); tcx.ensure().fn_sig(def_id); } hir::ImplItemKind::Type(_) => { @@ -750,37 +750,34 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } } -fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) { - let def_id = tcx.hir().local_def_id(ctor_id); +fn convert_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); } -fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) { +fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); let mut prev_discr = None::<Discr<'_>>; // fill the discriminant values and field types - for variant in variants { + for variant in def.variants() { let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); prev_discr = Some( - if let Some(ref e) = variant.disr_expr { - let expr_did = tcx.hir().local_def_id(e.hir_id); - def.eval_explicit_discr(tcx, expr_did.to_def_id()) + if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr { + def.eval_explicit_discr(tcx, const_def_id) } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { Some(discr) } else { - struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed") - .span_label( - variant.span, - format!("overflowed on value after {}", prev_discr.unwrap()), - ) + let span = tcx.def_span(variant.def_id); + struct_span_err!(tcx.sess, span, E0370, "enum discriminant overflowed") + .span_label(span, format!("overflowed on value after {}", prev_discr.unwrap())) .note(&format!( "explicitly set `{} = {}` if that is desired outcome", - variant.ident, wrapped_discr + tcx.item_name(variant.def_id), + wrapped_discr )) .emit(); None @@ -788,17 +785,16 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V .unwrap_or(wrapped_discr), ); - for f in variant.data.fields() { - let def_id = tcx.hir().local_def_id(f.hir_id); - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + for f in &variant.fields { + tcx.ensure().generics_of(f.did); + tcx.ensure().type_of(f.did); + tcx.ensure().predicates_of(f.did); } // Convert the ctor, if any. This also registers the variant as // an item. - if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - convert_variant_ctor(tcx, ctor_hir_id); + if let Some(ctor_def_id) = variant.ctor_def_id() { + convert_variant_ctor(tcx, ctor_def_id.expect_local()); } } } @@ -806,7 +802,6 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::V fn convert_variant( tcx: TyCtxt<'_>, variant_did: Option<LocalDefId>, - ctor_did: Option<LocalDefId>, ident: Ident, discr: ty::VariantDiscr, def: &hir::VariantData<'_>, @@ -818,7 +813,6 @@ fn convert_variant( .fields() .iter() .map(|f| { - let fid = tcx.hir().local_def_id(f.hir_id); let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { tcx.sess.emit_err(errors::FieldAlreadyDeclared { @@ -830,7 +824,11 @@ fn convert_variant( seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } - ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) } + ty::FieldDef { + did: f.def_id.to_def_id(), + name: f.ident.name, + vis: tcx.visibility(f.def_id), + } }) .collect(); let recovered = match def { @@ -840,10 +838,9 @@ fn convert_variant( ty::VariantDef::new( ident.name, variant_did.map(LocalDefId::to_def_id), - ctor_did.map(LocalDefId::to_def_id), + def.ctor().map(|(kind, _, def_id)| (kind, def_id.to_def_id())), discr, fields, - CtorKind::from_hir(def), adt_kind, parent_did.to_def_id(), recovered, @@ -863,7 +860,7 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { bug!(); }; - let repr = ReprOptions::new(tcx, def_id.to_def_id()); + let repr = tcx.repr_options_of_def(def_id.to_def_id()); let (kind, variants) = match item.kind { ItemKind::Enum(ref def, _) => { let mut distance_from_explicit = 0; @@ -871,13 +868,9 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { .variants .iter() .map(|v| { - let variant_did = Some(tcx.hir().local_def_id(v.id)); - let ctor_did = - v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - let discr = if let Some(ref e) = v.disr_expr { distance_from_explicit = 0; - ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id()) + ty::VariantDiscr::Explicit(e.def_id.to_def_id()) } else { ty::VariantDiscr::Relative(distance_from_explicit) }; @@ -885,8 +878,7 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { convert_variant( tcx, - variant_did, - ctor_did, + Some(v.def_id), v.ident, discr, &v.data, @@ -898,41 +890,23 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { (AdtKind::Enum, variants) } - ItemKind::Struct(ref def, _) => { - let variant_did = None::<LocalDefId>; - let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - - let variants = std::iter::once(convert_variant( - tcx, - variant_did, - ctor_did, - item.ident, - ty::VariantDiscr::Relative(0), - def, - AdtKind::Struct, - def_id, - )) - .collect(); - - (AdtKind::Struct, variants) - } - ItemKind::Union(ref def, _) => { - let variant_did = None; - let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id)); - + ItemKind::Struct(ref def, _) | ItemKind::Union(ref def, _) => { + let adt_kind = match item.kind { + ItemKind::Struct(..) => AdtKind::Struct, + _ => AdtKind::Union, + }; let variants = std::iter::once(convert_variant( tcx, - variant_did, - ctor_did, + None, item.ident, ty::VariantDiscr::Relative(0), def, - AdtKind::Union, + adt_kind, def_id, )) .collect(); - (AdtKind::Union, variants) + (adt_kind, variants) } _ => bug!(), }; @@ -1181,10 +1155,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi) } - Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { + Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => { let ty = tcx.type_of(tcx.hir().get_parent_item(hir_id)); - let inputs = - data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); + let inputs = data.fields().iter().map(|f| tcx.type_of(f.def_id)); ty::Binder::dummy(tcx.mk_fn_sig( inputs, ty, @@ -1394,12 +1367,14 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate "predicates_defined_on: inferred_outlives_of({:?}) = {:?}", def_id, inferred_outlives, ); + let inferred_outlives_iter = + inferred_outlives.iter().map(|(clause, span)| ((*clause).to_predicate(tcx), *span)); if result.predicates.is_empty() { - result.predicates = inferred_outlives; + result.predicates = tcx.arena.alloc_from_iter(inferred_outlives_iter); } else { - result.predicates = tcx - .arena - .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); + result.predicates = tcx.arena.alloc_from_iter( + result.predicates.into_iter().copied().chain(inferred_outlives_iter), + ); } } @@ -1840,7 +1815,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { ); } else if attr.has_name(sym::linkage) { if let Some(val) = attr.value_str() { - codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str())); + let linkage = Some(linkage_by_name(tcx, did, val.as_str())); + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + } else { + codegen_fn_attrs.linkage = linkage; + } } } else if attr.has_name(sym::link_section) { if let Some(val) = attr.value_str() { @@ -2099,17 +2079,25 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; + codegen_fn_attrs.inline = InlineAttr::Never; + } + // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only // strippable by the linker. // // Additionally weak lang items have predetermined symbol names. - if tcx.is_weak_lang_item(did.to_def_id()) { + if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } - if let Some(name) = weak_lang_items::link_name(attrs) { - codegen_fn_attrs.export_name = Some(name); - codegen_fn_attrs.link_name = Some(name); + if let Some((name, _)) = lang_items::extract(attrs) + && let Some(lang_item) = LangItem::from_name(name) + && let Some(link_name) = lang_item.link_name() + { + codegen_fn_attrs.export_name = Some(link_name); + codegen_fn_attrs.link_name = Some(link_name); } check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); @@ -2170,7 +2158,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { - use rustc_ast::{Lit, LitIntType, LitKind}; + use rustc_ast::{LitIntType, LitKind, MetaItemLit}; if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" { feature_err( &tcx.sess.parse_sess, @@ -2181,9 +2169,9 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { .emit(); } let meta_item_list = attr.meta_item_list(); - let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref); + let meta_item_list = meta_item_list.as_deref(); let sole_meta_list = match meta_item_list { - Some([item]) => item.literal(), + Some([item]) => item.lit(), Some(_) => { tcx.sess .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`") @@ -2193,7 +2181,9 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { } _ => None, }; - if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { + if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = + sole_meta_list + { // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information @@ -2261,3 +2251,13 @@ fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: } } } + +fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + match tcx.hir().get_if_local(def_id) { + Some(Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. })) => { + matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) + } + Some(_) => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id), + _ => bug!("tried getting opaque_ty_origin for non-local def-id {:?}", def_id), + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index c7777a946..639f81f20 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -51,7 +51,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed. None } else if tcx.lazy_normalization() { - if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) { + if let Some(param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id) { // If the def_id we are calling generics_of on is an anon ct default i.e: // // struct Foo<const N: usize = { .. }>; @@ -77,8 +77,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // This has some implications for how we get the predicates available to the anon const // see `explicit_predicates_of` for more information on this let generics = tcx.generics_of(parent_def_id.to_def_id()); - let param_def = tcx.hir().local_def_id(param_id).to_def_id(); - let param_def_idx = generics.param_def_id_to_index[¶m_def]; + let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()]; // In the above example this would be .params[..N#0] let params = generics.params[..param_def_idx as usize].to_owned(); let param_def_id_to_index = @@ -241,7 +240,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { name: param.name.ident().name, index: own_start + i as u32, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + def_id: param.def_id.to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind: ty::GenericParamDefKind::Lifetime, })); @@ -286,7 +285,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Some(ty::GenericParamDef { index: next_index(), name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + def_id: param.def_id.to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind, }) @@ -303,7 +302,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Some(ty::GenericParamDef { index: next_index(), name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + def_id: param.def_id.to_def_id(), pure_wrt_drop: param.pure_wrt_drop, kind: ty::GenericParamDefKind::Const { has_default: default.is_some() }, }) @@ -399,7 +398,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { - self.has_late_bound_regions = Some(lt.span); + self.has_late_bound_regions = Some(lt.ident.span); } } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0d34a8bfe..0542e2c8f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -35,9 +35,11 @@ fn associated_type_bounds<'tcx>( let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| { match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tr) => tr.self_ty() == item_ty, - ty::PredicateKind::Projection(proj) => proj.projection_ty.self_ty() == item_ty, - ty::PredicateKind::TypeOutlives(outlives) => outlives.0 == item_ty, + ty::PredicateKind::Clause(ty::Clause::Trait(tr)) => tr.self_ty() == item_ty, + ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => { + proj.projection_ty.self_ty() == item_ty + } + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(outlives)) => outlives.0 == item_ty, _ => false, } }); diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs index 3f263a6de..e4fe3e90e 100644 --- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs +++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs @@ -15,19 +15,18 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; use rustc_middle::bug; -use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::fmt; trait RegionExt { - fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); + fn early(param: &GenericParam<'_>) -> (LocalDefId, Region); - fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); + fn late(index: u32, param: &GenericParam<'_>) -> (LocalDefId, Region); fn id(&self) -> Option<DefId>; @@ -35,20 +34,18 @@ trait RegionExt { } impl RegionExt for Region { - fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) { - let def_id = hir_map.local_def_id(param.hir_id); - debug!("Region::early: def_id={:?}", def_id); - (def_id, Region::EarlyBound(def_id.to_def_id())) + fn early(param: &GenericParam<'_>) -> (LocalDefId, Region) { + debug!("Region::early: def_id={:?}", param.def_id); + (param.def_id, Region::EarlyBound(param.def_id.to_def_id())) } - fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) { + fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, Region) { let depth = ty::INNERMOST; - let def_id = hir_map.local_def_id(param.hir_id); debug!( "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?}", - idx, param, depth, def_id, + idx, param, depth, param.def_id, ); - (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) + (param.def_id, Region::LateBound(depth, idx, param.def_id.to_def_id())) } fn id(&self) -> Option<DefId> { @@ -94,11 +91,6 @@ struct LifetimeContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, - - /// Indicates that we only care about the definition of a trait. This should - /// be false if the `Item` we are resolving lifetimes for is not a trait or - /// we eventually need lifetimes resolve for trait items. - trait_definition_only: bool, } #[derive(Debug)] @@ -166,7 +158,9 @@ enum Scope<'a> { s: ScopeRef<'a>, }, - Root, + Root { + opt_parent_item: Option<LocalDefId>, + }, } #[derive(Copy, Clone, Debug)] @@ -214,95 +208,58 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("s", &"..") .finish(), Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), - Scope::Root => f.debug_struct("Root").finish(), + Scope::Root { opt_parent_item } => { + f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() + } } } } type ScopeRef<'a> = &'a Scope<'a>; -const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; - pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { - resolve_lifetimes_trait_definition, resolve_lifetimes, - named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id), + named_region_map: |tcx, id| tcx.resolve_lifetimes(id).defs.get(&id), is_late_bound_map, object_lifetime_default, - late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id), + late_bound_vars_map: |tcx, id| tcx.resolve_lifetimes(id).late_bound_vars.get(&id), ..*providers }; } -/// Like `resolve_lifetimes`, but does not resolve lifetimes for trait items. -/// Also does not generate any diagnostics. -/// -/// This is ultimately a subset of the `resolve_lifetimes` work. It effectively -/// resolves lifetimes only within the trait "header" -- that is, the trait -/// and supertrait list. In contrast, `resolve_lifetimes` resolves all the -/// lifetimes within the trait and its items. There is room to refactor this, -/// for example to resolve lifetimes for each trait item in separate queries, -/// but it's convenient to do the entire trait at once because the lifetimes -/// from the trait definition are in scope within the trait items as well. -/// -/// The reason for this separate call is to resolve what would otherwise -/// be a cycle. Consider this example: -/// -/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub) -/// trait Base<'a> { -/// type BaseItem; -/// } -/// trait Sub<'b>: for<'a> Base<'a> { -/// type SubItem: Sub<BaseItem = &'b u32>; -/// } -/// ``` -/// -/// When we resolve `Sub` and all its items, we also have to resolve `Sub<BaseItem = &'b u32>`. -/// To figure out the index of `'b`, we have to know about the supertraits -/// of `Sub` so that we can determine that the `for<'a>` will be in scope. -/// (This is because we -- currently at least -- flatten all the late-bound -/// lifetimes into a single binder.) This requires us to resolve the -/// *trait definition* of `Sub`; basically just enough lifetime information -/// to look at the supertraits. -#[instrument(level = "debug", skip(tcx))] -fn resolve_lifetimes_trait_definition( - tcx: TyCtxt<'_>, - local_def_id: LocalDefId, -) -> ResolveLifetimes { - convert_named_region_map(do_resolve(tcx, local_def_id, true)) -} - /// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use /// `named_region_map`, `is_late_bound_map`, etc. #[instrument(level = "debug", skip(tcx))] -fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes { - convert_named_region_map(do_resolve(tcx, local_def_id, false)) -} - -fn do_resolve( - tcx: TyCtxt<'_>, - local_def_id: LocalDefId, - trait_definition_only: bool, -) -> NamedRegionMap { - let item = tcx.hir().expect_item(local_def_id); +fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveLifetimes { let mut named_region_map = NamedRegionMap { defs: Default::default(), late_bound_vars: Default::default() }; let mut visitor = LifetimeContext { tcx, map: &mut named_region_map, - scope: ROOT_SCOPE, - trait_definition_only, + scope: &Scope::Root { opt_parent_item: None }, }; - visitor.visit_item(item); - - named_region_map -} + match tcx.hir().owner(local_def_id) { + hir::OwnerNode::Item(item) => visitor.visit_item(item), + hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), + hir::OwnerNode::TraitItem(item) => { + let scope = + Scope::Root { opt_parent_item: Some(tcx.local_parent(item.owner_id.def_id)) }; + visitor.scope = &scope; + visitor.visit_trait_item(item) + } + hir::OwnerNode::ImplItem(item) => { + let scope = + Scope::Root { opt_parent_item: Some(tcx.local_parent(item.owner_id.def_id)) }; + visitor.scope = &scope; + visitor.visit_impl_item(item) + } + hir::OwnerNode::Crate(_) => {} + } -fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes { let mut rl = ResolveLifetimes::default(); for (hir_id, v) in named_region_map.defs { @@ -319,53 +276,6 @@ fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetime rl } -/// Given `any` owner (structs, traits, trait methods, etc.), does lifetime resolution. -/// There are two important things this does. -/// First, we have to resolve lifetimes for -/// the entire *`Item`* that contains this owner, because that's the largest "scope" -/// where we can have relevant lifetimes. -/// Second, if we are asking for lifetimes in a trait *definition*, we use `resolve_lifetimes_trait_definition` -/// instead of `resolve_lifetimes`, which does not descend into the trait items and does not emit diagnostics. -/// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner -/// other than the trait itself (like the trait methods or associated types), then we just use the regular -/// `resolve_lifetimes`. -fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: hir::OwnerId) -> &'tcx ResolveLifetimes { - let item_id = item_for(tcx, def_id.def_id); - let local_def_id = item_id.owner_id.def_id; - if item_id.owner_id == def_id { - let item = tcx.hir().item(item_id); - match item.kind { - hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(local_def_id), - _ => tcx.resolve_lifetimes(local_def_id), - } - } else { - tcx.resolve_lifetimes(local_def_id) - } -} - -/// Finds the `Item` that contains the given `LocalDefId` -fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> hir::ItemId { - match tcx.hir().find_by_def_id(local_def_id) { - Some(Node::Item(item)) => { - return item.item_id(); - } - _ => {} - } - let item = { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); - let mut parent_iter = tcx.hir().parent_iter(hir_id); - loop { - let node = parent_iter.next().map(|n| n.1); - match node { - Some(hir::Node::Item(item)) => break item.item_id(), - Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."), - _ => {} - } - } - }; - item -} - fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind { match region { Region::LateBound(_, _, def_id) => { @@ -383,7 +293,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut supertrait_lifetimes = vec![]; loop { match scope { - Scope::Body { .. } | Scope::Root => { + Scope::Body { .. } | Scope::Root { .. } => { break (vec![], BinderScopeType::Normal); } @@ -414,21 +324,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { - type NestedFilter = nested_filter::All; + type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { self.tcx.hir() } - // We want to nest trait/impl items in their parent, but nothing else. - fn visit_nested_item(&mut self, _: hir::ItemId) {} - - fn visit_trait_item_ref(&mut self, ii: &'tcx hir::TraitItemRef) { - if !self.trait_definition_only { - intravisit::walk_trait_item_ref(self, ii) - } - } - fn visit_nested_body(&mut self, body: hir::BodyId) { let body = self.tcx.hir().body(body); self.with(Scope::Body { id: body.id(), s: self.scope }, |this| { @@ -491,7 +392,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); + let pair = Region::late(late_bound_idx as u32, param); let r = late_region_as_bound_region(self.tcx, &pair.1); (pair, r) }) @@ -548,7 +449,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_item(this, item) }); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias, .. + }) => { // Opaque types are visited when we visit the // `TyKind::OpaqueDef`, so that they have the lifetimes from // their parent opaque_ty in scope. @@ -557,34 +460,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // their owner, we can keep going until we find the Item that owns that. We then // conservatively add all resolved lifetimes. Otherwise we run into problems in // cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`. - for (_hir_id, node) in self.tcx.hir().parent_iter(item.owner_id.into()) { - match node { - hir::Node::Item(parent_item) => { - let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes( - item_for(self.tcx, parent_item.owner_id.def_id).owner_id.def_id, - ); - // We need to add *all* deps, since opaque tys may want them from *us* - for (&owner, defs) in resolved_lifetimes.defs.iter() { - defs.iter().for_each(|(&local_id, region)| { - self.map.defs.insert(hir::HirId { owner, local_id }, *region); - }); - } - for (&owner, late_bound_vars) in - resolved_lifetimes.late_bound_vars.iter() - { - late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { - self.record_late_bound_vars( - hir::HirId { owner, local_id }, - late_bound_vars.clone(), - ); - }); - } - break; + let parent_item = self.tcx.hir().get_parent_item(item.hir_id()); + let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes(parent_item); + // We need to add *all* deps, since opaque tys may want them from *us* + for (&owner, defs) in resolved_lifetimes.defs.iter() { + defs.iter().for_each(|(&local_id, region)| { + self.map.defs.insert(hir::HirId { owner, local_id }, *region); + }); + } + for (&owner, late_bound_vars) in resolved_lifetimes.late_bound_vars.iter() { + late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { + self.record_late_bound_vars( + hir::HirId { owner, local_id }, + late_bound_vars.clone(), + ); + }); + } + } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_), + generics, + .. + }) => { + // We want to start our early-bound indices at the end of the parent scope, + // not including any parent `impl Trait`s. + let mut lifetimes = FxIndexMap::default(); + debug!(?generics.params); + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => { + let (def_id, reg) = Region::early(¶m); + lifetimes.insert(def_id, reg); } - hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"), - _ => {} + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} } } + + let scope = Scope::Binder { + hir_id: item.hir_id(), + lifetimes, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| intravisit::walk_item(this, item)) + }); } hir::ItemKind::TyAlias(_, ref generics) | hir::ItemKind::Enum(_, ref generics) @@ -598,9 +520,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .params .iter() .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::early(self.tcx.hir(), param)) - } + GenericParamKind::Lifetime { .. } => Some(Region::early(param)), GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, }) .collect(); @@ -609,7 +529,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir_id: item.hir_id(), lifetimes, scope_type: BinderScopeType::Normal, - s: ROOT_SCOPE, + s: self.scope, where_bound_origin: None, }; self.with(scope, |this| { @@ -648,7 +568,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); + let pair = Region::late(late_bound_idx as u32, param); let r = late_region_as_bound_region(self.tcx, &pair.1); (pair, r) }) @@ -675,7 +595,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.visit_poly_trait_ref(bound); } }); - match lifetime.name { + match lifetime.res { LifetimeName::ImplicitObjectLifetimeDefault => { // If the user does not write *anything*, we // use the object lifetime defaulting @@ -712,7 +632,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // ^ ^ this gets resolved in the scope of // the opaque_ty generics let opaque_ty = self.tcx.hir().item(item_id); - let (generics, bounds) = match opaque_ty.kind { + match opaque_ty.kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. @@ -733,10 +653,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..), - ref generics, - bounds, .. - }) => (generics, bounds), + }) => {} ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), }; @@ -766,65 +684,28 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Ensure that the parent of the def is an item, not HRTB let parent_id = self.tcx.hir().get_parent_node(hir_id); if !parent_id.is_owner() { - if !self.trait_definition_only { - struct_span_err!( - self.tcx.sess, - lifetime.span, - E0657, - "`impl Trait` can only capture lifetimes \ - bound at the fn or impl level" - ) - .emit(); - } + struct_span_err!( + self.tcx.sess, + lifetime.ident.span, + E0657, + "`impl Trait` can only capture lifetimes bound at the fn or impl level" + ) + .emit(); self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } if let hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy { .. }, .. }) = self.tcx.hir().get(parent_id) { - if !self.trait_definition_only { - let mut err = self.tcx.sess.struct_span_err( - lifetime.span, - "higher kinded lifetime bounds on nested opaque types are not supported yet", - ); - err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); - err.emit(); - } + let mut err = self.tcx.sess.struct_span_err( + lifetime.ident.span, + "higher kinded lifetime bounds on nested opaque types are not supported yet", + ); + err.span_note(self.tcx.def_span(def_id), "lifetime declared here"); + err.emit(); self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } } - - // We want to start our early-bound indices at the end of the parent scope, - // not including any parent `impl Trait`s. - let mut lifetimes = FxIndexMap::default(); - debug!(?generics.params); - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => { - let (def_id, reg) = Region::early(self.tcx.hir(), ¶m); - lifetimes.insert(def_id, reg); - } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} - } - } - self.record_late_bound_vars(ty.hir_id, vec![]); - - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); } _ => intravisit::walk_ty(self, ty), } @@ -845,9 +726,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .params .iter() .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::early(self.tcx.hir(), param)) - } + GenericParamKind::Lifetime { .. } => Some(Region::early(param)), GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, }) .collect(); @@ -893,9 +772,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .params .iter() .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::early(self.tcx.hir(), param)) - } + GenericParamKind::Lifetime { .. } => Some(Region::early(param)), GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None, }) .collect(); @@ -925,9 +802,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { - match lifetime_ref.name { + match lifetime_ref.res { hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), - hir::LifetimeName::Param(param_def_id, _) => { + hir::LifetimeName::Param(param_def_id) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. @@ -937,7 +814,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { for (i, segment) in path.segments.iter().enumerate() { let depth = path.segments.len() - i - 1; if let Some(ref args) = segment.args { @@ -1000,7 +877,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }) .enumerate() .map(|(late_bound_idx, param)| { - Region::late(late_bound_idx as u32, this.tcx.hir(), param) + Region::late(late_bound_idx as u32, param) }) .collect(); let binders: Vec<_> = @@ -1035,27 +912,27 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.visit_lifetime(lifetime); walk_list!(this, visit_param_bound, bounds); - if lifetime.name != hir::LifetimeName::Static { + if lifetime.res != hir::LifetimeName::Static { for bound in bounds { let hir::GenericBound::Outlives(ref lt) = bound else { continue; }; - if lt.name != hir::LifetimeName::Static { + if lt.res != hir::LifetimeName::Static { continue; } this.insert_lifetime(lt, Region::Static); this.tcx .sess .struct_span_warn( - lifetime.span, + lifetime.ident.span, &format!( "unnecessary lifetime parameter `{}`", - lifetime.name.ident(), + lifetime.ident, ), ) .help(&format!( "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.name.ident(), + lifetime.ident, )) .emit(); } @@ -1113,8 +990,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = - Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param); + let pair = Region::late(initial_bound_vars + late_bound_idx as u32, param); let r = late_region_as_bound_region(self.tcx, &pair.1); lifetimes.insert(pair.0, pair.1); r @@ -1167,7 +1043,7 @@ fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> Obje for bound in bound.bounds { if let hir::GenericBound::Outlives(ref lifetime) = *bound { - set.insert(lifetime.name.normalize_to_macros_2_0()); + set.insert(lifetime.res); } } } @@ -1175,7 +1051,7 @@ fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> Obje match set { Set1::Empty => ObjectLifetimeDefault::Empty, Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeName::Param(param_def_id, _)) => { + Set1::One(hir::LifetimeName::Param(param_def_id)) => { ObjectLifetimeDefault::Param(param_def_id.to_def_id()) } _ => ObjectLifetimeDefault::Ambiguous, @@ -1193,12 +1069,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>), { let LifetimeContext { tcx, map, .. } = self; - let mut this = LifetimeContext { - tcx: *tcx, - map, - scope: &wrap_scope, - trait_definition_only: self.trait_definition_only, - }; + let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope }; let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); @@ -1250,9 +1121,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if self.tcx.is_late_bound(param.hir_id) { let late_bound_idx = named_late_bound_vars; named_late_bound_vars += 1; - Some(Region::late(late_bound_idx, self.tcx.hir(), param)) + Some(Region::late(late_bound_idx, param)) } else { - Some(Region::early(self.tcx.hir(), param)) + Some(Region::early(param)) } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, @@ -1268,7 +1139,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); + let pair = Region::late(late_bound_idx as u32, param); late_region_as_bound_region(self.tcx, &pair.1) }) .collect(); @@ -1303,7 +1174,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Root => { + Scope::Root { opt_parent_item } => { + if let Some(parent_item) = opt_parent_item + && let parent_generics = self.tcx.generics_of(parent_item) + && parent_generics.param_def_id_to_index.contains_key(®ion_def_id.to_def_id()) + { + break Some(Region::EarlyBound(region_def_id.to_def_id())); + } break None; } @@ -1318,42 +1195,52 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in // regular fns. if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin - && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name + && let hir::LifetimeName::Param(param_id) = lifetime_ref.res + && let Some(generics) = self.tcx.hir().get_generics(self.tcx.local_parent(param_id)) + && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id) + && param.is_elided_lifetime() && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id) && !self.tcx.features().anonymous_lifetime_in_impl_trait { let mut diag = rustc_session::parse::feature_err( &self.tcx.sess.parse_sess, sym::anonymous_lifetime_in_impl_trait, - lifetime_ref.span, + lifetime_ref.ident.span, "anonymous lifetimes in `impl Trait` are unstable", ); - match self.tcx.hir().get_generics(lifetime_ref.hir_id.owner.def_id) { - Some(generics) => { - - let new_param_sugg_tuple; - - new_param_sugg_tuple = match generics.span_for_param_suggestion() { - Some(_) => { - Some((self.tcx.sess.source_map().span_through_char(generics.span, '<').shrink_to_hi(), "'a, ".to_owned())) - }, - None => Some((generics.span, "<'a>".to_owned())) - }; - - let mut multi_sugg_vec = vec![(lifetime_ref.span.shrink_to_hi(), "'a ".to_owned())]; - - if let Some(new_tuple) = new_param_sugg_tuple{ - multi_sugg_vec.push(new_tuple); - } - - diag.span_label(lifetime_ref.span, "expected named lifetime parameter"); - diag.multipart_suggestion("consider introducing a named lifetime parameter", - multi_sugg_vec, - rustc_errors::Applicability::MaybeIncorrect); - - }, - None => { } + if let Some(generics) = + self.tcx.hir().get_generics(lifetime_ref.hir_id.owner.def_id) + { + let new_param_sugg = if let Some(span) = + generics.span_for_lifetime_suggestion() + { + (span, "'a, ".to_owned()) + } else { + (generics.span, "<'a>".to_owned()) + }; + + let lifetime_sugg = match lifetime_ref.suggestion_position() { + (hir::LifetimeSuggestionPosition::Normal, span) => (span, "'a".to_owned()), + (hir::LifetimeSuggestionPosition::Ampersand, span) => (span, "'a ".to_owned()), + (hir::LifetimeSuggestionPosition::ElidedPath, span) => (span, "<'a>".to_owned()), + (hir::LifetimeSuggestionPosition::ElidedPathArgument, span) => (span, "'a, ".to_owned()), + (hir::LifetimeSuggestionPosition::ObjectDefault, span) => (span, "+ 'a".to_owned()), + }; + let suggestions = vec![ + lifetime_sugg, + new_param_sugg, + ]; + + diag.span_label( + lifetime_ref.ident.span, + "expected named lifetime parameter", + ); + diag.multipart_suggestion( + "consider introducing a named lifetime parameter", + suggestions, + rustc_errors::Applicability::MaybeIncorrect, + ); } diag.emit(); @@ -1377,11 +1264,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } else if let Some(body_id) = outermost_body { let fn_id = self.tcx.hir().body_owner(body_id); match self.tcx.hir().get(fn_id) { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) - | Node::TraitItem(&hir::TraitItem { + Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { + | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) + | Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { let scope = self.tcx.hir().local_def_id(fn_id); def = Region::Free(scope.to_def_id(), def.id().unwrap()); } @@ -1409,14 +1297,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), .. } => { let mut err = self.tcx.sess.struct_span_err( - lifetime_ref.span, + lifetime_ref.ident.span, "`impl Trait` can only mention lifetimes bound at the fn or impl level", ); err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here"); err.emit(); return; } - Scope::Root => break, + Scope::Root { .. } => break, Scope::Binder { s, .. } | Scope::Body { s, .. } | Scope::Elision { s, .. } @@ -1429,7 +1317,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } self.tcx.sess.delay_span_bug( - lifetime_ref.span, + lifetime_ref.ident.span, &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,), ); } @@ -1494,7 +1382,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut scope = self.scope; loop { match *scope { - Scope::Root => break false, + Scope::Root { .. } => break false, Scope::Body { .. } => break true, @@ -1680,7 +1568,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(data) => { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { // The order here needs to match what we would get from `subst_supertrait` let pred_bound_vars = bound_predicate.bound_vars(); let mut all_bound_vars = bound_vars.clone(); @@ -1731,7 +1619,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Root | Scope::Elision { .. } => break Region::Static, + Scope::Root { .. } | Scope::Elision { .. } => break Region::Static, Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, @@ -1747,10 +1635,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { - debug!( - node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id), - span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span) - ); + debug!(span = ?lifetime_ref.ident.span); self.map.defs.insert(lifetime_ref.hir_id, def); } @@ -1780,7 +1665,7 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< let mut late_bound = FxIndexSet::default(); - let mut constrained_by_input = ConstrainedCollector::default(); + let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx }; for arg_ty in decl.inputs { constrained_by_input.visit_ty(arg_ty); } @@ -1833,12 +1718,65 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< debug!(?late_bound); return Some(tcx.arena.alloc(late_bound)); - #[derive(Default)] - struct ConstrainedCollector { + /// Visits a `ty::Ty` collecting information about what generic parameters are constrained. + /// + /// The visitor does not operate on `hir::Ty` so that it can be called on the rhs of a `type Alias<...> = ...;` + /// which may live in a separate crate so there would not be any hir available. Instead we use the `type_of` + /// query to obtain a `ty::Ty` which will be present even in cross crate scenarios. It also naturally + /// handles cycle detection as we go through the query system. + /// + /// This is necessary in the first place for the following case: + /// ``` + /// type Alias<'a, T> = <T as Trait<'a>>::Assoc; + /// fn foo<'a>(_: Alias<'a, ()>) -> Alias<'a, ()> { ... } + /// ``` + /// + /// If we conservatively considered `'a` unconstrained then we could break users who had written code before + /// we started correctly handling aliases. If we considered `'a` constrained then it would become late bound + /// causing an error during astconv as the `'a` is not constrained by the input type `<() as Trait<'a>>::Assoc` + /// but appears in the output type `<() as Trait<'a>>::Assoc`. + /// + /// We must therefore "look into" the `Alias` to see whether we should consider `'a` constrained or not. + /// + /// See #100508 #85533 #47511 for additional context + struct ConstrainedCollectorPostAstConv { + arg_is_constrained: Box<[bool]>, + } + + use std::ops::ControlFlow; + use ty::Ty; + impl<'tcx> TypeVisitor<'tcx> for ConstrainedCollectorPostAstConv { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> { + match t.kind() { + ty::Param(param_ty) => { + self.arg_is_constrained[param_ty.index as usize] = true; + } + ty::Projection(_) => return ControlFlow::Continue(()), + _ => (), + } + t.super_visit_with(self) + } + + fn visit_const(&mut self, _: ty::Const<'tcx>) -> ControlFlow<!> { + ControlFlow::Continue(()) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<!> { + debug!("r={:?}", r.kind()); + if let ty::RegionKind::ReEarlyBound(region) = r.kind() { + self.arg_is_constrained[region.index as usize] = true; + } + + ControlFlow::Continue(()) + } + } + + struct ConstrainedCollector<'tcx> { + tcx: TyCtxt<'tcx>, regions: FxHashSet<LocalDefId>, } - impl<'v> Visitor<'v> for ConstrainedCollector { + impl<'v> Visitor<'v> for ConstrainedCollector<'_> { fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { match ty.kind { hir::TyKind::Path( @@ -1849,6 +1787,47 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< // (defined above) } + hir::TyKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: Res::Def(DefKind::TyAlias, alias_def), segments, span }, + )) => { + // See comments on `ConstrainedCollectorPostAstConv` for why this arm does not just consider + // substs to be unconstrained. + let generics = self.tcx.generics_of(alias_def); + let mut walker = ConstrainedCollectorPostAstConv { + arg_is_constrained: vec![false; generics.params.len()].into_boxed_slice(), + }; + walker.visit_ty(self.tcx.type_of(alias_def)); + + match segments.last() { + Some(hir::PathSegment { args: Some(args), .. }) => { + let tcx = self.tcx; + for constrained_arg in + args.args.iter().enumerate().flat_map(|(n, arg)| { + match walker.arg_is_constrained.get(n) { + Some(true) => Some(arg), + Some(false) => None, + None => { + tcx.sess.delay_span_bug( + *span, + format!( + "Incorrect generic arg count for alias {:?}", + alias_def + ), + ); + None + } + } + }) + { + self.visit_generic_arg(constrained_arg); + } + } + Some(_) => (), + None => bug!("Path with no segments or self type"), + } + } + hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { // consider only the lifetimes on the final // segment; I am not sure it's even currently @@ -1867,7 +1846,7 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< } fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { - if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name { + if let hir::LifetimeName::Param(def_id) = lifetime_ref.res { self.regions.insert(def_id); } } @@ -1880,7 +1859,7 @@ fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet< impl<'v> Visitor<'v> for AllCollector { fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { - if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name { + if let hir::LifetimeName::Param(def_id) = lifetime_ref.res { self.regions.insert(def_id); } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 2e84e1d01..45e241f4e 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -84,60 +84,30 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP Node::ImplItem(item) => item.generics, - Node::Item(item) => { - match item.kind { - ItemKind::Impl(ref impl_) => { - if impl_.defaultness.is_default() { - is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy); - } - &impl_.generics - } - ItemKind::Fn(.., ref generics, _) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) => *generics, - - ItemKind::Trait(_, _, ref generics, ..) => { - is_trait = Some(ty::TraitRef::identity(tcx, def_id)); - *generics + Node::Item(item) => match item.kind { + ItemKind::Impl(ref impl_) => { + if impl_.defaultness.is_default() { + is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy); } - ItemKind::TraitAlias(ref generics, _) => { - is_trait = Some(ty::TraitRef::identity(tcx, def_id)); - *generics - } - ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), - .. - }) => { - // return-position impl trait - // - // We don't inherit predicates from the parent here: - // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` - // then the return type is `f::<'static, T>::{{opaque}}`. - // - // If we inherited the predicates of `f` then we would - // require that `T: 'static` to show that the return - // type is well-formed. - // - // The only way to have something with this opaque type - // is from the return type of the containing function, - // which will ensure that the function's predicates - // hold. - return ty::GenericPredicates { parent: None, predicates: &[] }; - } - ItemKind::OpaqueTy(OpaqueTy { - ref generics, - origin: hir::OpaqueTyOrigin::TyAlias, - .. - }) => { - // type-alias impl trait - generics - } - - _ => NO_GENERICS, + &impl_.generics } - } + ItemKind::Fn(.., ref generics, _) + | ItemKind::TyAlias(_, ref generics) + | ItemKind::Enum(_, ref generics) + | ItemKind::Struct(_, ref generics) + | ItemKind::Union(_, ref generics) => *generics, + + ItemKind::Trait(_, _, ref generics, ..) => { + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); + *generics + } + ItemKind::TraitAlias(ref generics, _) => { + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); + *generics + } + ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) => generics, + _ => NO_GENERICS, + }, Node::ForeignItem(item) => match item.kind { ForeignItemKind::Static(..) => NO_GENERICS, @@ -181,6 +151,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP trace!(?predicates); trace!(?ast_generics); + trace!(?generics); // Collect the predicates that were written inline by the user on each // type parameter (e.g., `<T: Foo>`). @@ -199,7 +170,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP &icx, &mut bounds, &[], - Some((param.hir_id, ast_generics.predicates)), + Some((param.def_id, ast_generics.predicates)), param.span, ); trace!(?bounds); @@ -258,12 +229,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.span) + (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.ident.span) } _ => bug!(), }; - let pred = ty::Binder::dummy(ty::PredicateKind::RegionOutlives( - ty::OutlivesPredicate(r1, r2), + let pred = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::Clause::RegionOutlives(ty::OutlivesPredicate(r1, r2)), )) .to_predicate(icx.tcx); @@ -299,6 +270,52 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP ); } + // Opaque types duplicate some of their generic parameters. + // We create bi-directional Outlives predicates between the original + // and the duplicated parameter, to ensure that they do not get out of sync. + if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { + let opaque_ty_id = tcx.hir().get_parent_node(hir_id); + let opaque_ty_node = tcx.hir().get(opaque_ty_id); + let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else { + bug!("unexpected {opaque_ty_node:?}") + }; + debug!(?lifetimes); + for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) { + let hir::GenericArg::Lifetime(arg) = arg else { bug!() }; + let orig_region = <dyn AstConv<'_>>::ast_region_to_region(&icx, &arg, None); + if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) { + // Only early-bound regions can point to the original generic parameter. + continue; + } + + let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue }; + let dup_def = tcx.hir().local_def_id(duplicate.hir_id).to_def_id(); + + let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() }; + + let dup_region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: dup_def, + index: dup_index, + name: duplicate.name.ident().name, + })); + predicates.push(( + ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives( + ty::OutlivesPredicate(orig_region, dup_region), + ))) + .to_predicate(icx.tcx), + duplicate.span, + )); + predicates.push(( + ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives( + ty::OutlivesPredicate(dup_region, orig_region), + ))) + .to_predicate(icx.tcx), + duplicate.span, + )); + } + debug!(?predicates); + } + ty::GenericPredicates { parent: generics.parent, predicates: tcx.arena.alloc_from_iter(predicates), @@ -316,10 +333,9 @@ fn const_evaluatable_predicates_of<'tcx>( impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> { fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - let def_id = self.tcx.hir().local_def_id(c.hir_id); - let ct = ty::Const::from_anon_const(self.tcx, def_id); + let ct = ty::Const::from_anon_const(self.tcx, c.def_id); if let ty::ConstKind::Unevaluated(_) = ct.kind() { - let span = self.tcx.hir().span(c.hir_id); + let span = self.tcx.def_span(c.def_id); self.preds.insert(( ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)) .to_predicate(self.tcx), @@ -408,11 +424,13 @@ pub(super) fn explicit_predicates_of<'tcx>( .iter() .copied() .filter(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()), - ty::PredicateKind::Projection(proj) => { + ty::PredicateKind::Clause(ty::Clause::Trait(tr)) => !is_assoc_item_ty(tr.self_ty()), + ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => { !is_assoc_item_ty(proj.projection_ty.self_ty()) } - ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(outlives)) => { + !is_assoc_item_ty(outlives.0) + } _ => true, }) .collect(); @@ -427,7 +445,9 @@ pub(super) fn explicit_predicates_of<'tcx>( } else { if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - if tcx.hir().opt_const_param_default_param_hir_id(hir_id).is_some() { + let parent_def_id = tcx.hir().get_parent_item(hir_id); + + if tcx.hir().opt_const_param_default_param_def_id(hir_id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) @@ -439,8 +459,33 @@ pub(super) fn explicit_predicates_of<'tcx>( // parent of generics returned by `generics_of` // // In the above code we want the anon const to have predicates in its param env for `T: Trait` - let item_def_id = tcx.hir().get_parent_item(hir_id); - // In the above code example we would be calling `explicit_predicates_of(Foo)` here + // and we would be calling `explicit_predicates_of(Foo)` here + return tcx.explicit_predicates_of(parent_def_id); + } + + let parent_def_kind = tcx.def_kind(parent_def_id); + if matches!(parent_def_kind, DefKind::OpaqueTy) { + // In `instantiate_identity` we inherit the predicates of our parent. + // However, opaque types do not have a parent (see `gather_explicit_predicates_of`), which means + // that we lose out on the predicates of our actual parent if we dont return those predicates here. + // + // + // fn foo<T: Trait>() -> impl Iterator<Output = Another<{ <T as Trait>::ASSOC }> > { todo!() } + // ^^^^^^^^^^^^^^^^^^^ the def id we are calling + // explicit_predicates_of on + // + // In the above code we want the anon const to have predicates in its param env for `T: Trait`. + // However, the anon const cannot inherit predicates from its parent since it's opaque. + // + // To fix this, we call `explicit_predicates_of` directly on `foo`, the parent's parent. + + // In the above example this is `foo::{opaque#0}` or `impl Iterator` + let parent_hir_id = tcx.hir().local_def_id_to_hir_id(parent_def_id.def_id); + + // In the above example this is the function `foo` + let item_def_id = tcx.hir().get_parent_item(parent_hir_id); + + // In the above code example we would be calling `explicit_predicates_of(foo)` here return tcx.explicit_predicates_of(item_def_id); } } @@ -504,7 +549,7 @@ pub(super) fn super_predicates_that_define_assoc_type( let is_trait_alias = tcx.is_trait_alias(trait_def_id); let superbounds2 = icx.type_parameter_bounds_in_generics( generics, - item.hir_id(), + item.owner_id.def_id, self_param_ty, OnlySelfBounds(!is_trait_alias), assoc_name, @@ -521,7 +566,9 @@ pub(super) fn super_predicates_that_define_assoc_type( // which will, in turn, reach indirect supertraits. for &(pred, span) in superbounds { debug!("superbound: {:?}", pred); - if let ty::PredicateKind::Trait(bound) = pred.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(bound)) = + pred.kind().skip_binder() + { tcx.at(span).super_predicates_of(bound.def_id()); } } @@ -614,14 +661,14 @@ pub(super) fn type_param_predicates( let extra_predicates = extend.into_iter().chain( icx.type_parameter_bounds_in_generics( ast_generics, - param_id, + def_id, ty, OnlySelfBounds(true), Some(assoc_name), ) .into_iter() .filter(|(predicate, _)| match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) => data.self_ty().is_param(index), + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => data.self_ty().is_param(index), _ => false, }), ); @@ -639,13 +686,11 @@ impl<'tcx> ItemCtxt<'tcx> { fn type_parameter_bounds_in_generics( &self, ast_generics: &'tcx hir::Generics<'tcx>, - param_id: hir::HirId, + param_def_id: LocalDefId, ty: Ty<'tcx>, only_self_bounds: OnlySelfBounds, assoc_name: Option<Ident>, ) -> Vec<(ty::Predicate<'tcx>, Span)> { - let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id(); - trace!(?param_def_id); ast_generics .predicates .iter() @@ -654,7 +699,7 @@ impl<'tcx> ItemCtxt<'tcx> { _ => None, }) .flat_map(|bp| { - let bt = if bp.is_param_bound(param_def_id) { + let bt = if bp.is_param_bound(param_def_id.to_def_id()) { Some(ty) } else if !only_self_bounds.0 { Some(self.to_ty(bp.bounded_ty)) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c29a645eb..9bd1715ce 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -514,10 +514,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } Node::GenericParam(&GenericParam { - hir_id: param_hir_id, + def_id: param_def_id, kind: GenericParamKind::Const { default: Some(ct), .. }, .. - }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), + }) if ct.hir_id == hir_id => tcx.type_of(param_def_id), x => tcx.ty_error_with_message( DUMMY_SP, @@ -636,9 +636,8 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T self.tcx.hir() } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure { .. } = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); + if let hir::ExprKind::Closure(closure) = ex.kind { + self.check(closure.def_id); } intravisit::walk_expr(self, ex); } @@ -698,7 +697,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T } let Some(hidden) = locator.found else { - tcx.sess.emit_err(UnconstrainedOpaqueType { + let reported = tcx.sess.emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), what: match tcx.hir().get(scope) { @@ -708,7 +707,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T _ => "item", }, }); - return tcx.ty_error(); + return tcx.ty_error_with_guaranteed(reported); }; // Only check against typeck if we didn't already error @@ -771,9 +770,8 @@ fn find_opaque_ty_constraints_for_rpit( self.tcx.hir() } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure { .. } = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); + if let hir::ExprKind::Closure(closure) = ex.kind { + self.check(closure.def_id); } intravisit::walk_expr(self, ex); } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 213b89fc7..b4057df78 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -187,7 +187,8 @@ pub fn setup_constraining_predicates<'tcx>( for j in i..predicates.len() { // Note that we don't have to care about binders here, // as the impl trait ref never contains any late-bound regions. - if let ty::PredicateKind::Projection(projection) = predicates[j].0.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(projection)) = + predicates[j].0.kind().skip_binder() { // Special case: watch out for some kind of sneaky attempt // to project out an associated type defined by this very diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d5b1a7ce1..c92ab749b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -43,6 +43,10 @@ pub struct LifetimesOrBoundsMismatchOnTrait { pub span: Span, #[label(generics_label)] pub generics_span: Option<Span>, + #[label(where_label)] + pub where_span: Option<Span>, + #[label(bounds_label)] + pub bounds_span: Vec<Span>, pub item_kind: &'static str, pub ident: Ident, } @@ -120,7 +124,7 @@ pub struct TypeofReservedKeywordUsed<'tcx> { #[primary_span] #[label] pub span: Span, - #[suggestion_verbose(code = "{ty}")] + #[suggestion(style = "verbose", code = "{ty}")] pub opt_sugg: Option<(Span, Applicability)>, } @@ -156,6 +160,7 @@ pub struct MissingTypeParams { // Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`. impl<'a> IntoDiagnostic<'a> for MissingTypeParams { + #[track_caller] fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> { let mut err = handler.struct_span_err_with_code( self.span, @@ -238,7 +243,11 @@ pub struct UnusedExternCrate { #[derive(LintDiagnostic)] #[diag(hir_analysis_extern_crate_not_idiomatic)] pub struct ExternCrateNotIdiomatic { - #[suggestion_short(applicability = "machine-applicable", code = "{suggestion_code}")] + #[suggestion( + style = "short", + applicability = "machine-applicable", + code = "{suggestion_code}" + )] pub span: Span, pub msg_code: String, pub suggestion_code: String, @@ -280,3 +289,10 @@ pub struct SelfInImplSelf { #[note] pub note: (), } + +#[derive(Diagnostic)] +#[diag(hir_analysis_linkage_type, code = "E0791")] +pub(crate) struct LinkageType { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index b0fdfcf38..4f9d58265 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -5,7 +5,7 @@ use rustc_hir::{ForeignItem, ForeignItemKind, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder}; use rustc_trait_selection::traits; pub fn provide(providers: &mut Providers) { @@ -74,10 +74,10 @@ fn diagnostic_hir_wf_check<'tcx>( let errors = traits::fully_solve_obligation( &infcx, traits::Obligation::new( + self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())) - .to_predicate(self.tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())), ), ); if !errors.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index e806e9487..fd8e8ed7b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; @@ -80,6 +81,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; +use tracing::instrument; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { @@ -103,13 +105,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti } /// Check that `impl1` is a sound specialization +#[instrument(level = "debug", skip(tcx))] fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) { if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) { let impl2_def_id = impl2_node.def_id(); - debug!( - "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", - impl1_def_id, impl2_def_id, impl2_substs - ); + debug!(?impl2_def_id, ?impl2_substs); let parent_substs = if impl2_node.is_from_trait() { impl2_substs.to_vec() @@ -118,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node }; let span = tcx.def_span(impl1_def_id); + check_constness(tcx, impl1_def_id, impl2_node, span); check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } +/// Check that the specializing impl `impl1` is at least as const as the base +/// impl `impl2` +fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) { + if impl2_node.is_from_trait() { + // This isn't a specialization + return; + } + + let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); + let impl2_constness = tcx.constness(impl2_node.def_id()); + + if let hir::Constness::Const = impl2_constness { + if let hir::Constness::NotConst = impl1_constness { + tcx.sess + .struct_span_err(span, "cannot specialize on const impl with non-const impl") + .emit(); + } + } +} + /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two /// substitutions `(S1, S2)` that equate their trait references. The returned /// types are expressed in terms of the generics of `impl1`. @@ -155,7 +176,7 @@ fn get_impl_substs<'tcx>( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None); return None; } @@ -193,7 +214,9 @@ fn unconstrained_parent_impl_substs<'tcx>( // the functions in `cgp` add the constrained parameters to a list of // unconstrained parameters. for (predicate, _) in impl_generic_predicates.predicates.iter() { - if let ty::PredicateKind::Projection(proj) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = + predicate.kind().skip_binder() + { let projection_ty = proj.projection_ty; let projected_ty = proj.term; @@ -278,15 +301,15 @@ fn check_static_lifetimes<'tcx>( /// Check whether predicates on the specializing impl (`impl1`) are allowed. /// -/// Each predicate `P` must be: +/// Each predicate `P` must be one of: /// -/// * global (not reference any parameters) -/// * `T: Tr` predicate where `Tr` is an always-applicable trait -/// * on the base `impl impl2` -/// * Currently this check is done using syntactic equality, which is -/// conservative but generally sufficient. -/// * a well-formed predicate of a type argument of the trait being implemented, +/// * Global (not reference any parameters). +/// * A `T: Tr` predicate where `Tr` is an always-applicable trait. +/// * Present on the base impl `impl2`. +/// * This check is done using the `trait_predicates_eq` function below. +/// * A well-formed predicate of a type argument of the trait being implemented, /// including the `Self`-type. +#[instrument(level = "debug", skip(tcx))] fn check_predicates<'tcx>( tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, @@ -322,10 +345,7 @@ fn check_predicates<'tcx>( .map(|obligation| obligation.predicate) .collect() }; - debug!( - "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", - impl1_predicates, impl2_predicates, - ); + debug!(?impl1_predicates, ?impl2_predicates); // Since impls of always applicable traits don't get to assume anything, we // can also assume their supertraits apply. @@ -373,25 +393,90 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.contains(&predicate) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) { check_specialization_on(tcx, predicate, span) } } } +/// Checks if some predicate on the specializing impl (`predicate1`) is the same +/// as some predicate on the base impl (`predicate2`). +/// +/// This basically just checks syntactic equivalence, but is a little more +/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// +/// ```ignore (illustrative) +/// #[rustc_specialization_trait] +/// trait Specialize { } +/// +/// impl<T: Bound> Tr for T { } +/// impl<T: ~const Bound + Specialize> const Tr for T { } +/// ``` +/// +/// However, we *don't* want to allow the reverse, i.e., when the bound on the +/// specializing impl is not as const as the bound on the base impl: +/// +/// ```ignore (illustrative) +/// impl<T: ~const Bound> const Tr for T { } +/// impl<T: Bound + Specialize> const Tr for T { } // should be T: ~const Bound +/// ``` +/// +/// So we make that check in this function and try to raise a helpful error message. +fn trait_predicates_eq<'tcx>( + tcx: TyCtxt<'tcx>, + predicate1: ty::Predicate<'tcx>, + predicate2: ty::Predicate<'tcx>, + span: Span, +) -> bool { + let pred1_kind = predicate1.kind().skip_binder(); + let pred2_kind = predicate2.kind().skip_binder(); + let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { + ( + ty::PredicateKind::Clause(ty::Clause::Trait(pred1)), + ty::PredicateKind::Clause(ty::Clause::Trait(pred2)), + ) => (pred1, pred2), + // Just use plain syntactic equivalence if either of the predicates aren't + // trait predicates or have bound vars. + _ => return predicate1 == predicate2, + }; + + let predicates_equal_modulo_constness = { + let pred1_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; + let pred2_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; + pred1_unconsted == pred2_unconsted + }; + + if !predicates_equal_modulo_constness { + return false; + } + + // Check that the predicate on the specializing impl is at least as const as + // the one on the base. + match (trait_pred2.constness, trait_pred1.constness) { + (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => { + tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit(); + } + _ => {} + } + + true +} + +#[instrument(level = "debug", skip(tcx))] fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { - debug!("can_specialize_on(predicate = {:?})", predicate); match predicate.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateKind::Trait(ty::TraitPredicate { + ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { trait_ref, - constness: ty::BoundConstness::NotConst, + constness: _, polarity: _, - }) => { + })) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) @@ -407,7 +492,10 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc .emit(); } } - ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { + ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate { + projection_ty, + term, + })) => { tcx.sess .struct_span_err( span, @@ -428,12 +516,14 @@ fn trait_predicate_kind<'tcx>( predicate: ty::Predicate<'tcx>, ) -> Option<TraitSpecializationKind> { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { - Some(tcx.trait_def(trait_ref.def_id).specialization_kind) - } - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - | ty::PredicateKind::Projection(_) + ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + trait_ref, + constness: _, + polarity: _, + })) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::Projection(_)) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) @@ -441,6 +531,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 525cd2419..2058832d5 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -106,7 +106,7 @@ use rustc_middle::middle; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::util; -use rustc_session::config::EntryFnType; +use rustc_session::{config::EntryFnType, parse::feature_err}; use rustc_span::{symbol::sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -118,20 +118,40 @@ use astconv::AstConv; use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { - match (decl.c_variadic, abi) { - // The function has the correct calling convention, or isn't a "C-variadic" function. - (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {} - // The function is a "C-variadic" function with an incorrect calling convention. - (true, _) => { - let mut err = struct_span_err!( - tcx.sess, + const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention"; + const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`"; + const CONVENTIONS_STABLE: &str = "`C` or `cdecl`"; + const UNSTABLE_EXPLAIN: &str = + "using calling conventions other than `C` or `cdecl` for varargs functions is unstable"; + + if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) { + return; + } + + let extended_abi_support = tcx.features().extended_varargs_abi_support; + let conventions = match (extended_abi_support, abi.supports_varargs()) { + // User enabled additional ABI support for varargs and function ABI matches those ones. + (true, true) => return, + + // Using this ABI would be ok, if the feature for additional ABI support was enabled. + // Return CONVENTIONS_STABLE, because we want the other error to look the same. + (false, true) => { + feature_err( + &tcx.sess.parse_sess, + sym::extended_varargs_abi_support, span, - E0045, - "C-variadic function must have C or cdecl calling convention" - ); - err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + UNSTABLE_EXPLAIN, + ) + .emit(); + CONVENTIONS_STABLE } - } + + (false, false) => CONVENTIONS_STABLE, + (true, false) => CONVENTIONS_UNSTABLE, + }; + + let mut err = struct_span_err!(tcx.sess, span, E0045, "{}, like {}", ERROR_HEAD, conventions); + err.span_label(span, ERROR_HEAD).emit(); } fn require_same_types<'tcx>( @@ -153,7 +173,7 @@ fn require_same_types<'tcx>( match &errors[..] { [] => true, errors => { - infcx.err_ctxt().report_fulfillment_errors(errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(errors, None); false } } @@ -312,11 +332,11 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ObligationCauseCode::MainFunctionType, ); let ocx = traits::ObligationCtxt::new(&infcx); - let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty); + let norm_return_ty = ocx.normalize(&cause, param_env, return_ty); ocx.register_bound(cause, param_env, norm_return_ty, term_did); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); error = true; } // now we can take the return type of the given main function diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 7534482cc..663f1c49d 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -30,28 +30,30 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { // process predicates and convert to `RequiredPredicates` entry, see below for &(predicate, span) in predicates.predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::TypeOutlives(OutlivesPredicate(ty, reg)) => { - insert_outlives_predicate( - tcx, - ty.into(), - reg, - span, - &mut required_predicates, - ) - } + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(OutlivesPredicate( + ty, + reg, + ))) => insert_outlives_predicate( + tcx, + ty.into(), + reg, + span, + &mut required_predicates, + ), - ty::PredicateKind::RegionOutlives(OutlivesPredicate(reg1, reg2)) => { - insert_outlives_predicate( - tcx, - reg1.into(), - reg2, - span, - &mut required_predicates, - ) - } + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(OutlivesPredicate( + reg1, + reg2, + ))) => insert_outlives_predicate( + tcx, + reg1.into(), + reg2, + span, + &mut required_predicates, + ), - ty::PredicateKind::Trait(..) - | ty::PredicateKind::Projection(..) + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) @@ -59,6 +61,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => (), } } diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index e50c26765..81fe32000 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt}; +use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -17,12 +17,12 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers }; } -fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] { +fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Clause<'_>, Span)] { let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization() { - if tcx.hir().opt_const_param_default_param_hir_id(id).is_some() { + if tcx.hir().opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) @@ -50,10 +50,10 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate if tcx.has_attr(item_def_id, sym::rustc_outlives) { let mut pred: Vec<String> = predicates .iter() - .map(|(out_pred, _)| match out_pred.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(p) => p.to_string(), - ty::PredicateKind::TypeOutlives(p) => p.to_string(), - err => bug!("unexpected predicate {:?}", err), + .map(|(out_pred, _)| match out_pred { + ty::Clause::RegionOutlives(p) => p.to_string(), + ty::Clause::TypeOutlives(p) => p.to_string(), + err => bug!("unexpected clause {:?}", err), }) .collect(); pred.sort(); @@ -101,17 +101,11 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { |(ty::OutlivesPredicate(kind1, region2), &span)| { match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( - ty::Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(ty1, *region2), - )) - .to_predicate(tcx), + ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)), span, )), GenericArgKind::Lifetime(region1) => Some(( - ty::Binder::dummy(ty::PredicateKind::RegionOutlives( - ty::OutlivesPredicate(region1, *region2), - )) - .to_predicate(tcx), + ty::Clause::RegionOutlives(ty::OutlivesPredicate(region1, *region2)), span, )), GenericArgKind::Const(_) => { diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 435912464..4451db19f 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -296,25 +296,35 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { ) -> String { debug!(?path_hir_id); + // If there was already a lifetime among the arguments, just replicate that one. + if let Some(lt) = self.gen_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }) { + return std::iter::repeat(lt.to_string()) + .take(num_params_to_take) + .collect::<Vec<_>>() + .join(", "); + } + let mut ret = Vec::new(); + let mut ty_id = None; for (id, node) in self.tcx.hir().parent_iter(path_hir_id) { debug!(?id); - let params = if let Some(generics) = node.generics() { - generics.params - } else if let hir::Node::Ty(ty) = node - && let hir::TyKind::BareFn(bare_fn) = ty.kind - { - bare_fn.generic_params - } else { - &[] - }; - ret.extend(params.iter().filter_map(|p| { - let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } - = p.kind - else { return None }; - let hir::ParamName::Plain(name) = p.name else { return None }; - Some(name.to_string()) - })); + if let hir::Node::Ty(_) = node { + ty_id = Some(id); + } + + // Suggest `'_` when in function parameter or elided function return. + if let Some(fn_decl) = node.fn_decl() && let Some(ty_id) = ty_id { + let in_arg = fn_decl.inputs.iter().any(|t| t.hir_id == ty_id); + let in_ret = matches!(fn_decl.output, hir::FnRetTy::Return(ty) if ty.hir_id == ty_id); + + if in_arg || (in_ret && fn_decl.lifetime_elision_allowed) { + return std::iter::repeat("'_".to_owned()).take(num_params_to_take).collect::<Vec<_>>().join(", "); + } + } + // Suggest `'static` when in const/static item-like. if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. }, @@ -334,11 +344,29 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { }) | hir::Node::AnonConst(..) = node { - ret.extend( - std::iter::repeat("'static".to_owned()) - .take(num_params_to_take.saturating_sub(ret.len())), - ); + return std::iter::repeat("'static".to_owned()) + .take(num_params_to_take.saturating_sub(ret.len())) + .collect::<Vec<_>>() + .join(", "); } + + let params = if let Some(generics) = node.generics() { + generics.params + } else if let hir::Node::Ty(ty) = node + && let hir::TyKind::BareFn(bare_fn) = ty.kind + { + bare_fn.generic_params + } else { + &[] + }; + ret.extend(params.iter().filter_map(|p| { + let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } + = p.kind + else { return None }; + let hir::ParamName::Plain(name) = p.name else { return None }; + Some(name.to_string()) + })); + if ret.len() >= num_params_to_take { return ret[..num_params_to_take].join(", "); } @@ -728,7 +756,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { && let Some(trait_path_segment) = path.segments.get(0) { let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params(); - if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait { + if num_generic_args_supplied_to_trait + num_assoc_fn_excess_args == num_trait_generics_except_self + { if let Some(span) = self.gen_args.span_ext() && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let sugg = vec![ diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index eaf0310d5..6ce0c18bf 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -72,8 +72,8 @@ pub fn add_constraints_from_crate<'a, 'tcx>( let adt = tcx.adt_def(def_id); for variant in adt.variants() { - if let Some(ctor) = variant.ctor_def_id { - constraint_cx.build_constraints_for_item(ctor.expect_local()); + if let Some(ctor_def_id) = variant.ctor_def_id() { + constraint_cx.build_constraints_for_item(ctor_def_id.expect_local()); } } } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 82103c5a0..8b2719c2f 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -5,9 +5,11 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; +use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt}; +use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable}; +use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can /// allocate terms. @@ -50,6 +52,9 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { | DefKind::Union | DefKind::Variant | DefKind::Ctor(..) => {} + DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder => { + return variance_of_opaque(tcx, item_def_id.expect_local()); + } _ => { // Variance not relevant. span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item") @@ -61,3 +66,125 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { let crate_map = tcx.crate_variances(()); crate_map.variances.get(&item_def_id).copied().unwrap_or(&[]) } + +#[instrument(level = "trace", skip(tcx), ret)] +fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { + let generics = tcx.generics_of(item_def_id); + + // Opaque types may only use regions that are bound. So for + // ```rust + // type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b; + // ``` + // we may not use `'c` in the hidden type. + struct OpaqueTypeLifetimeCollector<'tcx> { + tcx: TyCtxt<'tcx>, + root_def_id: DefId, + variances: Vec<ty::Variance>, + } + + impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> { + #[instrument(level = "trace", skip(self), ret)] + fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> { + if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) { + let child_variances = self.tcx.variances_of(def_id); + for (a, v) in substs.iter().zip(child_variances) { + if *v != ty::Bivariant { + a.visit_with(self)?; + } + } + ControlFlow::CONTINUE + } else { + substs.visit_with(self) + } + } + } + + impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> { + #[instrument(level = "trace", skip(self), ret)] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() { + self.variances[ebr.index as usize] = ty::Invariant; + } + r.super_visit_with(self) + } + + #[instrument(level = "trace", skip(self), ret)] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + match t.kind() { + ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs), + ty::Projection(proj) + if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder => + { + self.visit_opaque(proj.item_def_id, proj.substs) + } + _ => t.super_visit_with(self), + } + } + } + + // By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt + // lifetime generics. + let mut variances: Vec<_> = std::iter::repeat(ty::Invariant).take(generics.count()).collect(); + + // Mark all lifetimes from parent generics as unused (Bivariant). + // This will be overridden later if required. + { + let mut generics = generics; + while let Some(def_id) = generics.parent { + generics = tcx.generics_of(def_id); + for param in &generics.params { + match param.kind { + ty::GenericParamDefKind::Lifetime => { + variances[param.index as usize] = ty::Bivariant; + } + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => {} + } + } + } + } + + let mut collector = + OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances }; + let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id()); + for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() { + let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs); + debug!(?pred); + + // We only ignore opaque type substs if the opaque type is the outermost type. + // The opaque type may be nested within itself via recursion in e.g. + // type Foo<'a> = impl PartialEq<Foo<'a>>; + // which thus mentions `'a` and should thus accept hidden types that borrow 'a + // instead of requiring an additional `+ 'a`. + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + trait_ref: ty::TraitRef { def_id: _, substs }, + constness: _, + polarity: _, + })) => { + for subst in &substs[1..] { + subst.visit_with(&mut collector); + } + } + ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { substs, item_def_id: _ }, + term, + })) => { + for subst in &substs[1..] { + subst.visit_with(&mut collector); + } + term.visit_with(&mut collector); + } + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + _, + region, + ))) => { + region.visit_with(&mut collector); + } + _ => { + pred.visit_with(&mut collector); + } + } + } + tcx.arena.alloc_from_iter(collector.variances.into_iter()) +} diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 1f763011e..3b286bb9c 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -42,22 +42,22 @@ impl<'a> fmt::Debug for VarianceTerm<'a> { } } -// The first pass over the crate simply builds up the set of inferreds. +/// The first pass over the crate simply builds up the set of inferreds. pub struct TermsContext<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, pub arena: &'a DroplessArena, - // For marker types, UnsafeCell, and other lang items where - // variance is hardcoded, records the item-id and the hardcoded - // variance. + /// For marker types, `UnsafeCell`, and other lang items where + /// variance is hardcoded, records the item-id and the hardcoded + /// variance. pub lang_items: Vec<(LocalDefId, Vec<ty::Variance>)>, - // Maps from the node id of an item to the first inferred index - // used for its type & region parameters. + /// Maps from the node id of an item to the first inferred index + /// used for its type & region parameters. pub inferred_starts: LocalDefIdMap<InferredIndex>, - // Maps from an InferredIndex to the term for that variable. + /// Maps from an InferredIndex to the term for that variable. pub inferred_terms: Vec<VarianceTermPtr<'a>>, } @@ -91,8 +91,8 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( let adt = tcx.adt_def(def_id); for variant in adt.variants() { - if let Some(ctor) = variant.ctor_def_id { - terms_cx.add_inferreds_for_item(ctor.expect_local()); + if let Some(ctor_def_id) = variant.ctor_def_id() { + terms_cx.add_inferreds_for_item(ctor_def_id.expect_local()); } } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index da27554a2..94bab9f33 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -398,7 +398,7 @@ impl<'a> State<'a> { } hir::ForeignItemKind::Static(t, m) => { self.head("static"); - if m == hir::Mutability::Mut { + if m.is_mut() { self.word_space("mut"); } self.print_ident(item.ident); @@ -519,7 +519,7 @@ impl<'a> State<'a> { } hir::ItemKind::Static(ty, m, expr) => { self.head("static"); - if m == hir::Mutability::Mut { + if m.is_mut() { self.word_space("mut"); } self.print_ident(item.ident); @@ -695,19 +695,8 @@ impl<'a> State<'a> { self.head("trait"); self.print_ident(item.ident); self.print_generic_params(generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - // FIXME(durka) this seems to be some quite outdated syntax - for b in bounds { - if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b); - } - } self.nbsp(); - self.print_bounds("=", real_bounds); + self.print_bounds("=", bounds); self.print_where_clause(generics); self.word(";"); self.end(); // end inner head-block @@ -754,7 +743,7 @@ impl<'a> State<'a> { for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(self.attrs(v.id)); + self.print_outer_attributes(self.attrs(v.hir_id)); self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); @@ -1480,7 +1469,9 @@ impl<'a> State<'a> { fn_decl, body, fn_decl_span: _, + fn_arg_span: _, movability: _, + def_id: _, }) => { self.print_closure_binder(binder, bound_generic_params); self.print_capture_clause(capture_clause); @@ -1590,7 +1581,7 @@ impl<'a> State<'a> { self.print_ident(Ident::with_dummy_span(name)) } - pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) { + pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) { self.maybe_print_comment(path.span.lo()); for (i, segment) in path.segments.iter().enumerate() { @@ -2158,7 +2149,7 @@ impl<'a> State<'a> { } pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) { - self.print_ident(lifetime.name.ident()) + self.print_ident(lifetime.ident) } pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) { diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 2b15d4dcd..e25a9e903 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,10 +1,10 @@ use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, MultiSpan}; +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, ToPredicate, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -137,55 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err| { - let Some(ret) = self - .tcx - .hir() - .find_by_def_id(self.body_id.owner.def_id) - .and_then(|owner| owner.fn_decl()) - .map(|decl| decl.output.span()) - else { return; }; - let Expectation::IsLast(stmt) = orig_expected else { - return - }; - let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { - Some(ret_coercion) if self.in_tail_expr => { - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - self.can_coerce(arm_ty, ret_ty) - && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) - // The match arms need to unify for the case of `impl Trait`. - && !matches!(ret_ty.kind(), ty::Opaque(..)) - } - _ => false, - }; - if !can_coerce_to_return_ty { - return; - } - - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); - let mut ret_span: MultiSpan = semi_span.into(); - ret_span.push_span_label( - expr.span, - "this could be implicitly returned but it is a statement, not a \ - tail expression", - ); - ret_span - .push_span_label(ret, "the `match` arms can conform to this return type"); - ret_span.push_span_label( - semi_span, - "the `match` is a statement because of this semicolon, consider \ - removing it", - ); - err.span_note( - ret_span, - "you might have meant to return the `match` expression", - ); - err.tool_only_span_suggestion( - semi_span, - "remove this semicolon", - "", - Applicability::MaybeIncorrect, - ); + self.suggest_removing_semicolon_for_coerce( + err, + expr, + orig_expected, + arm_ty, + prior_arm, + ) }), false, ); @@ -219,6 +177,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coercion.complete(self) } + fn suggest_removing_semicolon_for_coerce( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expectation: Expectation<'tcx>, + arm_ty: Ty<'tcx>, + prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>, + ) { + let hir = self.tcx.hir(); + + // First, check that we're actually in the tail of a function. + let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) = + hir.get(self.body_id) else { return; }; + let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. }) + = block.innermost_block().stmts.last() else { return; }; + if last_expr.hir_id != expr.hir_id { + return; + } + + // Next, make sure that we have no type expectation. + let Some(ret) = hir + .find_by_def_id(self.body_id.owner.def_id) + .and_then(|owner| owner.fn_decl()) + .map(|decl| decl.output.span()) else { return; }; + let Expectation::IsLast(stmt) = expectation else { + return; + }; + + let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { + Some(ret_coercion) => { + let ret_ty = ret_coercion.borrow().expected_ty(); + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); + self.can_coerce(arm_ty, ret_ty) + && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) + // The match arms need to unify for the case of `impl Trait`. + && !matches!(ret_ty.kind(), ty::Opaque(..)) + } + _ => false, + }; + if !can_coerce_to_return_ty { + return; + } + + let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); + let mut ret_span: MultiSpan = semi_span.into(); + ret_span.push_span_label( + expr.span, + "this could be implicitly returned but it is a statement, not a \ + tail expression", + ); + ret_span.push_span_label(ret, "the `match` arms can conform to this return type"); + ret_span.push_span_label( + semi_span, + "the `match` is a statement because of this semicolon, consider \ + removing it", + ); + diag.span_note(ret_span, "you might have meant to return the `match` expression"); + diag.tool_only_span_suggestion( + semi_span, + "remove this semicolon", + "", + Applicability::MaybeIncorrect, + ); + } + /// When the previously checked expression (the scrutinee) diverges, /// warn the user about the match arms being unreachable. fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { @@ -491,11 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. } = self.type_var_origin(expected)? else { return None; }; - let sig = *self - .typeck_results - .borrow() - .liberated_fn_sigs() - .get(hir::HirId::make_owner(self.body_id.owner.def_id))?; + let sig = self.body_fn_sig()?; let substs = sig.output().walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() @@ -519,23 +538,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .bound_explicit_item_bounds(rpit_def_id) .subst_iter_copied(self.tcx, substs) { - let pred = match pred.kind().skip_binder() { - ty::PredicateKind::Trait(mut trait_pred) => { + let pred = pred.kind().rebind(match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => { assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); - trait_pred.trait_ref.substs = - self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]); - pred.kind().rebind(trait_pred).to_predicate(self.tcx) + ty::PredicateKind::Clause(ty::Clause::Trait( + trait_pred.with_self_type(self.tcx, ty), + )) } - ty::PredicateKind::Projection(mut proj_pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => { assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty); - proj_pred.projection_ty.substs = self - .tcx - .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]); - pred.kind().rebind(proj_pred).to_predicate(self.tcx) + proj_pred.projection_ty.substs = self.tcx.mk_substs_trait( + ty, + proj_pred.projection_ty.substs.iter().skip(1), + ); + ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred)) } _ => continue, - }; + }); if !self.predicate_must_hold_modulo_regions(&Obligation::new( + self.tcx, ObligationCause::misc(span, self.body_id), self.param_env, pred, @@ -553,8 +574,5 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn arms_contain_ref_bindings<'tcx>(arms: &'tcx [hir::Arm<'tcx>]) -> Option<hir::Mutability> { - arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max_by_key(|m| match *m { - hir::Mutability::Mut => 1, - hir::Mutability::Not => 0, - }) + arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max() } diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 59c366ad7..41b52a4c4 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -12,18 +12,7 @@ use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span) - } - - /// Like `autoderef`, but provides a custom `Span` to use for calls to - /// an overloaded `Deref` operator - pub fn autoderef_overloaded_span( - &'a self, - span: Span, - base_ty: Ty<'tcx>, - overloaded_span: Span, - ) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span) + Autoderef::new(self, self.param_env, self.body_id, span, base_ty) } pub fn try_overloaded_deref( @@ -55,11 +44,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |InferOk { value: method, obligations: o }| { obligations.extend(o); if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { - region, - mutbl, - span: autoderef.overloaded_span(), - }) + Some(OverloadedDeref { region, mutbl, span: autoderef.span() }) } else { None } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 1b33f2f02..b09ddf80e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -6,7 +6,7 @@ use crate::type_error_struct; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey}; use rustc_hir as hir; -use rustc_hir::def::{self, Namespace, Res}; +use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_infer::{ infer, @@ -30,7 +30,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use std::iter; +use std::{iter, slice}; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -129,6 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { output } + #[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)] fn try_overloaded_call_step( &self, call_expr: &'tcx hir::Expr<'tcx>, @@ -138,10 +139,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<CallStep<'tcx>> { let adjusted_ty = self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", - call_expr, adjusted_ty - ); // If the callee is a bare function or a closure, then we're all set. match *adjusted_ty.kind() { @@ -182,12 +179,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hack: we know that there are traits implementing Fn for &F // where F:Fn and so forth. In the particular case of types - // like `x: &mut FnMut()`, if there is a call `x()`, we would - // normally translate to `FnMut::call_mut(&mut x, ())`, but - // that winds up requiring `mut x: &mut FnMut()`. A little - // over the top. The simplest fix by far is to just ignore - // this case and deref again, so we wind up with - // `FnMut::call_mut(&mut *x, ())`. + // like `f: &mut FnMut()`, if there is a call `f()`, we would + // normally translate to `FnMut::call_mut(&mut f, ())`, but + // that winds up potentially requiring the user to mark their + // variable as `mut` which feels unnecessary and unexpected. + // + // fn foo(f: &mut impl FnMut()) { f() } + // ^ without this hack `f` would have to be declared as mutable + // + // The simplest fix by far is to just ignore this case and deref again, + // so we wind up with `FnMut::call_mut(&mut *f, ())`. ty::Ref(..) if autoderef.step_count() == 0 => { return None; } @@ -230,22 +231,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ] { let Some(trait_def_id) = opt_trait_def_id else { continue }; - let opt_input_types = opt_arg_exprs.map(|arg_exprs| { - [self.tcx.mk_tup(arg_exprs.iter().map(|e| { + let opt_input_type = opt_arg_exprs.map(|arg_exprs| { + self.tcx.mk_tup(arg_exprs.iter().map(|e| { self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span: e.span, }) - }))] + })) }); - let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); if let Some(ok) = self.lookup_method_in_trait( call_expr.span, method_name, trait_def_id, adjusted_ty, - opt_input_types, + opt_input_type.as_ref().map(slice::from_ref), ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; @@ -261,15 +261,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], @@ -383,6 +379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { predicates.predicates.iter().zip(&predicates.spans) { let obligation = Obligation::new( + self.tcx, ObligationCause::dummy_with_span(callee_expr.span), self.param_env, *predicate, @@ -451,7 +448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // previously appeared within a `Binder<>` and hence would not // have been normalized before. let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); - let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); + let fn_sig = self.normalize(call_expr.span, fn_sig); // Call the generic checker. let expected_arg_tys = self.expected_inputs_for_expected_output( @@ -471,6 +468,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, ); + if fn_sig.abi == abi::Abi::RustCall { + let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); + if let Some(ty) = fn_sig.inputs().last().copied() { + self.register_bound( + ty, + self.tcx.require_lang_item(hir::LangItem::Tuple, Some(sp)), + traits::ObligationCause::new(sp, self.body_id, traits::RustCall), + ); + } else { + self.tcx.sess.span_err( + sp, + "functions with the \"rust-call\" ABI must take a single non-self tuple argument", + ); + } + } + fn_sig.output() } @@ -491,7 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // method lookup. let Ok(pick) = self .probe_for_name( - call_expr.span, Mode::MethodCall, segment.ident, IsSuggestion(true), @@ -582,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let mut unit_variant = None; if let hir::ExprKind::Path(qpath) = &callee_expr.kind - && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) + && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _) = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) // Only suggest removing parens if there are no arguments && arg_exprs.is_empty() diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index d1dab0540..890a068a7 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -33,6 +33,7 @@ use super::FnCtxt; use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; @@ -45,7 +46,6 @@ use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; /// Reifies a cast check to be checked once we have full type information for /// a function context. @@ -67,7 +67,7 @@ pub struct CastCheck<'tcx> { /// The kind of pointer and associated metadata (thin, length or vtable) - we /// only allow casts between fat pointers if their metadata have the same /// kind. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, @@ -76,11 +76,11 @@ enum PointerKind<'tcx> { /// Slice Length, /// The unsize info of this projection - OfProjection(&'tcx ty::ProjectionTy<'tcx>), + OfProjection(ty::ProjectionTy<'tcx>), /// The unsize info of this opaque ty OfOpaque(DefId, SubstsRef<'tcx>), /// The unsize info of this parameter - OfParam(&'tcx ty::ParamTy), + OfParam(ty::ParamTy), } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -94,10 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("pointer_kind({:?}, {:?})", t, span); let t = self.resolve_vars_if_possible(t); - - if let Some(reported) = t.error_reported() { - return Err(reported); - } + t.error_reported()?; if self.type_is_sized_modulo_regions(self.param_env, t, span) { return Ok(Some(PointerKind::Thin)); @@ -121,9 +118,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pointers to foreign types are thin, despite being unsized ty::Foreign(..) => Some(PointerKind::Thin), // We should really try to normalize here. - ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::Projection(pi) => Some(PointerKind::OfProjection(pi)), ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), - ty::Param(ref p) => Some(PointerKind::OfParam(p)), + ty::Param(p) => Some(PointerKind::OfParam(p)), // Insufficient type information. ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, @@ -222,8 +219,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // inference is more completely known. match cast_ty.kind() { ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - let reported = check.report_cast_to_unsized_type(fcx); - Err(reported) + Err(check.report_cast_to_unsized_type(fcx)) } _ => Ok(check), } @@ -501,10 +497,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { let ty = fcx.tcx.erase_regions(ty); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let expr_ty = fcx.tcx.erase_regions(expr_ty); - let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); if fcx .infcx - .type_implements_trait(from_trait, ty, ty_params, fcx.param_env) + .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env) .must_apply_modulo_regions() { label = false; @@ -614,10 +609,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) -> ErrorGuaranteed { - if let Some(reported) = - self.cast_ty.error_reported().or_else(|| self.expr_ty.error_reported()) - { - return reported; + if let Err(err) = self.cast_ty.error_reported() { + return err; + } + if let Err(err) = self.expr_ty.error_reported() { + return err; } let tstr = fcx.ty_to_string(self.cast_ty); @@ -730,9 +726,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!(" -> CoercionCast"); fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); } - Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { - self.report_object_unsafe_cast(&fcx, did); - } Err(_) => { match self.do_check(fcx) { Ok(k) => { @@ -744,14 +737,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; } } - - fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { - let violations = fcx.tcx.object_safety_violations(did); - let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); - err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); - err.emit(); - } - /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. @@ -767,10 +752,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { match *self.expr_ty.kind() { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. - let f = fcx.normalize_associated_types_in( - self.expr_span, - self.expr_ty.fn_sig(fcx.tcx), - ); + let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx)); let res = fcx.try_coerce( self.expr, self.expr_ty, @@ -912,7 +894,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } // vtable kinds must match - if cast_kind == expr_kind { + if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { Ok(CastKind::PtrPtrCast) } else { Err(CastError::DifferingKinds) @@ -954,7 +936,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { m_cast: ty::TypeAndMut<'tcx>, ) -> Result<CastKind, CastError> { // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const - if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not { + if m_expr.mutbl >= m_cast.mutbl { if let ty::Array(ety, _) = m_expr.ty.kind() { // Due to the limitations of LLVM global constants, // region pointers end up pointing at copies of diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 7f76364e1..32f86b804 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -1,18 +1,16 @@ use crate::coercion::CoerceMany; use crate::gather_locals::GatherLocalsVisitor; -use crate::{FnCtxt, Inherited}; -use crate::{GeneratorTypes, UnsafetyState}; +use crate::FnCtxt; +use crate::GeneratorTypes; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ImplicitSelfKind, ItemKind, Node}; use rustc_hir_analysis::check::fn_maybe_err; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use std::cell::RefCell; @@ -22,22 +20,16 @@ use std::cell::RefCell; /// /// * ... /// * inherited: other fields inherited from the enclosing fn (if any) -#[instrument(skip(inherited, body), level = "debug")] +#[instrument(skip(fcx, body), level = "debug")] pub(super) fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'tcx>, - param_env: ty::ParamEnv<'tcx>, + fcx: &mut FnCtxt<'a, 'tcx>, fn_sig: ty::FnSig<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, + fn_def_id: LocalDefId, body: &'tcx hir::Body<'tcx>, can_be_generator: Option<hir::Movability>, - return_type_pre_known: bool, -) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); - fcx.return_type_pre_known = return_type_pre_known; +) -> Option<GeneratorTypes<'tcx>> { + let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id); let tcx = fcx.tcx; let hir = tcx.hir(); @@ -49,11 +41,8 @@ pub(super) fn check_fn<'a, 'tcx>( declared_ret_ty, body.value.hir_id, decl.output.span(), - param_env, + fcx.param_env, )); - // If we replaced declared_ret_ty with infer vars, then we must be inferring - // an opaque type, so set a flag so we can improve diagnostics. - fcx.return_type_has_opaque = ret_ty != declared_ret_ty; fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); @@ -61,45 +50,15 @@ pub(super) fn check_fn<'a, 'tcx>( fn_maybe_err(tcx, span, fn_sig.abi); - if fn_sig.abi == Abi::RustCall { - let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 }; - - let err = || { - let item = match tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(header, ..), .. - }) => Some(header), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(header, ..), - .. - }) => Some(header), - // Closures are RustCall, but they tuple their arguments, so shouldn't be checked - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None, - node => bug!("Item being checked wasn't a function/closure: {:?}", node), - }; - - if let Some(header) = item { - tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple"); - } - }; - - if fn_sig.inputs().len() != expected_args { - err() + if let Some(kind) = body.generator_kind && can_be_generator.is_some() { + let yield_ty = if kind == hir::GeneratorKind::Gen { + let yield_ty = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); + fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + yield_ty } else { - // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on - // This will probably require wide-scale changes to support a TupleKind obligation - // We can't resolve this without knowing the type of the param - if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) { - err() - } - } - } - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + tcx.mk_unit() + }; // Resume type defaults to `()` if the generator has no argument. let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); @@ -140,10 +99,9 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.write_ty(param.hir_id, param_ty); } - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); + fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - fcx.in_tail_expr = true; - if let ty::Dynamic(..) = declared_ret_ty.kind() { + if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() { // FIXME: We need to verify that the return type is `Sized` after the return expression has // been evaluated so that we have types available for all the nodes being returned, but that // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this @@ -161,7 +119,6 @@ pub(super) fn check_fn<'a, 'tcx>( fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); fcx.check_return_expr(&body.value, false); } - fcx.in_tail_expr = false; // We insert the deferred_generator_interiors entry after visiting the body. // This ensures that all nested generators appear before the entry of this generator. @@ -211,14 +168,7 @@ pub(super) fn check_fn<'a, 'tcx>( check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); } - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() - && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() - { - check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - (fcx, gen_ty) + gen_ty } fn check_panic_info_fn( @@ -246,7 +196,7 @@ fn check_panic_info_fn( let arg_is_panic_info = match *inputs[0].kind() { ty::Ref(region, ty, mutbl) => match *ty.kind() { ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static() + adt.did() == panic_info_did && mutbl.is_not() && !region.is_static() } _ => false, }, @@ -273,52 +223,3 @@ fn check_panic_info_fn( tcx.sess.span_err(span, "should have no const parameters"); } } - -fn check_alloc_error_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else { - tcx.sess.err("language item required, but not found: `alloc_layout`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_alloc_layout = match inputs[0].kind() { - ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess - .span_err(span, "`#[alloc_error_handler]` function should have no const parameters"); - } -} diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index a5a45f75e..429cb60ba 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -4,24 +4,26 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use hir::def::DefKind; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use std::cmp; use std::iter; /// What signature do we *expect* the closure to have from context? -#[derive(Debug)] +#[derive(Debug, Clone, TypeFoldable, TypeVisitable)] struct ExpectedSig<'tcx> { /// Span that gave us this expectation, if we know that. cause_span: Option<Span>, @@ -33,24 +35,20 @@ struct ClosureSignatures<'tcx> { bound_sig: ty::PolyFnSig<'tcx>, /// The signature within the function body. /// This mostly differs in the sense that lifetimes are now early bound and any - /// opaque types from the signature expectation are overriden in case there are + /// opaque types from the signature expectation are overridden in case there are /// explicit hidden types written by the user in the closure signature. liberated_sig: ty::FnSig<'tcx>, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")] + #[instrument(skip(self, closure), level = "debug")] pub fn check_expr_closure( &self, - expr: &hir::Expr<'_>, - _capture: hir::CaptureBy, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - gen: Option<hir::Movability>, + closure: &hir::Closure<'tcx>, + expr_span: Span, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - trace!("expr = {:#?}", expr); + trace!("decl = {:#?}", closure.fn_decl); // It's always helpful for inference if we know the kind of // closure sooner rather than later, so first examine the expected @@ -59,42 +57,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ty) => self.deduce_expectations_from_expected_type(ty), None => (None, None), }; - let body = self.tcx.hir().body(body_id); - self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) + let body = self.tcx.hir().body(closure.body); + self.check_closure(closure, expr_span, expected_kind, body, expected_sig) } - #[instrument(skip(self, expr, body, decl), level = "debug", ret)] + #[instrument(skip(self, closure, body), level = "debug", ret)] fn check_closure( &self, - expr: &hir::Expr<'_>, + closure: &hir::Closure<'tcx>, + expr_span: Span, opt_kind: Option<ty::ClosureKind>, - decl: &'tcx hir::FnDecl<'tcx>, body: &'tcx hir::Body<'tcx>, - gen: Option<hir::Movability>, expected_sig: Option<ExpectedSig<'tcx>>, ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); + trace!("decl = {:#?}", closure.fn_decl); + let expr_def_id = closure.def_id; debug!(?expr_def_id); let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig); + self.sig_of_closure(expr_def_id, closure.fn_decl, body, expected_sig); debug!(?bound_sig, ?liberated_sig); - let return_type_pre_known = !liberated_sig.output().is_ty_infer(); - + let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id); let generator_types = check_fn( - self, - self.param_env.without_const(), + &mut fcx, liberated_sig, - decl, - expr.hir_id, + closure.fn_decl, + expr_def_id, body, - gen, - return_type_pre_known, - ) - .1; + closure.movability, + ); let parent_substs = InternalSubsts::identity_for_item( self.tcx, @@ -103,7 +96,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(expr.hir_id), + span: self.tcx.def_span(expr_def_id), }); if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types @@ -149,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => self.next_ty_var(TypeVariableOrigin { // FIXME(eddyb) distinguish closure kind inference variables from the rest. kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, + span: expr_span, }), }; @@ -174,34 +167,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { match *expected_ty.kind() { - ty::Opaque(def_id, substs) => { - let bounds = self.tcx.bound_explicit_item_bounds(def_id); - let sig = - bounds.subst_iter_copied(self.tcx, substs).find_map(|(pred, span)| match pred - .kind() - .skip_binder() - { - ty::PredicateKind::Projection(proj_predicate) => self - .deduce_sig_from_projection( - Some(span), - pred.kind().rebind(proj_predicate), - ), - _ => None, - }); - - let kind = bounds - .0 - .iter() - .filter_map(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tp) => { - self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) - } - _ => None, - }) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - trace!(?sig, ?kind); - (sig, kind) - } + ty::Opaque(def_id, substs) => self.deduce_signature_from_predicates( + self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs), + ), ty::Dynamic(ref object_type, ..) => { let sig = object_type.projection_bounds().find_map(|pb| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); @@ -209,10 +177,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let kind = object_type .principal_def_id() - .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); + .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did)); (sig, kind) } - ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), + ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates( + self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)), + ), ty::FnPtr(sig) => { let expected_sig = ExpectedSig { cause_span: None, sig }; (Some(expected_sig), Some(ty::ClosureKind::Fn)) @@ -221,37 +191,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn deduce_expectations_from_obligations( + fn deduce_signature_from_predicates( &self, - expected_vid: ty::TyVid, + predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>, ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { - let expected_sig = - self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| { - debug!(?obligation.predicate); - - let bound_predicate = obligation.predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = - obligation.predicate.kind().skip_binder() - { - // Given a Projection predicate, we can potentially infer - // the complete signature. + let mut expected_sig = None; + let mut expected_kind = None; + + for obligation in traits::elaborate_predicates_with_span( + self.tcx, + // Reverse the obligations here, since `elaborate_*` uses a stack, + // and we want to keep inference generally in the same order of + // the registered obligations. + predicates.rev(), + ) { + debug!(?obligation.predicate); + let bound_predicate = obligation.predicate.kind(); + + // Given a Projection predicate, we can potentially infer + // the complete signature. + if expected_sig.is_none() + && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder() + { + expected_sig = self.normalize( + obligation.cause.span, self.deduce_sig_from_projection( - Some(obligation.cause.span), + Some(obligation.cause.span), bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }); + ), + ); + } - // Even if we can't infer the full signature, we may be able to - // infer the kind. This can occur when we elaborate a predicate - // like `F : Fn<A>`. Note that due to subtyping we could encounter - // many viable options, so pick the most restrictive. - let expected_kind = self - .obligations_for_self_ty(expected_vid) - .filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id())) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur when we elaborate a predicate + // like `F : Fn<A>`. Note that due to subtyping we could encounter + // many viable options, so pick the most restrictive. + let trait_def_id = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + Some(data.projection_ty.trait_def_id(self.tcx)) + } + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => Some(data.def_id()), + _ => None, + }; + if let Some(closure_kind) = + trait_def_id.and_then(|def_id| self.tcx.fn_trait_kind_from_def_id(def_id)) + { + expected_kind = Some( + expected_kind + .map_or_else(|| closure_kind, |current| cmp::min(current, closure_kind)), + ); + } + } (expected_sig, expected_kind) } @@ -272,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_def_id = projection.trait_def_id(tcx); - let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some(); + let is_fn = tcx.is_fn_trait(trait_def_id); let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); let is_gen = gen_trait == trait_def_id; if !is_fn && !is_gen { @@ -323,30 +313,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: Option<ExpectedSig<'tcx>>, ) -> ClosureSignatures<'tcx> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e) + self.sig_of_closure_with_expectation(expr_def_id, decl, body, e) } else { - self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body) + self.sig_of_closure_no_expectation(expr_def_id, decl, body) } } /// If there is no expected signature, then we will convert the /// types that the user gave into a signature. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] + #[instrument(skip(self, expr_def_id, decl, body), level = "debug")] fn sig_of_closure_no_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, ) -> ClosureSignatures<'tcx> { - let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); + let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); self.closure_sigs(expr_def_id, body, bound_sig) } @@ -392,17 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// # Arguments /// - /// - `expr_def_id`: the `DefId` of the closure expression + /// - `expr_def_id`: the `LocalDefId` of the closure expression /// - `decl`: the HIR declaration of the closure /// - `body`: the body of the closure /// - `expected_sig`: the expected signature (if any). Note that /// this is missing a binder: that is, there may be late-bound /// regions with depth 1, which are bound then by the closure. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] + #[instrument(skip(self, expr_def_id, decl, body), level = "debug")] fn sig_of_closure_with_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: ExpectedSig<'tcx>, @@ -411,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expectation if things don't see to match up with what we // expect. if expected_sig.sig.c_variadic() != decl.c_variadic { - return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body); + return self.sig_of_closure_no_expectation(expr_def_id, decl, body); } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( expr_def_id, @@ -447,27 +434,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.merge_supplied_sig_with_expectation( - hir_id, - expr_def_id, - decl, - body, - closure_sigs, - ) { + match self.merge_supplied_sig_with_expectation(expr_def_id, decl, body, closure_sigs) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), + Err(_) => self.sig_of_closure_no_expectation(expr_def_id, decl, body), } } fn sig_of_closure_with_mismatched_number_of_arguments( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { let hir = self.tcx.hir(); - let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); + let expr_map_node = hir.get_by_def_id(expr_def_id); let expected_args: Vec<_> = expected_sig .sig .skip_binder() @@ -475,18 +456,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .map(|ty| ArgKind::from_expected_ty(*ty, None)) .collect(); - let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { - Some((sp, args)) => (Some(sp), args), - None => (None, Vec::new()), - }; + let (closure_span, closure_arg_span, found_args) = + match self.get_fn_like_arguments(expr_map_node) { + Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args), + None => (None, None, Vec::new()), + }; let expected_span = - expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); + expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); self.report_arg_count_mismatch( expected_span, closure_span, expected_args, found_args, true, + closure_arg_span, ) .emit(); @@ -498,11 +481,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Enforce the user's types against the expectation. See /// `sig_of_closure_with_expectation` for details on the overall /// strategy. - #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))] + #[instrument(level = "debug", skip(self, expr_def_id, decl, body, expected_sigs))] fn merge_supplied_sig_with_expectation( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, mut expected_sigs: ClosureSignatures<'tcx>, @@ -511,7 +493,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); + let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); debug!(?supplied_sig); @@ -591,8 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, decl, body), level = "debug", ret)] fn supplied_sig_of_closure( &self, - hir_id: hir::HirId, - expr_def_id: DefId, + expr_def_id: LocalDefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, ) -> ty::PolyFnSig<'tcx> { @@ -601,6 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trace!("decl = {:#?}", decl); debug!(?body.generator_kind); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(expr_def_id); let bound_vars = self.tcx.late_bound_vars(hir_id); // First, convert the types that the user supplied (if any). @@ -642,7 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Astconv can't normalize inputs or outputs with escaping bound vars, // so normalize them here, after we've wrapped them in a binder. - let result = self.normalize_associated_types_in(self.tcx.hir().span(hir_id), result); + let result = self.normalize(self.tcx.hir().span(hir_id), result); let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); @@ -659,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] fn deduce_future_output_from_obligations( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, body_id: hir::HirId, ) -> Option<Ty<'tcx>> { let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { @@ -677,7 +659,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = + bound_predicate.skip_binder() + { self.deduce_future_output_from_projection( span, bound_predicate.rebind(proj_predicate), @@ -689,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { + self.obligations_for_self_ty(ret_vid).find_map(|obligation| { get_future_output(obligation.predicate, obligation.cause.span) })? } @@ -808,17 +792,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn closure_sigs( &self, - expr_def_id: DefId, + expr_def_id: LocalDefId, body: &hir::Body<'_>, bound_sig: ty::PolyFnSig<'tcx>, ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig); - let liberated_sig = self.inh.normalize_associated_types_in( - body.value.span, - body.value.hir_id, - self.param_env, - liberated_sig, - ); + let liberated_sig = + self.tcx().liberate_late_bound_regions(expr_def_id.to_def_id(), bound_sig); + let liberated_sig = self.normalize(body.value.span, liberated_sig); ClosureSignatures { bound_sig, liberated_sig } } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 86597a703..f0b349f0c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,7 +46,7 @@ use rustc_hir::Expr; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; -use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -55,14 +55,16 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; +use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ + self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, +}; use smallvec::{smallvec, SmallVec}; use std::ops::Deref; @@ -108,11 +110,7 @@ fn coerce_mutbls<'tcx>( from_mutbl: hir::Mutability, to_mutbl: hir::Mutability, ) -> RelateResult<'tcx, ()> { - match (from_mutbl, to_mutbl) { - (hir::Mutability::Mut, hir::Mutability::Mut | hir::Mutability::Not) - | (hir::Mutability::Not, hir::Mutability::Not) => Ok(()), - (hir::Mutability::Not, hir::Mutability::Mut) => Err(TypeError::Mutability), - } + if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } } /// Do not require any adjustments, i.e. coerce `x -> x`. @@ -199,10 +197,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce: unsize successful"); return unsize; } - Err(TypeError::ObjectUnsafeCoercion(did)) => { - debug!("coerce: unsize not object safe"); - return Err(TypeError::ObjectUnsafeCoercion(did)); - } Err(error) => { debug!(?error, "coerce: unsize failed"); } @@ -277,13 +271,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { for &source_ty in &[a, b] { if source_ty != target_ty { obligations.push(Obligation::new( + self.tcx(), self.cause.clone(), self.param_env, ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { a: source_ty, b: target_ty, - })) - .to_predicate(self.tcx()), + })), )); } } @@ -456,7 +450,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(err); }; - if ty == a && mt_a.mutbl == hir::Mutability::Not && autoderef.step_count() == 1 { + if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { // As a special case, if we would produce `&'a *x`, that's // a total no-op. We end up with the type `&'a T` just as // we started with. In that case, just skip it @@ -468,7 +462,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // `self.x` both have `&mut `type would be a move of // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. - assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U + assert!(mutbl_b.is_not()); // can only coerce &T -> &U return success(vec![], ty, obligations); } @@ -482,12 +476,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let ty::Ref(r_borrow, _, _) = ty.kind() else { span_bug!(span, "expected a ref type, got {:?}", ty); }; - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => { - AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase } - } - }; + let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase); adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)), target: ty, @@ -507,27 +496,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { target = self.shallow_resolve(target); debug!(?source, ?target); - // These 'if' statements require some explanation. - // The `CoerceUnsized` trait is special - it is only - // possible to write `impl CoerceUnsized<B> for A` where - // A and B have 'matching' fields. This rules out the following - // two types of blanket impls: - // - // `impl<T> CoerceUnsized<T> for SomeType` - // `impl<T> CoerceUnsized<SomeType> for T` - // - // Both of these trigger a special `CoerceUnsized`-related error (E0376) - // - // We can take advantage of this fact to avoid performing unnecessary work. - // If either `source` or `target` is a type variable, then any applicable impl - // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`) - // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for - // SomeType`). - // - // However, these are exactly the kinds of impls which are forbidden by - // the compiler! Therefore, we can be sure that coercion will always fail - // when either the source or target type is a type variable. This allows us - // to skip performing any trait selection, and immediately bail out. + // We don't apply any coercions incase either the source or target + // aren't sufficiently well known but tend to instead just equate + // them both. if source.is_ty_var() { debug!("coerce_unsized: source is a TyVar, bailing out"); return Err(TypeError::Mismatch); @@ -556,15 +527,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let coercion = Coercion(self.cause.span); let r_borrow = self.next_region_var(coercion); - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // We don't allow two-phase borrows here, at least for initial - // implementation. If it happens that this coercion is a function argument, - // the reborrow in coerce_borrowed_ptr will pick it up. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + Some(( Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { @@ -588,7 +556,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } _ => None, }; - let coerce_source = reborrow.as_ref().map_or(source, |&(_, ref r)| r.target); + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); // Setup either a subtyping or a LUB relationship between // the `CoerceUnsized` target type and the expected type. @@ -629,8 +597,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { cause, coerce_unsized_did, 0, - coerce_source, - &[coerce_target.into()] + [coerce_source, coerce_target] )]; let mut has_unsized_tuple_coercion = false; @@ -645,7 +612,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { debug!("coerce_unsized resolve step: {:?}", obligation); let bound_predicate = obligation.predicate.kind(); let trait_pred = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_pred) if traits.contains(&trait_pred.def_id()) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + if traits.contains(&trait_pred.def_id()) => + { if unsize_did == trait_pred.def_id() { let self_ty = trait_pred.self_ty(); let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty(); @@ -668,7 +637,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { continue; } }; - match selcx.select(&obligation.with(trait_pred)) { + match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { if trait_pred.def_id() == unsize_did { @@ -705,12 +674,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Object safety violations or miscellaneous. Err(err) => { - self.err_ctxt().report_selection_error( - obligation.clone(), - &obligation, - &err, - false, - ); + self.err_ctxt().report_selection_error(obligation.clone(), &obligation, &err); // Treat this like an obligation and follow through // with the unsizing - the lack of a coercion should // be silent, as it causes a type mismatch later. @@ -752,7 +716,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { &self, a: Ty<'tcx>, b: Ty<'tcx>, - predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, b_region: ty::Region<'tcx>, ) -> CoerceResult<'tcx> { if !self.tcx.features().dyn_star { @@ -761,25 +725,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if let ty::Dynamic(a_data, _, _) = a.kind() && let ty::Dynamic(b_data, _, _) = b.kind() + && a_data.principal_def_id() == b_data.principal_def_id() { - if a_data.principal_def_id() == b_data.principal_def_id() { - return self.unify_and(a, b, |_| vec![]); - } else if !self.tcx().features().trait_upcasting { - let mut err = feature_err( - &self.tcx.sess.parse_sess, - sym::trait_upcasting, - self.cause.span, - &format!( - "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental" - ), - ); - err.emit(); - } + return self.unify_and(a, b, |_| vec![]); } // Check the obligations of the cast -- for example, when casting // `usize` to `dyn* Clone + 'static`: - let obligations = predicates + let mut obligations: Vec<_> = predicates .iter() .map(|predicate| { // For each existential predicate (e.g., `?Self: Clone`) substitute @@ -787,18 +740,31 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // and then require that the resulting predicate (e.g., `usize: Clone`) // holds (it does). let predicate = predicate.with_self_ty(self.tcx, a); - Obligation::new(self.cause.clone(), self.param_env, predicate) + Obligation::new(self.tcx, self.cause.clone(), self.param_env, predicate) }) - // Enforce the region bound (e.g., `usize: 'static`, in our example). - .chain([Obligation::new( - self.cause.clone(), - self.param_env, - self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(a, b_region), - ))), - )]) + .chain([ + // Enforce the region bound (e.g., `usize: 'static`, in our example). + Obligation::new( + self.tcx, + self.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives( + ty::OutlivesPredicate(a, b_region), + ))), + ), + ]) .collect(); + // Enforce that the type is `usize`/pointer-sized. + obligations.push(Obligation::new( + self.tcx, + self.cause.clone(), + self.param_env, + ty::Binder::dummy( + self.tcx.at(self.cause.span).mk_trait_ref(hir::LangItem::PointerSized, [a]), + ), + )); + Ok(InferOk { value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), obligations, @@ -868,7 +834,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); let InferOk { value: b, mut obligations } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, b); + self.at(&self.cause, self.param_env).normalize(b); debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); match b.kind() { @@ -890,7 +856,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } let InferOk { value: a_sig, obligations: o1 } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, a_sig); + self.at(&self.cause, self.param_env).normalize(a_sig); obligations.extend(o1); let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig); @@ -1043,9 +1009,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Ok(ok) = coerce.coerce(source, target) else { return false; }; - let mut fcx = traits::FulfillmentContext::new_in_snapshot(); - fcx.register_predicate_obligations(self, ok.obligations); - fcx.select_where_possible(&self).is_empty() + let ocx = ObligationCtxt::new_in_snapshot(self); + ocx.register_obligations(ok.obligations); + ocx.select_where_possible().is_empty() }) } @@ -1072,8 +1038,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.infcx .type_implements_trait( self.tcx.lang_items().deref_mut_trait()?, - expr_ty, - ty::List::empty(), + [expr_ty], self.param_env, ) .may_apply() @@ -1116,15 +1081,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Special-case that coercion alone cannot handle: // Function items or non-capturing closures of differing IDs or InternalSubsts. let (a_sig, b_sig) = { - #[allow(rustc::usage_of_ty_tykind)] - let is_capturing_closure = |ty: &ty::TyKind<'tcx>| { - if let &ty::Closure(closure_def_id, _substs) = ty { + let is_capturing_closure = |ty: Ty<'tcx>| { + if let &ty::Closure(closure_def_id, _substs) = ty.kind() { self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() } else { false } }; - if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) { + if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { (None, None) } else { match (prev_ty.kind(), new_ty.kind()) { @@ -1179,8 +1143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(TypeError::IntrinsicCast); } // The signature must match. - let a_sig = self.normalize_associated_types_in(new.span, a_sig); - let b_sig = self.normalize_associated_types_in(new.span, b_sig); + let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self .at(cause, self.param_env) .trace(prev_ty, new_ty) @@ -1547,7 +1510,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Mark that we've failed to coerce the types here to suppress // any superfluous errors we might encounter while trying to // emit or provide suggestions on how to fix the initial error. - fcx.set_tainted_by_errors(); + fcx.set_tainted_by_errors( + fcx.tcx.sess.delay_span_bug(cause.span, "coercion error but no error emitted"), + ); let (expected, found) = if label_expression_as_expected { // In the case where this is a "forced unit", like // `break`, we want to call the `()` "expected" @@ -1644,9 +1609,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); } - err.emit_unless(unsized_return); + let reported = err.emit_unless(unsized_return); - self.final_ty = Some(fcx.tcx.ty_error()); + self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported)); } } } @@ -1782,7 +1747,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // may occur at the first return expression we see in the closure // (if it conflicts with the declared return type). Skip adding a // note in this case, since it would be incorrect. - && !fcx.return_type_pre_known + && let Some(fn_sig) = fcx.body_fn_sig() + && fn_sig.output().is_ty_var() { err.span_note( sp, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 16febfc46..24184bdbf 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -2,6 +2,7 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::CtorKind; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_infer::infer::InferOk; @@ -18,6 +19,7 @@ use rustc_trait_selection::traits::ObligationCause; use super::method::probe; +use std::cmp::min; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -30,6 +32,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, error: Option<TypeError<'tcx>>, ) { + if expr_ty == expected { + return; + } + self.annotate_expected_due_to_let_ty(err, expr, error); // Use `||` to give these suggestions a precedence @@ -42,15 +48,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) - || self.suggest_into(err, expr, expr_ty, expected); + || self.suggest_into(err, expr, expr_ty, expected) + || self.suggest_option_to_bool(err, expr, expr_ty, expected) + || self.suggest_floating_point_literal(err, expr, expected); self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); } - // Requires that the two types unify, and prints an error message if - // they don't. + /// Requires that the two types unify, and prints an error message if + /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) { e.emit(); @@ -148,7 +156,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) => e, }; - self.set_tainted_by_errors(); + self.set_tainted_by_errors(self.tcx.sess.delay_span_bug( + expr.span, + "`TypeError` when attempting coercion but no error emitted", + )); let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); @@ -395,27 +406,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(path) = variant_path.strip_prefix("std::prelude::") && let Some((_, path)) = path.split_once("::") { - return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)); + return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy)); } - Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)) + Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy)) } else { None } }) .collect(); - let suggestions_for = |variant: &_, ctor, field_name| { + let suggestions_for = |variant: &_, ctor_kind, field_name| { let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { Some(ident) => format!("{ident}: "), None => String::new(), }; - let (open, close) = match ctor { - hir::def::CtorKind::Fn => ("(".to_owned(), ")"), - hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"), + let (open, close) = match ctor_kind { + Some(CtorKind::Fn) => ("(".to_owned(), ")"), + None => (format!(" {{ {field_name}: "), " }"), // unit variants don't have fields - hir::def::CtorKind::Const => unreachable!(), + Some(CtorKind::Const) => unreachable!(), }; // Suggest constructor as deep into the block tree as possible. @@ -845,31 +856,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { - if mutability == hir::Mutability::Mut { + if mutability.is_mut() { // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` return None; } } let sugg_expr = if needs_parens { format!("({src})") } else { src }; - return Some(match mutability { - hir::Mutability::Mut => ( - sp, - "consider mutably borrowing here".to_string(), - format!("{prefix}&mut {sugg_expr}"), - Applicability::MachineApplicable, - false, - false, - ), - hir::Mutability::Not => ( - sp, - "consider borrowing here".to_string(), - format!("{prefix}&{sugg_expr}"), - Applicability::MachineApplicable, - false, - false, - ), - }); + return Some(( + sp, + format!("consider {}borrowing here", mutability.mutably_str()), + format!("{prefix}{}{sugg_expr}", mutability.ref_prefix_str()), + Applicability::MachineApplicable, + false, + false, + )); } } } @@ -927,51 +928,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Ok(src) = sm.span_to_snippet(sp) { let derefs = "*".repeat(steps); - if let Some((span, src, applicability)) = match mutbl_b { - hir::Mutability::Mut => { - let new_prefix = "&mut ".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(5); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - ( - sp, - format!("mut {derefs}"), - Applicability::Unspecified, - ) - }) - } - } - } - hir::Mutability::Not => { - let new_prefix = "&".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let lo = sp.lo() + BytePos(1); - let hi = sp.lo() + BytePos(5); - let sp = sp.with_lo(lo).with_hi(hi); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - } - } - } { + let old_prefix = mutbl_a.ref_prefix_str(); + let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs; + + let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| { + // skip `&` or `&mut ` if both mutabilities are mutable + let lo = sp.lo() + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _); + // skip `&` or `&mut ` + let hi = sp.lo() + BytePos(old_prefix.len() as _); + let sp = sp.with_lo(lo).with_hi(hi); + + ( + sp, + format!("{}{derefs}", if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }), + if mutbl_b <= mutbl_a { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect } + ) + }); + + if let Some((span, src, applicability)) = suggestion { return Some(( span, "consider dereferencing".to_string(), @@ -995,10 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the expression has `&`, removing it would fix the error prefix_span = prefix_span.with_hi(inner.span.lo()); expr = inner; - remove += match mutbl { - hir::Mutability::Not => "&", - hir::Mutability::Mut => "&mut ", - }; + remove.push_str(mutbl.ref_prefix_str()); steps -= 1; } else { break; diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 175037f9b..507272fde 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -1,10 +1,11 @@ -//! Errors emitted by `rustc_hir_analysis`. +//! Errors emitted by `rustc_hir_typeck`. +use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span}; #[derive(Diagnostic)] -#[diag(hir_analysis_field_multiply_specified_in_initializer, code = "E0062")] +#[diag(hir_typeck_field_multiply_specified_in_initializer, code = "E0062")] pub struct FieldMultiplySpecifiedInInitializer { #[primary_span] #[label] @@ -15,7 +16,7 @@ pub struct FieldMultiplySpecifiedInInitializer { } #[derive(Diagnostic)] -#[diag(hir_analysis_return_stmt_outside_of_fn_body, code = "E0572")] +#[diag(hir_typeck_return_stmt_outside_of_fn_body, code = "E0572")] pub struct ReturnStmtOutsideOfFnBody { #[primary_span] pub span: Span, @@ -26,14 +27,14 @@ pub struct ReturnStmtOutsideOfFnBody { } #[derive(Diagnostic)] -#[diag(hir_analysis_yield_expr_outside_of_generator, code = "E0627")] +#[diag(hir_typeck_yield_expr_outside_of_generator, code = "E0627")] pub struct YieldExprOutsideOfGenerator { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_struct_expr_non_exhaustive, code = "E0639")] +#[diag(hir_typeck_struct_expr_non_exhaustive, code = "E0639")] pub struct StructExprNonExhaustive { #[primary_span] pub span: Span, @@ -41,21 +42,21 @@ pub struct StructExprNonExhaustive { } #[derive(Diagnostic)] -#[diag(hir_analysis_method_call_on_unknown_type, code = "E0699")] +#[diag(hir_typeck_method_call_on_unknown_type, code = "E0699")] pub struct MethodCallOnUnknownType { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_functional_record_update_on_non_struct, code = "E0436")] +#[diag(hir_typeck_functional_record_update_on_non_struct, code = "E0436")] pub struct FunctionalRecordUpdateOnNonStruct { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(hir_analysis_address_of_temporary_taken, code = "E0745")] +#[diag(hir_typeck_address_of_temporary_taken, code = "E0745")] pub struct AddressOfTemporaryTaken { #[primary_span] #[label] @@ -65,7 +66,7 @@ pub struct AddressOfTemporaryTaken { #[derive(Subdiagnostic)] pub enum AddReturnTypeSuggestion { #[suggestion( - hir_analysis_add_return_type_add, + hir_typeck_add_return_type_add, code = "-> {found} ", applicability = "machine-applicable" )] @@ -75,7 +76,7 @@ pub enum AddReturnTypeSuggestion { found: String, }, #[suggestion( - hir_analysis_add_return_type_missing_here, + hir_typeck_add_return_type_missing_here, code = "-> _ ", applicability = "has-placeholders" )] @@ -87,12 +88,12 @@ pub enum AddReturnTypeSuggestion { #[derive(Subdiagnostic)] pub enum ExpectedReturnTypeLabel<'tcx> { - #[label(hir_analysis_expected_default_return_type)] + #[label(hir_typeck_expected_default_return_type)] Unit { #[primary_span] span: Span, }, - #[label(hir_analysis_expected_return_type)] + #[label(hir_typeck_expected_return_type)] Other { #[primary_span] span: Span, @@ -101,10 +102,10 @@ pub enum ExpectedReturnTypeLabel<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_missing_parentheses_in_range, code = "E0689")] +#[diag(hir_typeck_missing_parentheses_in_range, code = "E0689")] pub struct MissingParentheseInRange { #[primary_span] - #[label(hir_analysis_missing_parentheses_in_range)] + #[label(hir_typeck_missing_parentheses_in_range)] pub span: Span, pub ty_str: String, pub method_name: String, @@ -113,8 +114,9 @@ pub struct MissingParentheseInRange { } #[derive(Subdiagnostic)] -#[multipart_suggestion_verbose( - hir_analysis_add_missing_parentheses_in_range, +#[multipart_suggestion( + hir_typeck_add_missing_parentheses_in_range, + style = "verbose", applicability = "maybe-incorrect" )] pub struct AddMissingParenthesesInRange { @@ -124,3 +126,49 @@ pub struct AddMissingParenthesesInRange { #[suggestion_part(code = ")")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_op_trait_generic_params)] +pub struct OpMethodGenericParams { + #[primary_span] + pub span: Span, + pub method_name: String, +} + +pub struct TypeMismatchFruTypo { + /// Span of the LHS of the range + pub expr_span: Span, + /// Span of the `..RHS` part of the range + pub fru_span: Span, + /// Rendered expression of the RHS of the range + pub expr: Option<String>, +} + +impl AddToDiagnostic for TypeMismatchFruTypo { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("expr", self.expr.as_deref().unwrap_or("NONE")); + + // Only explain that `a ..b` is a range if it's split up + if self.expr_span.between(self.fru_span).is_empty() { + diag.span_note( + self.expr_span.to(self.fru_span), + rustc_errors::fluent::hir_typeck_fru_note, + ); + } else { + let mut multispan: MultiSpan = vec![self.expr_span, self.fru_span].into(); + multispan.push_span_label(self.expr_span, rustc_errors::fluent::hir_typeck_fru_expr); + multispan.push_span_label(self.fru_span, rustc_errors::fluent::hir_typeck_fru_expr2); + diag.span_note(multispan, rustc_errors::fluent::hir_typeck_fru_note); + } + + diag.span_suggestion( + self.expr_span.shrink_to_hi(), + rustc_errors::fluent::hir_typeck_fru_suggestion, + ", ", + Applicability::MaybeIncorrect, + ); + } +} diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index e9e810344..4f086cf59 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -79,9 +79,9 @@ impl<'a, 'tcx> Expectation<'tcx> { } } - // Resolves `expected` by a single level if it is a variable. If - // there is no expected type or resolution is not possible (e.g., - // no constraints yet present), just returns `self`. + /// Resolves `expected` by a single level if it is a variable. If + /// there is no expected type or resolution is not possible (e.g., + /// no constraints yet present), just returns `self`. fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { match self { NoExpectation => NoExpectation, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 9fde62a81..ed87b94a0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -5,6 +5,7 @@ use crate::cast; use crate::coercion::CoerceMany; use crate::coercion::DynamicCoerceMany; +use crate::errors::TypeMismatchFruTypo; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, @@ -30,7 +31,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Closure, ExprKind, HirId, QPath}; +use rustc_hir::{ExprKind, HirId, QPath}; use rustc_hir_analysis::astconv::AstConv as _; use rustc_hir_analysis::check::ty_kind_suggestion; use rustc_infer::infer; @@ -80,14 +81,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // coercions from ! to `expected`. if ty.is_never() { if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - self.tcx().sess.delay_span_bug( + let reported = self.tcx().sess.delay_span_bug( expr.span, "expression with never type wound up being adjusted", ); return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { target.to_owned() } else { - self.tcx().ty_error() + self.tcx().ty_error_with_guaranteed(reported) }; } @@ -103,8 +104,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); + // FIXME(compiler-errors): We probably should fold some of the + // `suggest_` functions from `emit_coerce_suggestions` into here, + // since some of those aren't necessarily just coerce suggestions. + let _ = self.suggest_deref_ref_or_into( + &mut err, + expr.peel_drop_temps(), + expected_ty, + ty, + None, + ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty); extend_err(&mut err); err.emit(); } @@ -220,7 +229,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hide the outer diverging and has_errors flags. let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); let ty = ensure_sufficient_stack(|| match &expr.kind { hir::ExprKind::Path( @@ -259,7 +267,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and has_error flags. self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); debug!("... {:?}, expected is {:?}", ty, expected); @@ -318,9 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Match(discrim, arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) } - ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { - self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) - } + ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected), ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), ExprKind::MethodCall(segment, receiver, args, _) => { @@ -398,8 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } - err.emit(); - oprnd_t = tcx.ty_error(); + oprnd_t = tcx.ty_error_with_guaranteed(err.emit()); } } hir::UnOp::Not => { @@ -524,12 +528,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); let ty = match res { Res::Err => { - self.set_tainted_by_errors(); - tcx.ty_error() + self.suggest_assoc_method_call(segs); + let e = + self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + tcx.ty_error_with_guaranteed(e) } - Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, qpath, expr.span); - tcx.ty_error() + Res::Def(DefKind::Variant, _) => { + let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, "E0533", "value"); + tcx.ty_error_with_guaranteed(e) } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; @@ -840,10 +847,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_expr_ty, ); - if self.return_type_has_opaque { + if let Some(fn_sig) = self.body_fn_sig() + && fn_sig.output().has_opaque_types() + { // Point any obligations that were registered due to opaque type // inference at the return expression. - self.select_obligations_where_possible(false, |errors| { + self.select_obligations_where_possible(|errors| { self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); }); } @@ -906,8 +915,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Check if an expression `original_expr_id` comes from the condition of a while loop, - // as opposed from the body of a while loop, which we can naively check by iterating - // parents until we find a loop... + /// as opposed from the body of a while loop, which we can naively check by iterating + /// parents until we find a loop... pub(super) fn comes_from_while_condition( &self, original_expr_id: HirId, @@ -1097,12 +1106,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the assignment expression itself is ill-formed, don't // bother emitting another error - if lhs_ty.references_error() || rhs_ty.references_error() { - err.delay_as_bug() - } else { - err.emit(); - } - return self.tcx.ty_error(); + let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); + return self.tcx.ty_error_with_guaranteed(reported); } let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); @@ -1114,9 +1119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let lhs_deref_ty_is_sized = self .infcx .type_implements_trait( - self.tcx.lang_items().sized_trait().unwrap(), - lhs_deref_ty, - ty::List::empty(), + self.tcx.require_lang_item(LangItem::Sized, None), + [lhs_deref_ty], self.param_env, ) .may_apply(); @@ -1613,10 +1617,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No); if let Some(mut diag) = diag { - if idx == ast_fields.len() - 1 && remaining_fields.is_empty() { - self.suggest_fru_from_range(field, variant, substs, &mut diag); + if idx == ast_fields.len() - 1 { + if remaining_fields.is_empty() { + self.suggest_fru_from_range(field, variant, substs, &mut diag); + diag.emit(); + } else { + diag.stash(field.span, StashKey::MaybeFruTypo); + } + } else { + diag.emit(); } - diag.emit(); } } @@ -1637,6 +1647,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. if error_happened { + if let Some(base_expr) = base_expr { + self.check_expr(base_expr); + } return; } @@ -1655,7 +1668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .map(|f| { - let fru_ty = self.normalize_associated_types_in( + let fru_ty = self.normalize( expr_span, self.field_ty(base_expr.span, f, fresh_substs), ); @@ -1739,9 +1752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(adt, substs) if adt.is_struct() => variant .fields .iter() - .map(|f| { - self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) - }) + .map(|f| self.normalize(expr_span, f.ty(self.tcx, substs))) .collect(), _ => { self.tcx @@ -1874,19 +1885,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|adt| adt.did()) != range_def_id { - let instead = self + // Suppress any range expr type mismatches + if let Some(mut diag) = self + .tcx + .sess + .diagnostic() + .steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo) + { + diag.delay_as_bug(); + } + + // Use a (somewhat arbitrary) filtering heuristic to avoid printing + // expressions that are either too long, or have control character + //such as newlines in them. + let expr = self .tcx .sess .source_map() .span_to_snippet(range_end.expr.span) - .map(|s| format!(" from `{s}`")) - .unwrap_or_default(); - err.span_suggestion( - range_start.span.shrink_to_hi(), - &format!("to set the remaining fields{instead}, separate the last named field with a comma"), - ",", - Applicability::MaybeIncorrect, - ); + .ok() + .filter(|s| s.len() < 25 && !s.contains(|c: char| c.is_control())); + + let fru_span = self + .tcx + .sess + .source_map() + .span_extend_while(range_start.span, |c| c.is_whitespace()) + .unwrap_or(range_start.span).shrink_to_hi().to(range_end.span); + + err.subdiagnostic(TypeMismatchFruTypo { + expr_span: range_start.span, + fru_span, + expr, + }); } } @@ -1961,7 +1992,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: Span, ) { if variant.is_recovered() { - self.set_tainted_by_errors(); + self.set_tainted_by_errors( + self.tcx + .sess + .delay_span_bug(expr_span, "parser recovered but no error was emitted"), + ); return; } let mut err = self.err_ctxt().type_error_struct_with_diag( @@ -1991,8 +2026,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind { - CtorKind::Fn => match ty.kind() { + match variant.ctor_kind() { + Some(CtorKind::Fn) => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( variant_ident_span, @@ -2300,12 +2335,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, ) { - let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty), - _ => return, - }; + let Some(output_ty) = self.get_impl_future_output_ty(ty) else { return; }; let mut add_label = true; - if let ty::Adt(def, _) = output_ty.skip_binder().kind() { + if let ty::Adt(def, _) = output_ty.kind() { // no field access on enum type if !def.is_enum() { if def @@ -2738,7 +2770,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((index_ty, element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); - self.select_obligations_where_possible(false, |errors| { + self.select_obligations_where_possible(|errors| { self.point_at_index_if_possible(errors, idx.span) }); element_ty @@ -2777,8 +2809,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } } } @@ -2791,7 +2823,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { for error in errors { match error.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(predicate) + ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { } _ => continue, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index fce2a5888..03b174c77 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -252,11 +252,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(discr)); - self.maybe_read_scrutinee( + return_if_err!(self.maybe_read_scrutinee( discr, discr_place.clone(), arms.iter().map(|arm| arm.pat), - ); + )); // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -352,8 +352,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_expr(base); } - hir::ExprKind::Closure { .. } => { - self.walk_captures(expr); + hir::ExprKind::Closure(closure) => { + self.walk_captures(closure); } hir::ExprKind::Box(ref base) => { @@ -390,7 +390,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { discr: &Expr<'_>, discr_place: PlaceWithHirId<'tcx>, pats: impl Iterator<Item = &'t hir::Pat<'t>>, - ) { + ) -> Result<(), ()> { // Matching should not always be considered a use of the place, hence // discr does not necessarily need to be borrowed. // We only want to borrow discr if the pattern contain something other @@ -398,7 +398,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; let mut needs_to_be_read = false; for pat in pats { - return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { + mc.cat_pattern(discr_place.clone(), pat, |place, pat| { match &pat.kind { PatKind::Binding(.., opt_sub_pat) => { // If the opt_sub_pat is None, than the binding does not count as @@ -453,7 +453,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // examined } } - })); + })? } if needs_to_be_read { @@ -474,6 +474,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // that the discriminant has been initialized. self.walk_expr(discr); } + Ok(()) } fn walk_local<F>( @@ -490,7 +491,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { f(self); if let Some(els) = els { // borrowing because we need to test the discriminant - self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); + return_if_err!(self.maybe_read_scrutinee( + expr, + expr_place.clone(), + from_ref(pat).iter() + )); self.walk_block(els) } self.walk_irrefutable_pat(&expr_place, &pat); @@ -518,6 +523,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // Consume the expressions supplying values for each field. for field in fields { self.consume_expr(field.expr); + + // The struct path probably didn't resolve + if self.mc.typeck_results.opt_field_index(field.hir_id).is_none() { + self.tcx().sess.delay_span_bug(field.span, "couldn't resolve index for field"); + } } let with_expr = match *opt_with { @@ -535,9 +545,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { - let is_mentioned = fields.iter().any(|f| { - self.tcx().field_index(f.hir_id, self.mc.typeck_results) == f_index - }); + let is_mentioned = fields + .iter() + .any(|f| self.mc.typeck_results.opt_field_index(f.hir_id) == Some(f_index)); if !is_mentioned { let field_place = self.mc.cat_projection( &*with_expr, @@ -745,7 +755,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing /// closure as the DefId. - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { + fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) { fn upvar_is_local_variable<'tcx>( upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, upvar_id: hir::HirId, @@ -757,7 +767,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("walk_captures({:?})", closure_expr); let tcx = self.tcx(); - let closure_def_id = tcx.hir().local_def_id(closure_expr.hir_id); + let closure_def_id = closure_expr.def_id; let upvars = tcx.upvars_mentioned(self.body_owner); // For purposes of this function, generator and closures are equivalent. @@ -829,10 +839,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // be a local variable PlaceBase::Local(*var_hir_id) }; + let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id); let place_with_id = PlaceWithHirId::new( - capture_info.path_expr_id.unwrap_or( - capture_info.capture_kind_expr_id.unwrap_or(closure_expr.hir_id), - ), + capture_info + .path_expr_id + .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)), place.base_ty, place_base, place.projections.clone(), diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 747ecb036..ac6b0924a 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -7,16 +7,16 @@ use rustc_data_structures::{ use rustc_middle::ty::{self, Ty}; impl<'tcx> FnCtxt<'_, 'tcx> { - /// Performs type inference fallback, returning true if any fallback - /// occurs. - pub(super) fn type_inference_fallback(&self) -> bool { + /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred` + /// if fallback has occurred. + pub(super) fn type_inference_fallback(&self) { debug!( "type-inference-fallback start obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations() ); // All type checking constraints were added, try to fallback unsolved variables. - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); debug!( "type-inference-fallback post selection obligations: {:#?}", @@ -26,18 +26,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Check if we have any unsolved variables. If not, no need for fallback. let unsolved_variables = self.unsolved_variables(); if unsolved_variables.is_empty() { - return false; + return; } let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables); - let mut fallback_has_occurred = false; // We do fallback in two passes, to try to generate // better error messages. // The first time, we do *not* replace opaque types. for ty in unsolved_variables { debug!("unsolved_variable = {:?}", ty); - fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback); + self.fallback_if_possible(ty, &diverging_fallback); } // We now see if we can make progress. This might cause us to @@ -63,9 +62,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // If we had tried to fallback the opaque inference variable to `MyType`, // we will generate a confusing type-check error that does not explicitly // refer to opaque types. - self.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - fallback_has_occurred + self.select_obligations_where_possible(|_| {}); } // Tries to apply a fallback to `ty` if it is an unsolved variable. @@ -81,12 +78,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // Fallback becomes very dubious if we have encountered // type-checking errors. In that case, fallback to Error. // - // The return value indicates whether fallback has occurred. + // Sets `FnCtxt::fallback_has_occurred` if fallback is performed + // during this call. fn fallback_if_possible( &self, ty: Ty<'tcx>, diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>, - ) -> bool { + ) { // Careful: we do NOT shallow-resolve `ty`. We know that `ty` // is an unsolved variable, and we determine its fallback // based solely on how it was created, not what other type @@ -106,12 +104,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // type, `?T` is not considered unsolved, but `?I` is. The // same is true for float variables.) let fallback = match ty.kind() { - _ if self.is_tainted_by_errors() => self.tcx.ty_error(), + _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, _ => match diverging_fallback.get(&ty) { Some(&fallback_ty) => fallback_ty, - None => return false, + None => return, }, }; debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); @@ -122,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .map(|origin| origin.span) .unwrap_or(rustc_span::DUMMY_SP); self.demand_eqtype(span, ty, fallback); - true + self.fallback_has_occurred.set(true); } /// The "diverging fallback" system is rather complicated. This is diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6a1cffe3e..952d27262 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -16,13 +16,13 @@ use rustc_hir_analysis::astconv::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::{InferOk, InferResult}; +use rustc_infer::infer::InferResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, - ToPredicate, Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType, }; use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; @@ -30,11 +30,8 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; +use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; use std::collections::hash_map::Entry; use std::slice; @@ -106,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // possible. This can help substantially when there are // indirect dependencies that don't seem worth tracking // precisely. - self.select_obligations_where_possible(false, mutate_fulfillment_errors); + self.select_obligations_where_possible(mutate_fulfillment_errors); self.resolve_vars_if_possible(ty) } @@ -142,9 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); + if let Err(e) = ty.error_reported() { + self.set_tainted_by_errors(e); } } @@ -346,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs); let value = EarlyBinder(value).subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, value); + let result = self.normalize(span, value); debug!("instantiate_type_scheme = {:?}", result); result } @@ -362,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bounds = self.tcx.predicates_of(def_id); let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, result); + let result = self.normalize(span, result); debug!( "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", bounds, substs, result, spans, @@ -370,50 +366,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result, spans) } - pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - ) -> InferOk<'tcx, T> + pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T where T: TypeFoldable<'tcx>, { - self.inh.partially_normalize_associated_types_in( - ObligationCause::misc(span, self.body_id), - self.param_env, - value, - ) - } - - pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - opt_input_expr: Option<&hir::Expr<'_>>, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in( - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_ty: None, - }, - ), - self.param_env, - value, + self.register_infer_ok_obligations( + self.at(&self.misc(span), self.param_env).normalize(value), ) } @@ -490,11 +448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), hir::ArrayLen::Body(anon_const) => { - let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let span = self.tcx.hir().span(anon_const.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); + let span = self.tcx.def_span(anon_const.def_id); + let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id); self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize_associated_types_in(span, c) + self.normalize(span, c) } } } @@ -504,10 +461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_c: &hir::AnonConst, param_def_id: DefId, ) -> ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; + let const_def = + ty::WithOptConstParam { did: ast_c.def_id, const_param_did: Some(param_def_id) }; let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); self.register_wf_obligation( c.into(), @@ -534,7 +489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), None => { bug!( "no type for node {}: {} in fcx {}", @@ -549,7 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), - None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()), + None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error_with_guaranteed(e)), None => None, } } @@ -564,9 +519,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // WF obligations never themselves fail, so no real need to give a detailed cause: let cause = traits::ObligationCause::new(span, self.body_id, code); self.register_predicate(traits::Obligation::new( + self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), )); } @@ -588,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: &'tcx ty::FieldDef, substs: SubstsRef<'tcx>, ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) + self.normalize(span, field.ty(self.tcx, substs)) } pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { @@ -601,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } @@ -612,25 +568,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); } } /// Select as many obligations as we can at present. pub(in super::super) fn select_obligations_where_possible( &self, - fallback_has_occurred: bool, mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) { let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors( - &result, - self.inh.body_id, - fallback_has_occurred, - ); + self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); } } @@ -650,12 +601,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + let self_ty = self.shallow_resolve(self_ty); debug!(?self_ty); match *self_ty.kind() { @@ -674,54 +621,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn obligations_for_self_ty<'b>( &'b self, self_ty: ty::TyVid, - ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { + ) -> impl DoubleEndedIterator<Item = traits::PredicateObligation<'tcx>> + Captures<'tcx> + 'b + { // FIXME: consider using `sub_root_var` here so we // can see through subtyping. let ty_var_root = self.root_var(self_ty); trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - let bound_predicate = obligation.predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(data) => Some(( - bound_predicate.rebind(data).required_poly_trait_ref(self.tcx), - obligation, - )), - ty::PredicateKind::Trait(data) => { - Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateKind::Subtype(..) => None, - ty::PredicateKind::Coerce(..) => None, - ty::PredicateKind::RegionOutlives(..) => None, - ty::PredicateKind::TypeOutlives(..) => None, - ty::PredicateKind::WellFormed(..) => None, - ty::PredicateKind::ObjectSafe(..) => None, - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of an unresolved - // inference variable. - ty::PredicateKind::ClosureKind(..) => None, - ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map( + move |obligation| match &obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) + if self.self_type_matches_expected_vid( + data.projection_ty.self_ty(), + ty_var_root, + ) => + { + Some(obligation) } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) => + { + Some(obligation) + } + + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of an unresolved + // inference variable. + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + }, + ) } pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + let sized_did = self.tcx.lang_items().sized_trait(); + self.obligations_for_self_ty(self_ty).any(|obligation| { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + Some(data.def_id()) == sized_did + } + _ => false, + } + }) } pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { @@ -769,34 +726,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expect_args = self .fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new_in_snapshot(self); + // Attempt to apply a subtyping relationship between the formal // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - let errors = self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }); - - if !errors.is_empty() { - return Err(()); - } - } - Err(_) => return Err(()), + ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); } // Record all the argument types, with the substitutions @@ -1132,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Local(hid) = res { let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); self.write_ty(hir_id, ty); return (ty, res); } @@ -1173,9 +1112,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { explicit_late_bound = ExplicitLateBound::Yes; } - if let Err(GenericArgCountMismatch { reported: Some(_), .. }) = arg_count.correct { + if let Err(GenericArgCountMismatch { reported: Some(e), .. }) = arg_count.correct { infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. + self.set_tainted_by_errors(e); // See issue #53251. } } @@ -1189,11 +1128,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *ty.kind() { ty::Adt(adt_def, substs) if adt_def.has_ctor() => { let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) + let (ctor_kind, ctor_def_id) = variant.ctor.unwrap(); + (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs)) } _ => { let mut err = tcx.sess.struct_span_err( @@ -1215,9 +1151,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - - return (tcx.ty_error(), res); + let reported = err.emit(); + return (tcx.ty_error_with_guaranteed(reported), res); } } } else { @@ -1466,12 +1401,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.is_ty_var() { ty } else { - if !self.is_tainted_by_errors() { + let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) - .emit(); - } - let err = self.tcx.ty_error(); + .emit() + }); + let err = self.tcx.ty_error_with_guaranteed(e); self.demand_suptype(sp, err, ty); err } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8e0fcb56c..60fec05d3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -73,7 +73,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); if ty.has_non_region_infer() { - assert!(self.is_tainted_by_errors()); self.tcx.ty_error() } else { self.tcx.erase_regions(ty) @@ -136,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, Some(method.def_id), ); + method.sig.output() } @@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit" ) - .emit(); + .delay_as_bug(); (self.err_args(provided_args.len()), None) } } @@ -344,7 +344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // an "opportunistic" trait resolution of any trait bounds on // the call. This helps coercions. if check_closures { - self.select_obligations_where_possible(false, |_| {}) + self.select_obligations_where_possible(|_| {}) } // Check each argument, to satisfy the input it was provided for @@ -511,8 +511,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) } - self.set_tainted_by_errors(); let tcx = self.tcx; + // FIXME: taint after emitting errors and pass through an `ErrorGuaranteed` + self.set_tainted_by_errors( + tcx.sess.delay_span_bug(call_span, "no errors reported for args"), + ); // Get the argument span in the context of the call span so that // suggestions and labels are (more) correct when an arg is a @@ -596,6 +599,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), true, mismatched_ty, provided_ty) + }; + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -660,10 +675,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. err = self.err_ctxt().report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, + mk_trace( + *lo, + formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -747,9 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; @@ -773,8 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, @@ -846,8 +857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); if let Some(e) = error { self.err_ctxt().note_type_err( &mut err, @@ -1200,7 +1210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let variant = match def { Res::Err => { - self.set_tainted_by_errors(); + self.set_tainted_by_errors( + self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"), + ); return None; } Res::Def(DefKind::Variant, _) => match ty.kind() { @@ -1334,7 +1346,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hide the outer diverging and `has_errors` flags. let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); match stmt.kind { hir::StmtKind::Local(l) => { @@ -1364,7 +1375,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and `has_error` flags. self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); } pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { @@ -1383,8 +1393,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let prev = self.ps.replace(self.ps.get().recurse(blk)); - // In some cases, blocks have just one exit, but other blocks // can be targeted by multiple breaks. This can happen both // with labeled blocks as well as when we desugar @@ -1544,15 +1552,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(prev_diverges); } - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } + let ty = ctxt.coerce.unwrap().complete(self); self.write_ty(blk.hir_id, ty); - self.ps.set(prev); ty } @@ -1728,22 +1731,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; - // Skip over mentioning async lang item - if Some(def_id) == self.tcx.lang_items().from_generator_fn() - && error.obligation.cause.span.desugaring_kind() - == Some(rustc_span::DesugaringKind::Async) - { - return false; - } - let Some(unsubstituted_pred) = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) else { return false; }; let generics = self.tcx.generics_of(def_id); let predicate_substs = match unsubstituted_pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => pred.trait_ref.substs, - ty::PredicateKind::Projection(pred) => pred.projection_ty.substs, + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => pred.projection_ty.substs, _ => ty::List::empty(), }; @@ -1920,7 +1915,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { receiver: Option<&'tcx hir::Expr<'tcx>>, args: &'tcx [hir::Expr<'tcx>], ) -> bool { - let sig = self.tcx.fn_sig(def_id).skip_binder(); + let ty = self.tcx.type_of(def_id); + if !ty.is_fn() { + return false; + } + let sig = ty.fn_sig(self.tcx).skip_binder(); let args_referencing_param: Vec<_> = sig .inputs() .iter() @@ -2091,7 +2090,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) + && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) && let Some(callee_ty) = callee_ty { let callee_ty = callee_ty.peel_refs(); @@ -2115,9 +2114,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (predicate, span) in std::iter::zip(instantiated.predicates, instantiated.spans) { - if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder() && pred.self_ty().peel_refs() == callee_ty - && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() + && self.tcx.is_fn_trait(pred.def_id()) { err.span_note(span, "callable defined here"); return; @@ -2148,13 +2147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); let obligation = traits::Obligation::new( + self.tcx, traits::ObligationCause::dummy(), self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), + ty::Binder::dummy(trait_ref), ); match SelectionContext::new(&self).select(&obligation) { Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c600daf4..30b59da78 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -4,10 +4,11 @@ mod checks; mod suggestions; pub use _impl::*; +use rustc_errors::ErrorGuaranteed; pub use suggestions::*; use crate::coercion::DynamicCoerceMany; -use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; +use crate::{Diverges, EnclosingBreakables, Inherited}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir_analysis::astconv::AstConv; @@ -21,7 +22,7 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; use std::ops::Deref; @@ -68,17 +69,11 @@ pub struct FnCtxt<'a, 'tcx> { /// any). pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell<Option<Span>>, pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - pub(super) ps: Cell<UnsafetyState>, - /// Whether the last checked node generates a divergence (e.g., /// `return` will set this to `Always`). In general, when entering /// an expression or other node in the tree, the initial value @@ -112,21 +107,11 @@ pub struct FnCtxt<'a, 'tcx> { /// the diverges flag is set to something other than `Maybe`. pub(super) diverges: Cell<Diverges>, - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell<bool>, - pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, pub(super) inh: &'a Inherited<'tcx>, - /// True if the function or closure's return type is known before - /// entering the function/closure, i.e. if the return type is - /// either given explicitly or inferred from, say, an `Fn*` trait - /// bound. Used for diagnostic purposes only. - pub(super) return_type_pre_known: bool, - - /// True if the return type has an Opaque type - pub(super) return_type_has_opaque: bool, + pub(super) fallback_has_occurred: Cell<bool>, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -140,19 +125,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env, err_count_on_creation: inh.tcx.sess.err_count(), ret_coercion: None, - in_tail_expr: false, ret_coercion_span: Cell::new(None), resume_yield_tys: None, - ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), enclosing_breakables: RefCell::new(EnclosingBreakables { stack: Vec::new(), by_id: Default::default(), }), inh, - return_type_pre_known: true, - return_type_has_opaque: false, + fallback_has_occurred: Cell::new(false), } } @@ -174,7 +155,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { - TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) } + TypeErrCtxt { + infcx: &self.infcx, + typeck_results: Some(self.typeck_results.borrow()), + fallback_has_occurred: self.fallback_has_occurred.get(), + normalize_fn_sig: Box::new(|fn_sig| { + if fn_sig.has_escaping_bound_vars() { + return fn_sig; + } + self.probe(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self); + let normalized_fn_sig = + ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig); + if ocx.select_all_or_error().is_empty() { + let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig); + if !normalized_fn_sig.needs_infer() { + return normalized_fn_sig; + } + } + fn_sig + }) + }), + } } pub fn errors_reported_since_creation(&self) -> bool { @@ -194,8 +196,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.tcx } - fn item_def_id(&self) -> Option<DefId> { - None + fn item_def_id(&self) -> DefId { + self.body_id.owner.to_def_id() } fn get_type_parameter_bounds( @@ -213,7 +215,9 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { predicates: tcx.arena.alloc_from_iter( self.param_env.caller_bounds().iter().filter_map(|predicate| { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if data.self_ty().is_param(index) => + { // HACK(eddyb) should get the original `Span`. let span = tcx.def_span(def_id); Some((predicate, span)) @@ -298,12 +302,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { if ty.has_escaping_bound_vars() { ty // FIXME: normalization and escaping regions } else { - self.normalize_associated_types_in(span, ty) + self.normalize(span, ty) } } - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.infcx.set_tainted_by_errors(e) } fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4db9c56f9..4f92477b5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,24 +10,35 @@ use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::NormalizeExt; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> { + self.typeck_results + .borrow() + .liberated_fn_sigs() + .get(self.tcx.hir().get_parent_node(self.body_id)) + .copied() + } + pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { + // This suggestion is incorrect for + // fn foo() -> bool { match () { () => true } || match () { () => true } } err.span_suggestion_short( span.shrink_to_hi(), "consider using a semicolon here", ";", - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } @@ -165,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Opaque(def_id, substs) => { self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be substs[1] && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() @@ -200,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Param(param) => { let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() && proj.projection_ty.self_ty() == found // args tuple will always be substs[1] @@ -237,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implied by wf, but also because that would possibly result in // erroneous errors later on. let infer::InferOk { value: output, obligations: _ } = - self.normalize_associated_types_in_as_infer_ok(expr.span, output); + self.at(&self.misc(expr.span), self.param_env).normalize(output); if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } @@ -337,8 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if annotation { let suggest_annotation = match expr.peel_drop_temps().kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(), _ => return true, }; let mut tuple_indexes = Vec::new(); @@ -366,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let annotation_span = ty.span; err.span_suggestion( annotation_span.with_hi(annotation_span.lo()), - format!("alternatively, consider changing the type annotation"), + "alternatively, consider changing the type annotation", suggest_annotation, Applicability::MaybeIncorrect, ); @@ -456,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ref_cnt += 1; } if let ty::Adt(adt, _) = peeled.kind() - && self.tcx.is_diagnostic_item(sym::String, adt.did()) + && Some(adt.did()) == self.tcx.lang_items().string() { err.span_suggestion_verbose( expr.span.shrink_to_hi(), @@ -752,7 +762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("suggest_missing_return_type: expected type {:?}", ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = Binder::bind_with_vars(ty, bound_vars); - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); if self.can_coerce(expected, ty) { err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); @@ -913,22 +923,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); - let ty = self.normalize_associated_types_in(expr.span, ty); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => { - let infcx = self.tcx.infer_ctxt().build(); - infcx - .get_impl_future_output_ty(ty) - .unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }) - .skip_binder() - } + hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!(fn_decl.output.span(), "failed to get output type of async function") + }), hir::IsAsync::NotAsync => ty, }; + let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", @@ -1082,14 +1083,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + self.tcx, self.misc(expr.span), self.param_env, - ty::Binder::dummy(ty::TraitRef { - def_id: into_def_id, - substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), - }) - .to_poly_trait_predicate() - .to_predicate(self.tcx), + ty::Binder::dummy(self.tcx.mk_trait_ref( + into_def_id, + [expr_ty, expected_ty] + )), )) { let sugg = if expr.precedence().order() >= PREC_POSTFIX { @@ -1108,6 +1108,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` + pub(crate) fn suggest_option_to_bool( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_bool() { + return false; + } + + let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; }; + if !self.tcx.is_diagnostic_item(sym::Option, def.did()) { + return false; + } + + let hir = self.tcx.hir(); + let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| { + matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And) + }).next(); + // Don't suggest: + // `let Some(_) = a.is_some() && b` + // ++++++++++ + // since the user probably just misunderstood how `let else` + // and `&&` work together. + if let Some((_, hir::Node::Local(local))) = cond_parent + && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind + && let hir::QPath::Resolved(None, path) = qpath + && let Some(did) = path.res.opt_def_id() + .and_then(|did| self.tcx.opt_parent(did)) + .and_then(|did| self.tcx.opt_parent(did)) + && self.tcx.is_diagnostic_item(sym::Option, did) + { + return false; + } + + diag.span_suggestion( + expr.span.shrink_to_hi(), + "use `Option::is_some` to test if the `Option` has a value", + ".is_some()", + Applicability::MachineApplicable, + ); + + true + } + /// Suggest wrapping the block in square brackets instead of curly braces /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( @@ -1149,6 +1196,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(skip(self, err))] + pub(crate) fn suggest_floating_point_literal( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_floating_point() { + return false; + } + match expr.kind { + ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { + err.span_suggestion_verbose( + start.span.shrink_to_hi().with_hi(end.span.lo()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + err.span_suggestion_verbose( + expr.span.with_lo(start.span.hi()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + err.span_suggestion_verbose( + expr.span.until(end.span), + "remove the unnecessary `.` operator and add an integer part for a floating point literal", + "0.", + Applicability::MaybeIncorrect, + ); + true + } + _ => false, + } + } + fn is_loop(&self, id: hir::HirId) -> bool { let node = self.tcx.hir().get(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index 122ad7009..fd8ea1ad7 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -9,9 +9,10 @@ use hir::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::vec::IndexVec; +use rustc_infer::infer::InferCtxt; use rustc_middle::{ hir::map::Map, - ty::{TyCtxt, TypeckResults}, + ty::{ParamEnv, TyCtxt, TypeVisitable, TypeckResults}, }; use std::mem::swap; @@ -21,20 +22,29 @@ use std::mem::swap; /// The resulting structure still needs to be iterated to a fixed point, which /// can be done with propagate_to_fixpoint in cfg_propagate. pub(super) fn build_control_flow_graph<'tcx>( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, typeck_results: &TypeckResults<'tcx>, + param_env: ParamEnv<'tcx>, consumed_borrowed_places: ConsumedAndBorrowedPlaces, body: &'tcx Body<'tcx>, num_exprs: usize, ) -> (DropRangesBuilder, FxHashSet<HirId>) { - let mut drop_range_visitor = - DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs); + let mut drop_range_visitor = DropRangeVisitor::new( + infcx, + typeck_results, + param_env, + consumed_borrowed_places, + num_exprs, + ); intravisit::walk_body(&mut drop_range_visitor, body); drop_range_visitor.drop_ranges.process_deferred_edges(); - if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { - super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); + if let Some(filename) = &infcx.tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { + super::cfg_visualize::write_graph_to_file( + &drop_range_visitor.drop_ranges, + filename, + infcx.tcx, + ); } (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries) @@ -82,40 +92,44 @@ pub(super) fn build_control_flow_graph<'tcx>( /// ``` struct DropRangeVisitor<'a, 'tcx> { - hir: Map<'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + infcx: &'a InferCtxt<'tcx>, + param_env: ParamEnv<'tcx>, places: ConsumedAndBorrowedPlaces, drop_ranges: DropRangesBuilder, expr_index: PostOrderId, - tcx: TyCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>, } impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { fn new( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, typeck_results: &'a TypeckResults<'tcx>, + param_env: ParamEnv<'tcx>, places: ConsumedAndBorrowedPlaces, num_exprs: usize, ) -> Self { debug!("consumed_places: {:?}", places.consumed); let drop_ranges = DropRangesBuilder::new( places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()), - hir, + infcx.tcx.hir(), num_exprs, ); Self { - hir, + infcx, + typeck_results, + param_env, places, drop_ranges, expr_index: PostOrderId::from_u32(0), - typeck_results, - tcx, label_stack: vec![], } } + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + fn record_drop(&mut self, value: TrackedValue) { if self.places.borrowed.contains(&value) { debug!("not marking {:?} as dropped because it is borrowed at some point", value); @@ -137,7 +151,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { .map_or(vec![], |places| places.iter().cloned().collect()); for place in places { trace!(?place, "consuming place"); - for_each_consumable(self.hir, place, |value| self.record_drop(value)); + for_each_consumable(self.tcx().hir(), place, |value| self.record_drop(value)); } } @@ -214,10 +228,15 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { /// return. fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { let ty = self.typeck_results.expr_ty(expr); - let ty = self.tcx.erase_regions(ty); - let m = self.tcx.parent_module(expr.hir_id).to_def_id(); - let param_env = self.tcx.param_env(m.expect_local()); - if self.tcx.is_ty_uninhabited_from(m, ty, param_env) { + let ty = self.infcx.resolve_vars_if_possible(ty); + if ty.has_non_region_infer() { + self.tcx() + .sess + .delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`")); + } + let ty = self.tcx().erase_regions(ty); + let m = self.tcx().parent_module(expr.hir_id).to_def_id(); + if !ty.is_inhabited_from(self.tcx(), m, self.param_env) { // This function will not return. We model this fact as an infinite loop. self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1); } @@ -238,7 +257,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { destination: hir::Destination, ) -> Result<HirId, LoopIdError> { destination.target_id.map(|target| { - let node = self.hir.get(target); + let node = self.tcx().hir().get(target); match node { hir::Node::Expr(_) => target, hir::Node::Block(b) => find_last_block_expression(b), diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs index 4f3bdfbe7..2abcadcc9 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -43,9 +43,9 @@ pub fn compute_drop_ranges<'a, 'tcx>( let typeck_results = &fcx.typeck_results.borrow(); let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( - fcx.tcx.hir(), - fcx.tcx, + &fcx, typeck_results, + fcx.param_env, consumed_borrowed_places, body, num_exprs, diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index b7dd599cd..3b1518ff7 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -13,10 +13,13 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; +use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::sym; use rustc_span::Span; +use smallvec::{smallvec, SmallVec}; mod drop_ranges; @@ -95,8 +98,8 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { expr, scope, ty, self.expr_count, yield_data.span ); - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) + if let Some((unresolved_term, unresolved_type_span)) = + self.fcx.first_unresolved_const_or_ty_var(&ty) { // If unresolved type isn't a ty_var then unresolved_type_span is None let span = self @@ -105,21 +108,22 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { // If we encounter an int/float variable, then inference fallback didn't // finish due to some other error. Don't emit spurious additional errors. - if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = - unresolved_type.kind() + if let Some(unresolved_ty) = unresolved_term.ty() + && let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = unresolved_ty.kind() { self.fcx .tcx .sess - .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); + .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_term)); } else { let note = format!( "the type is part of the {} because of this {}", - self.kind, yield_data.source + self.kind.descr(), + yield_data.source ); self.fcx - .need_type_info_err_in_generator(self.kind, span, unresolved_type) + .need_type_info_err_in_generator(self.kind, span, unresolved_term) .span_note(yield_data.span, &*note) .emit(); } @@ -159,7 +163,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { expr.map(|e| e.span) ); if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) + self.fcx.first_unresolved_const_or_ty_var(&ty) { debug!( "remained unresolved_type = {:?}, unresolved_type_span: {:?}", @@ -211,31 +215,57 @@ pub fn resolve_interior<'a, 'tcx>( debug!("types in generator {:?}, span = {:?}", types, body.value.span); - let mut counter = 0; + // We want to deduplicate if the lifetimes are the same modulo some non-informative counter. + // So, we need to actually do two passes: first by type to anonymize (preserving information + // required for diagnostics), then a second pass over all captured types to reassign disjoint + // region indices. let mut captured_tys = FxHashSet::default(); let type_causes: Vec<_> = types .into_iter() .filter_map(|mut cause| { - // Erase regions and canonicalize late-bound regions to deduplicate as many types as we - // can. - let ty = fcx.normalize_associated_types_in(cause.span, cause.ty); - let erased = fcx.tcx.erase_regions(ty); - if captured_tys.insert(erased) { - // Replace all regions inside the generator interior with late bound regions. - // Note that each region slot in the types gets a new fresh late bound region, - // which means that none of the regions inside relate to any other, even if - // typeck had previously found constraints that would cause them to be related. - let folded = fcx.tcx.fold_regions(erased, |_, current_depth| { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(counter), - }; - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); - counter += 1; - r - }); - - cause.ty = folded; + // Replace all regions inside the generator interior with late bound regions. + // Note that each region slot in the types gets a new fresh late bound region, + // which means that none of the regions inside relate to any other, even if + // typeck had previously found constraints that would cause them to be related. + + let mut counter = 0; + let mut mk_bound_region = |span| { + let kind = ty::BrAnon(counter, span); + let var = ty::BoundVar::from_u32(counter); + counter += 1; + ty::BoundRegion { var, kind } + }; + let ty = fcx.normalize(cause.span, cause.ty); + let ty = fcx.tcx.fold_regions(ty, |region, current_depth| { + let br = match region.kind() { + ty::ReVar(vid) => { + let origin = fcx.region_var_origin(vid); + match origin { + RegionVariableOrigin::EarlyBoundRegion(span, _) => { + mk_bound_region(Some(span)) + } + _ => mk_bound_region(None), + } + } + // FIXME: these should use `BrNamed` + ty::ReEarlyBound(region) => { + mk_bound_region(Some(fcx.tcx.def_span(region.def_id))) + } + ty::ReLateBound(_, ty::BoundRegion { kind, .. }) + | ty::ReFree(ty::FreeRegion { bound_region: kind, .. }) => match kind { + ty::BoundRegionKind::BrAnon(_, span) => mk_bound_region(span), + ty::BoundRegionKind::BrNamed(def_id, _) => { + mk_bound_region(Some(fcx.tcx.def_span(def_id))) + } + ty::BoundRegionKind::BrEnv => mk_bound_region(None), + }, + _ => mk_bound_region(None), + }; + let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); + r + }); + if captured_tys.insert(ty) { + cause.ty = ty; Some(cause) } else { None @@ -243,11 +273,38 @@ pub fn resolve_interior<'a, 'tcx>( }) .collect(); + let mut bound_vars: SmallVec<[BoundVariableKind; 4]> = smallvec![]; + let mut counter = 0; + // Optimization: If there is only one captured type, then we don't actually + // need to fold and reindex (since the first type doesn't change). + let type_causes = if captured_tys.len() > 0 { + // Optimization: Use `replace_escaping_bound_vars_uncached` instead of + // `fold_regions`, since we only have late bound regions, and it skips + // types without bound regions. + fcx.tcx.replace_escaping_bound_vars_uncached( + type_causes, + FnMutDelegate { + regions: &mut |br| { + let kind = match br.kind { + ty::BrAnon(_, span) => ty::BrAnon(counter, span), + _ => br.kind, + }; + let var = ty::BoundVar::from_usize(bound_vars.len()); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + counter += 1; + fcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var, kind })) + }, + types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"), + consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), + }, + ) + } else { + type_causes + }; + // Extract type components to build the witness type. let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let bound_vars = fcx.tcx.mk_bound_variable_kinds( - (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), - ); + let bound_vars = fcx.tcx.mk_bound_variable_kinds(bound_vars.iter()); let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); @@ -486,10 +543,10 @@ fn check_must_not_suspend_ty<'tcx>( data: SuspendCheckData<'_, 'tcx>, ) -> bool { if ty.is_unit() - // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage + // FIXME: should this check `Ty::is_inhabited_from`. This query is not available in this stage // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in // `must_use` - // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env) + // || !ty.is_inhabited_from(fcx.tcx, fcx.tcx.parent_module(hir_id).to_def_id(), fcx.param_env) { return false; } @@ -510,7 +567,7 @@ fn check_must_not_suspend_ty<'tcx>( let mut has_emitted = false; for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateKind::Trait(ref poly_trait_predicate) = + if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) = predicate.kind().skip_binder() { let def_id = poly_trait_predicate.trait_ref.def_id; diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 0fb7651b3..b33e7b8d6 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -1,21 +1,16 @@ use super::callee::DeferredCallResolution; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; use rustc_infer::infer; use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; use rustc_span::{self, Span}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _, -}; +use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _}; use std::cell::RefCell; use std::ops::Deref; @@ -38,19 +33,19 @@ pub struct Inherited<'tcx> { pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>, - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - // Removing these may also cause additional complications, see #101066. + /// Some additional `Sized` obligations badly affect type inference. + /// These obligations are added in a later stage of typeck. + /// Removing these may also cause additional complications, see #101066. pub(super) deferred_sized_obligations: RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>, - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. + /// When we process a call like `c()` where `c` is a closure type, + /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + /// `FnOnce` closure. In that case, we defer full resolution of the + /// call until upvar inference can kick in and make the + /// decision. We keep these deferred resolutions grouped by the + /// def-id of the closure, so that once we decide, we can easily go + /// back and process them. pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>, pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, @@ -94,29 +89,7 @@ impl<'tcx> Inherited<'tcx> { infcx: tcx .infer_ctxt() .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)) - .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| { - if fn_sig.has_escaping_bound_vars() { - return fn_sig; - } - infcx.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - let normalized_fn_sig = ocx.normalize( - ObligationCause::dummy(), - // FIXME(compiler-errors): This is probably not the right param-env... - infcx.tcx.param_env(def_id), - fn_sig, - ); - if ocx.select_all_or_error().is_empty() { - let normalized_fn_sig = - infcx.resolve_vars_if_possible(normalized_fn_sig); - if !normalized_fn_sig.needs_infer() { - return normalized_fn_sig; - } - } - fn_sig - }) - })), + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)), def_id, typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), } @@ -179,35 +152,4 @@ impl<'tcx> Inherited<'tcx> { self.register_predicates(infer_ok.obligations); infer_ok.value } - - pub(super) fn normalize_associated_types_in<T>( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - self.normalize_associated_types_in_with_cause( - ObligationCause::misc(span, body_id), - param_env, - value, - ) - } - - pub(super) fn normalize_associated_types_in_with_cause<T>( - &self, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(cause, param_env, value); - debug!(?ok); - self.register_infer_ok_obligations(ok) - } } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 9812d96fc..c2dc14024 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -3,7 +3,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_target::abi::{Pointer, VariantIdx}; use super::FnCtxt; @@ -46,7 +46,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let from = normalize(from); let to = normalize(to); trace!(?from, ?to); - + if from.has_non_region_infer() || to.has_non_region_infer() { + tcx.sess.delay_span_bug(span, "argument to transmute has inference variables"); + return; + } // Transmutes that are only changing lifetimes are always ok. if from == to { return; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 959c54866..5b2352cda 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,9 +53,9 @@ use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{struct_span_err, MultiSpan}; +use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::astconv::AstConv; @@ -89,38 +89,6 @@ pub struct LocalTy<'tcx> { revealed_ty: Ty<'tcx>, } -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, from_fn: true } - } - - pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => self, - - unsafety => { - let (unsafety, def) = match blk.rules { - BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id), - BlockCheckMode::DefaultBlock => (unsafety, self.def), - }; - UnsafetyState { def, unsafety, from_fn: false } - } - } - } -} - /// If this `DefId` is a "primary tables entry", returns /// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. /// @@ -209,6 +177,7 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T typeck_with_fallback(tcx, def_id, fallback) } +#[instrument(level = "debug", skip(tcx, fallback), ret)] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -232,9 +201,10 @@ fn typeck_with_fallback<'tcx>( let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { let param_env = tcx.param_env(def_id); - let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); + + if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id) @@ -244,15 +214,10 @@ fn typeck_with_fallback<'tcx>( // Compute the function signature from point of view of inside the fn. let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 + let fn_sig = fcx.normalize(body.value.span, fn_sig); + + check_fn(&mut fcx, fn_sig, decl, def_id, body, None); } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); let expected_type = body_ty .and_then(|ty| match ty.kind { hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)), @@ -303,7 +268,7 @@ fn typeck_with_fallback<'tcx>( _ => fallback(), }); - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); + let expected_type = fcx.normalize(body.value.span, expected_type); fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); // Gather locals in statics (because of block expressions). @@ -312,16 +277,14 @@ fn typeck_with_fallback<'tcx>( fcx.check_expr_coercable_to_type(&body.value, expected_type, None); fcx.write_ty(id, expected_type); - - fcx }; - let fallback_has_occurred = fcx.type_inference_fallback(); + fcx.type_inference_fallback(); // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); + fcx.select_obligations_where_possible(|_| {}); // Closure and generator analysis may run after fallback // because they don't constrain other type variables. @@ -343,7 +306,7 @@ fn typeck_with_fallback<'tcx>( fcx.select_all_obligations_or_error(); - if !fcx.infcx.is_tainted_by_errors() { + if let None = fcx.infcx.tainted_by_errors() { fcx.check_transmutes(); } @@ -427,16 +390,33 @@ impl<'tcx> EnclosingBreakables<'tcx> { } } -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { - struct_span_err!( - tcx.sess, +fn report_unexpected_variant_res( + tcx: TyCtxt<'_>, + res: Res, + qpath: &hir::QPath<'_>, + span: Span, + err_code: &str, + expected: &str, +) -> ErrorGuaranteed { + let res_descr = match res { + Res::Def(DefKind::Variant, _) => "struct variant", + _ => res.descr(), + }; + let path_str = rustc_hir_pretty::qpath_to_string(qpath); + let mut err = tcx.sess.struct_span_err_with_code( span, - E0533, - "expected unit struct, unit variant or constant, found {} `{}`", - res.descr(), - rustc_hir_pretty::qpath_to_string(qpath), - ) - .emit(); + format!("expected {expected}, found {res_descr} `{path_str}`"), + DiagnosticId::Error(err_code.into()), + ); + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => { + let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html"; + err.span_label(span, "`fn` calls are not allowed in patterns"); + err.help(format!("for more information, visit {patterns_url}")) + } + _ => err.span_label(span, format!("not a {expected}")), + } + .emit() } /// Controls whether the arguments are tupled. This is used for the call @@ -458,7 +438,7 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<' /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] enum TupleArgumentsFlag { DontTupleArguments, TupleArguments, diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 362f1c343..0b5dc946c 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -133,7 +133,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } fn is_tainted_by_errors(&self) -> bool { - self.infcx.is_tainted_by_errors() + self.infcx.tainted_by_errors().is_some() } fn resolve_type_vars_or_error( diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index be4ea9986..03d0e7926 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -106,7 +106,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // traits, no trait system method can be called before this point because they // could alter our Self-type, except for normalizing the receiver from the // signature (which is also done during probing). - let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]); + let method_sig_rcvr = self.normalize(self.span, method_sig.inputs()[0]); debug!( "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", self_ty, method_sig_rcvr, method_sig, method_predicates @@ -114,7 +114,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); let (method_sig, method_predicates) = - self.normalize_associated_types_in(self.span, (method_sig, method_predicates)); + self.normalize(self.span, (method_sig, method_predicates)); let method_sig = ty::Binder::dummy(method_sig); // Make sure nobody calls `drop()` explicitly. @@ -151,8 +151,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = - self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); + let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { return self.tcx.ty_error_with_message( rustc_span::DUMMY_SP, @@ -171,14 +170,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let base_ty = target; target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Method call receivers are the primary use case - // for two-phase borrows. - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + + // Method call receivers are the primary use case + // for two-phase borrows. + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes); + adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target, @@ -203,7 +199,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { target = match target.kind() { &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - assert_eq!(mutbl, hir::Mutability::Mut); + assert!(mutbl.is_mut()); self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) } other => panic!("Cannot adjust receiver type {:?} to const ptr", other), @@ -532,7 +528,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) // We don't care about regions here. .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + if trait_pred.def_id() == sized_def_id => + { let span = iter::zip(&predicates.predicates, &predicates.spans) .find_map( |(p, span)| { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index a1278edef..a2ca5c3b7 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -10,6 +10,7 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; +use crate::errors::OpMethodGenericParams; use crate::{Expectation, FnCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -19,11 +20,11 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitable}; use rustc_span::symbol::Ident; use rustc_span::Span; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, NormalizeExt}; use self::probe::{IsSuggestion, ProbeScope}; @@ -55,8 +56,7 @@ pub enum MethodError<'tcx> { // not-in-scope traits which may work. PrivateMatch(DefKind, DefId, Vec<DefId>), - // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have - // forgotten to import a trait. + // Found a `Self: Sized` bound where `Self` is a trait object. IllegalSizedBound(Vec<DefId>, bool, Span), // Found a match, but the return type is wrong @@ -93,17 +93,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_id: hir::HirId, allow_private: bool, ) -> bool { - let mode = probe::Mode::MethodCall; match self.probe_for_name( - method_name.span, - mode, + probe::Mode::MethodCall, method_name, IsSuggestion(false), self_ty, call_expr_id, ProbeScope::TraitsInScope, ) { - Ok(..) => true, + Ok(pick) => { + pick.maybe_emit_unstable_name_collision_hint( + self.tcx, + method_name.span, + call_expr_id, + ); + true + } Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, @@ -125,10 +130,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let params = self .probe_for_name( - method_name.span, probe::Mode::MethodCall, method_name, - IsSuggestion(false), + IsSuggestion(true), self_ty, call_expr.hir_id, ProbeScope::TraitsInScope, @@ -175,7 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: &'tcx [hir::Expr<'tcx>], ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> { let pick = - self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); @@ -200,13 +204,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }); // We probe again to see if there might be a borrow mutability discrepancy. match self.lookup_probe( - span, segment.ident, trait_type, call_expr, ProbeScope::TraitsInScope, ) { - Ok(ref new_pick) if *new_pick != pick => { + Ok(ref new_pick) if pick.differs_from(new_pick) => { needs_mut = true; } _ => {} @@ -214,29 +217,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We probe again, taking all traits into account (not only those in scope). - let mut candidates = match self.lookup_probe( - span, - segment.ident, - self_ty, - call_expr, - ProbeScope::AllTraits, - ) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)], - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), - CandidateSource::Trait(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; - candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id)); + let candidates = + match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if pick.differs_from(new_pick) => { + vec![new_pick.item.container_id(self.tcx)] + } + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), + CandidateSource::Trait(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; return Err(IllegalSizedBound(candidates, needs_mut, span)); } @@ -247,23 +246,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, call_expr))] pub fn lookup_probe( &self, - span: Span, method_name: Ident, self_ty: Ty<'tcx>, call_expr: &'tcx hir::Expr<'tcx>, scope: ProbeScope, ) -> probe::PickResult<'tcx> { - let mode = probe::Mode::MethodCall; - let self_ty = self.resolve_vars_if_possible(self_ty); - self.probe_for_name( - span, - mode, + let pick = self.probe_for_name( + probe::Mode::MethodCall, method_name, IsSuggestion(false), self_ty, call_expr.hir_id, scope, - ) + )?; + pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id); + Ok(pick) } pub(super) fn obligation_for_method( @@ -295,10 +292,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let poly_trait_ref = ty::Binder::dummy(trait_ref); ( traits::Obligation::misc( + self.tcx, span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), + poly_trait_ref.without_const(), ), substs, ) @@ -337,6 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ( traits::Obligation::new( + self.tcx, traits::ObligationCause::new( span, self.body_id, @@ -348,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ), self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), + poly_trait_ref, ), substs, ) @@ -444,7 +443,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let def_id = method_item.def_id; let generics = tcx.generics_of(def_id); - assert_eq!(generics.params.len(), 0); + + if generics.params.len() != 0 { + tcx.sess.emit_fatal(OpMethodGenericParams { + span: tcx.def_span(method_item.def_id), + method_name: m_name.to_string(), + }); + } debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); let mut obligations = vec![]; @@ -459,11 +464,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = fn_sig.subst(self.tcx, substs); let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig); - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) + let cause = if is_op { + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_ty: None, + }, + ) } else { - self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + traits::ObligationCause::misc(span, self.body_id) }; + + let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig); let fn_sig = { obligations.extend(o); value @@ -479,11 +495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) - } else { - self.normalize_associated_types_in_as_infer_ok(span, bounds) - }; + let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds); let bounds = { obligations.extend(o); value @@ -491,20 +503,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); - let cause = if is_op { - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: None, - }, - ) - } else { - traits::ObligationCause::misc(span, self.body_id) - }; let predicates_cause = cause.clone(); obligations.extend(traits::predicates_for_generics( move |_, _| predicates_cause.clone(), @@ -519,9 +517,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_ty, obligation ); obligations.push(traits::Obligation::new( + tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())), )); let callee = MethodCallee { def_id, substs, sig: fn_sig }; @@ -559,6 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; // Check if we have an enum variant. + let mut struct_variant = None; if let ty::Adt(adt_def, _) = self_ty.kind() { if adt_def.is_enum() { let variant_def = adt_def @@ -566,29 +566,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did())); if let Some(variant_def) = variant_def { - // Braced variants generate unusable names in value namespace (reserved for - // possible future use), so variants resolved as associated items may refer to - // them as well. It's ok to use the variant's id as a ctor id since an - // error will be reported on any use of such resolution anyway. - let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); - tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span)); - return Ok(( - DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), - ctor_def_id, - )); + if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor { + tcx.check_stability( + ctor_def_id, + Some(expr_id), + span, + Some(method_name.span), + ); + return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id)); + } else { + struct_variant = Some((DefKind::Variant, variant_def.def_id)); + } } } } let pick = self.probe_for_name( - span, probe::Mode::Path, method_name, IsSuggestion(false), self_ty, expr_id, ProbeScope::TraitsInScope, - )?; + ); + let pick = match (pick, struct_variant) { + // Fall back to a resolution that will produce an error later. + (Err(_), Some(res)) => return Ok(res), + (pick, _) => pick?, + }; + + pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id); self.lint_fully_qualified_call_from_2018( span, diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 3c98a2aa3..dea14dd93 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -5,10 +5,9 @@ use crate::{ use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; -use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_middle::ty::subst::InternalSubsts; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::{Adt, Array, Ref, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; use rustc_span::symbol::kw::{Empty, Underscore}; @@ -88,14 +87,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Mut, - .. - }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Not, - .. - }) => "&", + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => { + mutbl.ref_prefix_str() + } Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", }; if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) @@ -227,14 +221,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we know it does not, we don't need to warn. if method_name.name == sym::from_iter { if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { + let any_type = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + }); if !self .infcx - .type_implements_trait( - trait_def_id, - self_ty, - InternalSubsts::empty(), - self.param_env, - ) + .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env) .may_apply() { return; @@ -387,8 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&", + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(), Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", }; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 28aa2302f..ae299cc9d 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,7 +9,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -17,8 +16,10 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; +use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; use rustc_span::def_id::DefId; @@ -35,6 +36,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::traits::NormalizeExt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::cmp::max; use std::iter; @@ -83,8 +85,6 @@ struct ProbeContext<'a, 'tcx> { unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, - is_suggestion: IsSuggestion, - scope_expr_id: hir::HirId, } @@ -193,7 +193,7 @@ impl AutorefOrPtrAdjustment { } } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct Pick<'tcx> { pub item: ty::AssocItem, pub kind: PickKind<'tcx>, @@ -209,6 +209,9 @@ pub struct Pick<'tcx> { /// `*mut T`, convert it to `*const T`. pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>, pub self_ty: Ty<'tcx>, + + /// Unstable candidates alongside the stable ones. + unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -298,7 +301,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, - span: Span, mode: Mode, item_name: Ident, is_suggestion: IsSuggestion, @@ -307,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope: ProbeScope, ) -> PickResult<'tcx> { self.probe_op( - span, + item_name.span, mode, Some(item_name), None, @@ -340,10 +342,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut orig_values, ); - let steps = if mode == Mode::MethodCall { - self.tcx.method_autoderef_steps(param_env_and_self_ty) - } else { - self.probe(|_| { + let steps = match mode { + Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty), + Mode::Path => self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This // is a bit inefficient, but I don't think that writing @@ -372,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_bad_ty: None, reached_recursion_limit: false, } - }) + }), }; // If our autoderef loop had reached the recursion limit, @@ -446,7 +447,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type, orig_values, steps.steps, - is_suggestion, scope_expr_id, ); @@ -475,10 +475,9 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() @@ -542,7 +541,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return_type: Option<Ty<'tcx>>, orig_steps_var_values: OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], - is_suggestion: IsSuggestion, scope_expr_id: hir::HirId, ) -> ProbeContext<'a, 'tcx> { ProbeContext { @@ -560,7 +558,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { allow_similar_names: false, private_candidate: None, unsatisfied_predicates: Vec::new(), - is_suggestion, scope_expr_id, } } @@ -718,9 +715,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, // see issue #89650 let cause = traits::ObligationCause::misc(self.span, self.body_id); - let selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, self.param_env, cause, xform_self_ty); + let InferOk { value: xform_self_ty, obligations } = + self.fcx.at(&cause, self.param_env).normalize(xform_self_ty); + debug!( "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}", xform_self_ty, xform_ret_ty @@ -787,7 +784,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { match *trait_predicate.trait_ref.self_ty().kind() { ty::Param(p) if p == param_ty => { Some(bound_predicate.rebind(trait_predicate.trait_ref)) @@ -797,14 +794,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -882,7 +880,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - pub fn matches_return_type( + fn matches_return_type( &self, method: &ty::AssocItem, self_ty: Option<Ty<'tcx>>, @@ -1019,7 +1017,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container_id(self.tcx)], - //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), Some(Err(MethodError::Ambiguity(v))) => v .into_iter() .map(|source| match source { @@ -1054,26 +1051,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn pick_core(&mut self) -> Option<PickResult<'tcx>> { - let mut unstable_candidates = Vec::new(); - let pick = self.pick_all_method(Some(&mut unstable_candidates)); + let pick = self.pick_all_method(Some(&mut vec![])); // In this case unstable picking is done by `pick_method`. if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { return pick; } - match pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - pick - } - Some(_) => pick, - None => self.pick_all_method(None), + if pick.is_none() { + return self.pick_all_method(None); } + pick } fn pick_all_method( @@ -1218,7 +1206,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); - let mut unstable_candidates = Vec::new(); for (kind, candidates) in &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] @@ -1228,26 +1215,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty, candidates.iter(), &mut possibly_unsatisfied_predicates, - Some(&mut unstable_candidates), + Some(&mut vec![]), ); - if let Some(pick) = res { - if !self.is_suggestion.0 && !unstable_candidates.is_empty() { - if let Ok(p) = &pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - } - } - return Some(pick); + if res.is_some() { + return res; } } debug!("searching unstable candidates"); let res = self.consider_candidates( self_ty, - unstable_candidates.iter().map(|(c, _)| c), + self.inherent_candidates.iter().chain(&self.extension_candidates), &mut possibly_unsatisfied_predicates, None, ); @@ -1302,7 +1280,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>, )>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> where ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone, @@ -1326,7 +1304,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let Some(uc) = unstable_candidates { + if let Some(uc) = &mut unstable_candidates { applicable_candidates.retain(|&(p, _)| { if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(p.item.def_id, None, self.span, None) @@ -1345,30 +1323,63 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { applicable_candidates.pop().map(|(probe, status)| { if status == ProbeResult::Match { - Ok(probe.to_unadjusted_pick(self_ty)) + Ok(probe + .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) } else { Err(MethodError::BadReturnType) } }) } +} + +impl<'tcx> Pick<'tcx> { + /// In case there were unstable name collisions, emit them as a lint. + /// Checks whether two picks do not refer to the same trait item for the same `Self` type. + /// Only useful for comparisons of picks in order to improve diagnostics. + /// Do not use for type checking. + pub fn differs_from(&self, other: &Self) -> bool { + let Self { + item: + AssocItem { + def_id, + name: _, + kind: _, + container: _, + trait_item_def_id: _, + fn_has_self_parameter: _, + }, + kind: _, + import_ids: _, + autoderefs: _, + autoref_or_ptr_adjustment: _, + self_ty, + unstable_candidates: _, + } = *self; + self_ty != other.self_ty || def_id != other.item.def_id + } - fn emit_unstable_name_collision_hint( + /// In case there were unstable name collisions, emit them as a lint. + pub fn maybe_emit_unstable_name_collision_hint( &self, - stable_pick: &Pick<'_>, - unstable_candidates: &[(Candidate<'tcx>, Symbol)], + tcx: TyCtxt<'tcx>, + span: Span, + scope_expr_id: hir::HirId, ) { - let def_kind = stable_pick.item.kind.as_def_kind(); - self.tcx.struct_span_lint_hir( + if self.unstable_candidates.is_empty() { + return; + } + let def_kind = self.item.kind.as_def_kind(); + tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, - self.scope_expr_id, - self.span, + scope_expr_id, + span, format!( "{} {} with this name may be added to the standard library in the future", def_kind.article(), - def_kind.descr(stable_pick.item.def_id), + def_kind.descr(self.item.def_id), ), |lint| { - match (stable_pick.item.kind, stable_pick.item.container) { + match (self.item.kind, self.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only @@ -1377,31 +1388,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", - self.tcx.def_path_str(stable_pick.item.def_id), + tcx.def_path_str(self.item.def_id), )); } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { - let def_id = stable_pick.item.container_id(self.tcx); + let def_id = self.item.container_id(tcx); lint.span_suggestion( - self.span, + span, "use the fully qualified path to the associated const", format!( "<{} as {}>::{}", - stable_pick.self_ty, - self.tcx.def_path_str(def_id), - stable_pick.item.name + self.self_ty, + tcx.def_path_str(def_id), + self.item.name ), Applicability::MachineApplicable, ); } _ => {} } - if self.tcx.sess.is_nightly_build() { - for (candidate, feature) in unstable_candidates { + if tcx.sess.is_nightly_build() { + for (candidate, feature) in &self.unstable_candidates { lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, - self.tcx.def_path_str(candidate.item.def_id), + tcx.def_path_str(candidate.item.def_id), )); } } @@ -1410,14 +1421,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }, ); } +} +impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn select_trait_candidate( &self, trait_ref: ty::TraitRef<'tcx>, ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { let cause = traits::ObligationCause::misc(self.span, self.body_id); - let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate(); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); + let predicate = ty::Binder::dummy(trait_ref); + let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, predicate); traits::SelectionContext::new(self).select(&obligation) } @@ -1476,7 +1489,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut xform_ret_ty = probe.xform_ret_ty; debug!(?xform_ret_ty); - let selcx = &mut traits::SelectionContext::new(self); let cause = traits::ObligationCause::misc(self.span, self.body_id); let mut parent_pred = None; @@ -1490,10 +1502,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` // for why this is necessary - let traits::Normalized { + let InferOk { value: normalized_xform_ret_ty, obligations: normalization_obligations, - } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty); + } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty); xform_ret_ty = normalized_xform_ret_ty; debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); @@ -1501,8 +1513,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let impl_def_id = probe.item.container_id(self.tcx); let impl_bounds = self.tcx.predicates_of(impl_def_id); let impl_bounds = impl_bounds.instantiate(self.tcx, substs); - let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = - traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); + + let InferOk { value: impl_bounds, obligations: norm_obligations } = + self.fcx.at(&cause, self.param_env).normalize(impl_bounds); // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( @@ -1548,7 +1561,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let predicate = ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); parent_pred = Some(predicate); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); + let obligation = + traits::Obligation::new(self.tcx, cause, self.param_env, predicate); if !self.predicate_may_hold(&obligation) { result = ProbeResult::NoMatch; if self.probe(|_| { @@ -1669,6 +1683,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, + unstable_candidates: vec![], }) } @@ -1688,7 +1703,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.return_type, self.orig_steps_var_values.clone(), steps, - IsSuggestion(true), self.scope_expr_id, ); pcx.allow_similar_names = true; @@ -1861,6 +1875,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.tcx.erase_late_bound_regions(value) } + /// Determine if the given associated item type is relevant in the current context. + fn is_relevant_kind_for_mode(&self, kind: ty::AssocKind) -> bool { + match (self.mode, kind) { + (Mode::MethodCall, ty::AssocKind::Fn) => true, + (Mode::Path, ty::AssocKind::Const | ty::AssocKind::Fn) => true, + _ => false, + } + } + /// Finds the method with the appropriate name (or return type, as the case may be). If /// `allow_similar_names` is set, find methods with close-matching names. // The length of the returned iterator is nearly always 0 or 1 and this @@ -1873,7 +1896,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .associated_items(def_id) .in_definition_order() .filter(|x| { - if x.kind.namespace() != Namespace::ValueNS { + if !self.is_relevant_kind_for_mode(x.kind) { return false; } match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) @@ -1887,16 +1910,26 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } else { self.fcx .associated_value(def_id, name) + .filter(|x| self.is_relevant_kind_for_mode(x.kind)) .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) } } else { - self.tcx.associated_items(def_id).in_definition_order().copied().collect() + self.tcx + .associated_items(def_id) + .in_definition_order() + .filter(|x| self.is_relevant_kind_for_mode(x.kind)) + .copied() + .collect() } } } impl<'tcx> Candidate<'tcx> { - fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> { + fn to_unadjusted_pick( + &self, + self_ty: Ty<'tcx>, + unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, + ) -> Pick<'tcx> { Pick { item: self.item, kind: match self.kind { @@ -1921,6 +1954,7 @@ impl<'tcx> Candidate<'tcx> { autoderefs: 0, autoref_or_ptr_adjustment: None, self_ty, + unstable_candidates, } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6c21ed902..db93cfab2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -5,6 +5,7 @@ use crate::errors; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::StashKey; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -13,27 +14,35 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir::PatKind::Binding; +use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{ + type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + RegionVariableOrigin, +}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::util::supertraits; +use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ - FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, + FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, }; -use std::cmp::Ordering; -use std::iter; - use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; +use rustc_hir::intravisit::Visitor; +use std::cmp::Ordering; +use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -62,22 +71,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| { info!("check deref {:?} impl FnOnce", ty); self.probe(|_| { - let fn_once_substs = tcx.mk_substs_trait( - ty, - &[self - .next_ty_var(TypeVariableOrigin { + let trait_ref = tcx.mk_trait_ref( + fn_once, + [ + ty, + self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span, - }) - .into()], + }), + ], ); - let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); let poly_trait_ref = ty::Binder::dummy(trait_ref); let obligation = Obligation::misc( + tcx, span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(tcx), + poly_trait_ref.without_const(), ); self.predicate_may_hold(&obligation) }) @@ -107,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, sources: &mut Vec<CandidateSource>, - sugg_span: Span| { + sugg_span: Option<Span>| { sources.sort(); sources.dedup(); // Dynamic limit to avoid hiding just one candidate, which is silly. @@ -168,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.note(¬e_str); } - if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { + if let Some(sugg_span) = sugg_span + && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { let path = self.tcx.def_path_str(trait_ref.def_id); let ty = match item.kind { @@ -217,20 +228,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note(item_span, msg); None }; - let path = self.tcx.def_path_str(trait_did); - print_disambiguation_help( - item_name, - args, - err, - path, - rcvr_ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); + if let Some(sugg_span) = sugg_span { + let path = self.tcx.def_path_str(trait_did); + print_disambiguation_help( + item_name, + args, + err, + path, + rcvr_ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, + ); + } } } } @@ -248,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: mut static_sources, + mut static_candidates, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -256,15 +269,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => { let tcx = self.tcx; - let actual = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = self.ty_to_string(actual); + let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); + let ty_str = self.ty_to_string(rcvr_ty); let is_method = mode == Mode::MethodCall; let item_kind = if is_method { "method" - } else if actual.is_enum() { + } else if rcvr_ty.is_enum() { "variant or associated item" } else { - match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { + match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { (Some(name), false) if name.is_lowercase() => "function or associated item", (Some(_), false) => "associated item", (Some(_), true) | (None, false) => "variant or associated item", @@ -273,24 +286,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.suggest_wrapping_range_with_parens( - tcx, actual, source, span, item_name, &ty_str, + tcx, rcvr_ty, source, span, item_name, &ty_str, ) || self.suggest_constraining_numerical_ty( - tcx, actual, source, span, item_kind, item_name, &ty_str, + tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str, ) { return None; } - span = item_name.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = actual.kind() { + if let ty::Adt(_, generics) = rcvr_ty.kind() { if generics.len() > 0 { - let mut autoderef = self.autoderef(span, actual); + let mut autoderef = self.autoderef(span, rcvr_ty); let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_deref, _) = ty.kind() { + if let ty::Adt(adt_def, _) = ty.kind() { self.tcx - .inherent_impls(adt_deref.did()) + .inherent_impls(adt_def.did()) .iter() .filter_map(|def_id| self.associated_value(*def_id, item_name)) .count() @@ -315,16 +327,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "no {} named `{}` found for {} `{}` in the current scope", item_kind, item_name, - actual.prefix_string(self.tcx), + rcvr_ty.prefix_string(self.tcx), ty_str_reported, ); - if actual.references_error() { + if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( - &mut err, item_name, actual, cal, span, + &mut err, item_name, rcvr_ty, cal, span, ); } if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { @@ -335,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - if let ty::RawPtr(_) = &actual.kind() { + if let ty::RawPtr(_) = &rcvr_ty.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ @@ -347,22 +359,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - let ty_span = match actual.kind() { - ty::Param(param_type) => { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let type_param = generics.type_param(param_type, self.tcx); - Some(self.tcx.def_span(type_param.def_id)) - } + let ty_span = match rcvr_ty.kind() { + ty::Param(param_type) => Some( + param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()), + ), ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), _ => None, }; - if let Some(span) = ty_span { err.span_label( span, format!( "{item_kind} `{item_name}` not found for this {}", - actual.prefix_string(self.tcx) + rcvr_ty.prefix_string(self.tcx) ), ); } @@ -374,7 +383,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .hir() .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); let probe = self.lookup_probe( - span, item_name, output_ty, call_expr, @@ -386,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut custom_span_label = false; - if !static_sources.is_empty() { + if !static_candidates.is_empty() { err.note( "found the following associated functions; to be used as methods, \ functions must have a `self` parameter", @@ -394,43 +402,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, "this is an associated function, not a method"); custom_span_label = true; } - if static_sources.len() == 1 { - let ty_str = - if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) { - // When the "method" is resolved through dereferencing, we really want the - // original type that has the associated function for accurate suggestions. - // (#61411) - let ty = tcx.at(span).type_of(*impl_did); - match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { - (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { - // Use `actual` as it will have more `substs` filled in. - self.ty_to_value_string(actual.peel_refs()) - } - _ => self.ty_to_value_string(ty.peel_refs()), - } - } else { - self.ty_to_value_string(actual.peel_refs()) - }; - if let SelfSource::MethodCall(expr) = source { - err.span_suggestion( - expr.span.to(span), - "use associated function syntax instead", - format!("{}::{}", ty_str, item_name), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("try with `{}::{}`", ty_str, item_name,)); - } + if static_candidates.len() == 1 { + self.suggest_associated_call_syntax( + &mut err, + &static_candidates, + rcvr_ty, + source, + item_name, + args, + sugg_span, + ); - report_candidates(span, &mut err, &mut static_sources, sugg_span); - } else if static_sources.len() > 1 { - report_candidates(span, &mut err, &mut static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_candidates, None); + } else if static_candidates.len() > 1 { + report_candidates(span, &mut err, &mut static_candidates, Some(sugg_span)); } let mut bound_spans = vec![]; let mut restrict_type_params = false; let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(actual, span) { + if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { let msg = "consider using `len` instead"; if let SelfSource::MethodCall(_expr) = source { err.span_suggestion_short( @@ -444,7 +435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { let iterator_trait = self.tcx.def_path_str(iterator_trait); - err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement")); + err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement")); } } else if !unsatisfied_predicates.is_empty() { let mut type_params = FxHashMap::default(); @@ -454,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unimplemented_traits = FxHashMap::default(); let mut unimplemented_traits_only = true; for (predicate, _parent_pred, cause) in &unsatisfied_predicates { - if let (ty::PredicateKind::Trait(p), Some(cause)) = + if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) = (predicate.kind().skip_binder(), cause.as_ref()) { if p.trait_ref.self_ty() != rcvr_ty { @@ -481,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // because of some non-Clone item being iterated over. for (predicate, _parent_pred, _cause) in &unsatisfied_predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(p) + ty::PredicateKind::Clause(ty::Clause::Trait(p)) if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} _ => { unimplemented_traits_only = false; @@ -493,27 +484,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut collect_type_param_suggestions = |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateKind::Trait(p)) = + if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) = (self_ty.kind(), parent_pred.kind().skip_binder()) { + let hir = self.tcx.hir(); let node = match p.trait_ref.self_ty().kind() { ty::Param(_) => { // Account for `fn` items like in `issue-35677.rs` to // suggest restricting its type params. - let did = self.tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: self.body_id, - }); - Some( - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(did)), - ) + let parent_body = + hir.body_owner(hir::BodyId { hir_id: self.body_id }); + Some(hir.get(parent_body)) + } + ty::Adt(def, _) => { + def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) } - ty::Adt(def, _) => def.did().as_local().map(|def_id| { - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) - }), _ => None, }; if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { @@ -562,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut format_pred = |pred: ty::Predicate<'tcx>| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(pred) => { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { let pred = bound_predicate.rebind(pred); // `<Foo as Iterator>::Item = String`. let projection_ty = pred.skip_binder().projection_ty; @@ -585,7 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_span_label(projection_ty.self_ty(), &obligation, &quiet); Some((obligation, projection_ty.self_ty())) } - ty::PredicateKind::Trait(poly_trait_ref) => { + ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { let p = poly_trait_ref.trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); @@ -605,7 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(ref data) => { + ObligationCauseCode::ImplDerivedObligation(data) => { Some((&data.derived, p, parent, data.impl_def_id, data)) } _ => None, @@ -621,22 +606,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unmet obligation comes from a `derive` macro, point at it once to // avoid multiple span labels pointing at the same place. Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - .. - })) if matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - let span = ident.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - - Some(Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), .. })) if matches!( @@ -659,34 +628,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); } - // Unmet obligation coming from a `trait`. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - span: item_span, - .. - })) if !matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans - } else { - ident.span.into() - }; - spans.push_span_label(ident.span, "in this trait"); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - // Unmet obligation coming from an `impl`. Some(Node::Item(hir::Item { kind: @@ -695,23 +636,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), span: item_span, .. - })) if !matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) && !matches!( - of_trait.as_ref().map(|t| t - .path - .span - .ctxt() - .outer_expn_data() - .kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { + })) => { let sized_pred = unsatisfied_predicates.iter().any(|(pred, _, _)| { match pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { Some(pred.def_id()) == self.tcx.lang_items().sized_trait() && pred.polarity == ty::ImplPolarity::Positive @@ -759,7 +688,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let entry = spanned_predicates.entry(spans); entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); } - _ => {} + Some(_) => unreachable!(), + None => (), } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); @@ -844,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(_, path)| path) .collect::<Vec<_>>() .join("\n"); - let actual_prefix = actual.prefix_string(self.tcx); + let actual_prefix = rcvr_ty.prefix_string(self.tcx); info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); let (primary_message, label) = if unimplemented_traits.len() == 1 && unimplemented_traits_only { @@ -853,7 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .next() .map(|(_, (trait_ref, obligation))| { if trait_ref.self_ty().references_error() - || actual.references_error() + || rcvr_ty.references_error() { // Avoid crashing. return (None, None); @@ -863,7 +793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .on_unimplemented_note(trait_ref, &obligation); (message, label) }) - .unwrap_or((None, None)) + .unwrap() } else { (None, None) }; @@ -889,15 +819,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let label_span_not_found = |err: &mut Diagnostic| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); - let is_string_or_ref_str = match actual.kind() { + let is_string_or_ref_str = match rcvr_ty.kind() { ty::Ref(_, ty, _) => { ty.is_str() || matches!( ty.kind(), - ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did()) + ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() ) } - ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()), + ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), _ => false, }; if is_string_or_ref_str && item_name.name == sym::iter { @@ -925,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // different from the received one // So we avoid suggestion method with Box<Self> // for instance - self.tcx.at(span).type_of(*def_id) != actual + self.tcx.at(span).type_of(*def_id) != rcvr_ty && self.tcx.at(span).type_of(*def_id) != rcvr_ty } (Mode::Path, false, _) => true, @@ -972,7 +902,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) + if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) && lev_candidate.is_none() && !custom_span_label { @@ -982,13 +912,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - // Don't suggest (for example) `expr.field.method()` if `expr.method()` - // doesn't exist due to unsatisfied predicates. + // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` + // can't be called due to `typeof(expr): Clone` not holding. if unsatisfied_predicates.is_empty() { - self.check_for_field_method(&mut err, source, span, actual, item_name); + self.suggest_calling_method_on_field( + &mut err, source, span, rcvr_ty, item_name, + ); } - self.check_for_inner_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -996,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, &msg); } - if actual.is_numeric() && actual.is_fresh() || restrict_type_params { + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { } else { self.suggest_traits_to_import( &mut err, @@ -1007,15 +939,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, out_of_scope_traits, &unsatisfied_predicates, - &static_sources, + &static_candidates, unsatisfied_bounds, ); } // Don't emit a suggestion if we found an actual method // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() && actual.is_enum() { - let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); + if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { + let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); if let Some(suggestion) = lev_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), item_name.name, @@ -1030,7 +962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if item_name.name == sym::as_str && actual.peel_refs().is_str() { + if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() { let msg = "remove this method call"; let mut fallback_span = true; if let SelfSource::MethodCall(expr) = source { @@ -1089,7 +1021,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, &mut sources, sugg_span); + report_candidates(span, &mut err, &mut sources, Some(sugg_span)); err.emit(); } @@ -1146,7 +1078,133 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - fn suggest_field_call( + /// Suggest calling `Ty::method` if `.method()` isn't found because the method + /// doesn't take a `self` receiver. + fn suggest_associated_call_syntax( + &self, + err: &mut Diagnostic, + static_candidates: &Vec<CandidateSource>, + rcvr_ty: Ty<'tcx>, + source: SelfSource<'tcx>, + item_name: Ident, + args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>, + sugg_span: Span, + ) { + let mut has_unsuggestable_args = false; + let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) { + // When the "method" is resolved through dereferencing, we really want the + // original type that has the associated function for accurate suggestions. + // (#61411) + let impl_ty = self.tcx.type_of(*impl_did); + let target_ty = self + .autoderef(sugg_span, rcvr_ty) + .find(|(rcvr_ty, _)| { + DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer } + .types_may_unify(*rcvr_ty, impl_ty) + }) + .map_or(impl_ty, |(ty, _)| ty) + .peel_refs(); + if let ty::Adt(def, substs) = target_ty.kind() { + // If there are any inferred arguments, (`{integer}`), we should replace + // them with underscores to allow the compiler to infer them + let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| { + if !arg.is_suggestable(self.tcx, true) { + has_unsuggestable_args = true; + match arg.unpack() { + GenericArgKind::Lifetime(_) => self + .next_region_var(RegionVariableOrigin::MiscVariable( + rustc_span::DUMMY_SP, + )) + .into(), + GenericArgKind::Type(_) => self + .next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }) + .into(), + GenericArgKind::Const(arg) => self + .next_const_var( + arg.ty(), + ConstVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: ConstVariableOriginKind::MiscVariable, + }, + ) + .into(), + } + } else { + arg + } + })); + + self.tcx.value_path_str_with_substs(def.did(), infer_substs) + } else { + self.ty_to_value_string(target_ty) + } + } else { + self.ty_to_value_string(rcvr_ty.peel_refs()) + }; + if let SelfSource::MethodCall(_) = source { + let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) + && let Some(assoc) = self.associated_value(*impl_did, item_name) + && assoc.kind == ty::AssocKind::Fn + { + let sig = self.tcx.fn_sig(assoc.def_id); + sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() { + None + } else { + Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str())) + }) + } else { + None + }; + let mut applicability = Applicability::MachineApplicable; + let args = if let Some((receiver, args)) = args { + // The first arg is the same kind as the receiver + let explicit_args = if first_arg.is_some() { + std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() + } else { + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() + }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::<Vec<_>>() + .join(", "), + ) + } else { + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() + }; + err.span_suggestion( + sugg_span, + "use associated function syntax instead", + format!("{}::{}{}", ty_str, item_name, args), + applicability, + ); + } else { + err.help(&format!("try with `{}::{}`", ty_str, item_name,)); + } + } + + /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with + /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`. + fn suggest_calling_field_as_fn( &self, span: Span, rcvr_ty: Ty<'tcx>, @@ -1261,7 +1319,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); let pick = self.probe_for_name( - span, Mode::MethodCall, item_name, IsSuggestion(true), @@ -1408,7 +1465,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn check_for_field_method( + /// For code `rect::area(...)`, + /// if `rect` is a local variable and `area` is a valid assoc method for it, + /// we try to suggest `rect.area()` + pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) { + debug!("suggest_assoc_method_call segs: {:?}", segs); + let [seg1, seg2] = segs else { return; }; + let Some(mut diag) = + self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) + else { return }; + + let map = self.infcx.tcx.hir(); + let body = map.body(rustc_hir::BodyId { hir_id: self.body_id }); + struct LetVisitor<'a> { + result: Option<&'a hir::Expr<'a>>, + ident_name: Symbol, + } + + // FIXME: This really should be taking scoping, etc into account. + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind + && let Binding(_, _, ident, ..) = pat.kind + && ident.name == self.ident_name + { + self.result = *init; + } else { + hir::intravisit::walk_stmt(self, ex); + } + } + } + + let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; + visitor.visit_body(&body); + + let parent = self.tcx.hir().get_parent_node(seg1.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) + && let Some(expr) = visitor.result + && let Some(self_ty) = self.node_ty_opt(expr.hir_id) + { + let probe = self.lookup_probe( + seg2.ident, + self_ty, + call_expr, + ProbeScope::TraitsInScope, + ); + if probe.is_ok() { + let sm = self.infcx.tcx.sess.source_map(); + diag.span_suggestion_verbose( + sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), + "you may have meant to call an instance method", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + diag.emit(); + } + + /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` + fn suggest_calling_method_on_field( &self, err: &mut Diagnostic, source: SelfSource<'tcx>, @@ -1439,7 +1555,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, &|_, field_ty| { self.lookup_probe( - span, item_name, field_ty, call_expr, @@ -1487,7 +1602,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, source: SelfSource<'tcx>, - span: Span, actual: Ty<'tcx>, item_name: Ident, ) { @@ -1510,15 +1624,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::TraitsInScope, - ) - .ok() - .map(|pick| (variant, field, pick)) + self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1583,12 +1691,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [first] = ***substs else { return; }; let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; let Ok(pick) = self.lookup_probe( - span, - item_name, - ty, - call_expr, - ProbeScope::TraitsInScope, - ) else { return; }; + item_name, + ty, + call_expr, + ProbeScope::TraitsInScope, + ) else { return; }; let name = self.ty_to_value_string(actual); let inner_id = kind.did(); @@ -1668,7 +1775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let all_local_types_needing_impls = errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() { ty::Adt(def, _) => def.did().is_local(), _ => false, }, @@ -1677,7 +1784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut preds: Vec<_> = errors .iter() .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => Some(pred), + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred), _ => None, }) .collect(); @@ -1748,7 +1855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut derives = Vec::<(String, Span, Symbol)>::new(); let mut traits = Vec::<Span>::new(); for (pred, _, _) in unsatisfied_predicates { - let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; + let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue }; let adt = match trait_pred.self_ty().ty_adt_def() { Some(adt) if adt.did().is_local() => adt, _ => continue, @@ -1838,7 +1945,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let SelfSource::QPath(ty) = self_source else { return; }; for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( - ty.span, Mode::Path, item_name, IsSuggestion(true), @@ -1865,6 +1971,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Str | ty::Projection(_) | ty::Param(_) => format!("{deref_ty}"), + // we need to test something like <&[_]>::len or <(&[u32])>::len + // and Vec::function(); + // <&[_]>::len or <&[u32]>::len doesn't need an extra "<>" between + // but for Adt type like Vec::function() + // we would suggest <[_]>::function(); + _ if self.tcx.sess.source_map().span_wrapped_by_angle_or_parentheses(ty.span) => format!("{deref_ty}"), _ => format!("<{deref_ty}>"), }; err.span_suggestion_verbose( @@ -1887,7 +1999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { - ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)), + ty::Adt(def, substs) => self.tcx.def_path_str_with_substs(def.did(), substs), _ => self.ty_to_string(ty), } } @@ -1901,7 +2013,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ) { let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), + Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); @@ -2021,7 +2133,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { - debug!(?span, ?item_name, ?rcvr_ty, ?rcvr); + debug!( + "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}", + span, item_name, rcvr_ty, rcvr + ); let skippable = [ self.tcx.lang_items().clone_trait(), self.tcx.lang_items().deref_trait(), @@ -2037,7 +2152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -2060,7 +2175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // suggestions are generally misleading (see #94218). break; } - _ => {} + Err(_) => (), } for (rcvr_ty, pre) in &[ @@ -2071,7 +2186,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ] { if let Some(new_rcvr_t) = *rcvr_ty && let Ok(pick) = self.lookup_probe( - span, item_name, new_rcvr_t, rcvr, @@ -2151,8 +2265,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match p.kind().skip_binder() { // Hide traits if they are present in predicates as they can be fixed without // having to implement them. - ty::PredicateKind::Trait(t) => t.def_id() == info.def_id, - ty::PredicateKind::Projection(p) => { + ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { + t.def_id() == info.def_id + } + ty::PredicateKind::Clause(ty::Clause::Projection(p)) => { p.projection_ty.item_def_id == info.def_id } _ => false, @@ -2452,7 +2568,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: method_name.span, }; let probe = self.lookup_probe( - expr.span, new_name, self_ty, self_expr, @@ -2565,11 +2680,7 @@ fn print_disambiguation_help<'tcx>( let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { let args = format!( "({}{})", - if rcvr_ty.is_region_ptr() { - if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" } - } else { - "" - }, + rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()), std::iter::once(receiver) .chain(args.iter()) .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 895739976..b12d84af4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -19,7 +19,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; +use rustc_trait_selection::traits::FulfillmentError; use rustc_type_ir::sty::TyKind::*; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -263,14 +263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let by_ref_binop = !op.node.is_by_value(); if is_assign == IsAssign::Yes || by_ref_binop { if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); let autoref = Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], @@ -280,14 +273,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if by_ref_binop { if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; + // Allow two-phase borrows for binops in initial deployment + // since they desugar to methods + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); + let autoref = Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[1], @@ -529,8 +518,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - self.tcx.ty_error() + let reported = err.emit(); + self.tcx.ty_error_with_guaranteed(reported) } }; @@ -556,9 +545,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; let to_owned_msg = "create an owned `String` from a string reference"; + let string_type = self.tcx.lang_items().string(); let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def() - .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) + ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type) }; match (lhs_ty.kind(), rhs_ty.kind()) { @@ -772,7 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (method, trait_did) { (Some(ok), _) => { let method = self.register_infer_ok_obligations(ok); - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); Ok(method) } (None, None) => Err(vec![]), @@ -785,9 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_ty_expr, expected, ); - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - fulfill.register_predicate_obligation(self, obligation); - Err(fulfill.select_where_possible(&self.infcx)) + Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation)) } } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ea90da4a6..decd317d9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -19,7 +19,6 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, DUMMY_SP}; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -402,6 +401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + if self.tcx.features().string_deref_patterns && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind { + let tcx = self.tcx; + let expected = self.resolve_vars_if_possible(expected); + pat_ty = match expected.kind() { + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected, + ty::Str => tcx.mk_static_str(), + _ => pat_ty, + }; + } + // Somewhat surprising: in this case, the subtyping relation goes the // opposite way as the other cases. Actually what we really want is not // a subtyping relation at all but rather that there exists a LUB @@ -693,7 +702,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut_var_suggestion = 'block: { - if !matches!(mutbl, ast::Mutability::Mut) { + if mutbl.is_not() { break 'block None; } @@ -740,7 +749,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"), vec![ (pat.span.until(inner.span), "".to_owned()), - (ty_span.shrink_to_lo(), format!("&{}", mutbl.prefix_str())), + (ty_span.shrink_to_lo(), mutbl.ref_prefix_str().to_owned()), ], Applicability::MachineApplicable ); @@ -840,12 +849,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (res, opt_ty, segments) = path_resolution; match res { Res::Err => { - self.set_tainted_by_errors(); - return tcx.ty_error(); + let e = tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + return tcx.ty_error_with_guaranteed(e); } - Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, qpath, pat.span); - return tcx.ty_error(); + Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { + let expected = "unit struct, unit variant or constant"; + let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0533", expected); + return tcx.ty_error_with_guaranteed(e); } Res::SelfCtor(..) | Res::Def( @@ -986,65 +997,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let on_error = || { + let on_error = |e| { for pat in subpats { - self.check_pat(pat, tcx.ty_error(), def_bm, ti); + self.check_pat(pat, tcx.ty_error_with_guaranteed(e), def_bm, ti); } }; let report_unexpected_res = |res: Res| { - let sm = tcx.sess.source_map(); - let path_str = sm - .span_to_snippet(sm.span_until_char(pat.span, '(')) - .map_or_else(|_| String::new(), |s| format!(" `{}`", s.trim_end())); - let msg = format!( - "expected tuple struct or tuple variant, found {}{}", - res.descr(), - path_str - ); - - let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{msg}"); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { - err.span_label(pat.span, "`fn` calls are not allowed in patterns"); - err.help( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-00-patterns.html", - ); - } - _ => { - err.span_label(pat.span, "not a tuple variant or struct"); - } - } - err.emit(); - on_error(); + let expected = "tuple struct or tuple variant"; + let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0164", expected); + on_error(e); + e }; // Resolve the path and check the definition for errors. let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); if res == Res::Err { - self.set_tainted_by_errors(); - on_error(); - return self.tcx.ty_error(); + let e = tcx.sess.delay_span_bug(pat.span, "`Res:Err` but no error emitted"); + self.set_tainted_by_errors(e); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } // Type-check the path. let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); if !pat_ty.is_fn() { - report_unexpected_res(res); - return tcx.ty_error(); + let e = report_unexpected_res(res); + return tcx.ty_error_with_guaranteed(e); } let variant = match res { Res::Err => { - self.set_tainted_by_errors(); - on_error(); - return tcx.ty_error(); + let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - report_unexpected_res(res); - return tcx.ty_error(); + let e = report_unexpected_res(res); + return tcx.ty_error_with_guaranteed(e); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -1083,9 +1075,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { // Pattern has wrong number of fields. - self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); - on_error(); - return tcx.ty_error(); + let e = self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); + on_error(e); + return tcx.ty_error_with_guaranteed(e); } pat_ty } @@ -1099,7 +1091,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [ty::FieldDef], expected: Ty<'tcx>, had_err: bool, - ) { + ) -> ErrorGuaranteed { let subpats_ending = pluralize!(subpats.len()); let fields_ending = pluralize!(fields.len()); @@ -1246,7 +1238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - err.emit(); + err.emit() } fn check_pat_tuple( @@ -1278,12 +1270,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_tys = tcx.mk_type_list(element_tys_iter); let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { - err.emit(); + let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error_with_guaranteed(reported)); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, tcx.ty_error(), def_bm, ti); + self.check_pat(elem, tcx.ty_error_with_guaranteed(reported), def_bm, ti); } tcx.mk_tup(element_tys_iter) } else { @@ -1468,8 +1460,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // if this is a tuple struct, then all field names will be numbers // so if any fields in a struct pattern use shorthand syntax, they will // be invalid identifiers (for example, Foo { 0, 1 }). - if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) = - (variant.ctor_kind, &pat.kind) + if let (Some(CtorKind::Fn), PatKind::Struct(qpath, field_patterns, ..)) = + (variant.ctor_kind(), &pat.kind) { let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); if has_shorthand_field_name { @@ -1646,7 +1638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], variant: &ty::VariantDef, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { + if let (Some(CtorKind::Fn), PatKind::Struct(qpath, ..)) = (variant.ctor_kind(), &pat.kind) { let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { s.print_qpath(qpath, false) }); @@ -2132,7 +2124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ty::Array(..) | ty::Slice(..) = ty.kind() { err.help("the semantics of slice patterns changed recently; see issue #62254"); - } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span) + } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) && let (Some(span), true) = (ti.span, ti.origin_expr) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba8cf6926..952ea1488 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -90,8 +90,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - err.emit(); - Some((self.tcx.ty_error(), self.tcx.ty_error())) + let reported = err.emit(); + Some(( + self.tcx.ty_error_with_guaranteed(reported), + self.tcx.ty_error_with_guaranteed(reported), + )) } /// To type-check `base_expr[index_expr]`, we progressively autoderef diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 4dea40829..0f4697201 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -970,12 +970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { check_trait .map(|check_trait| { self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) + .type_implements_trait(check_trait, [ty], self.param_env) .must_apply_modulo_regions() }) .unwrap_or(false), @@ -999,12 +994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { check_trait .map(|check_trait| { self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) + .type_implements_trait(check_trait, [ty], self.param_env) .must_apply_modulo_regions() }) .unwrap_or(false), @@ -1347,14 +1337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_drop_defined_for_ty = |ty: Ty<'tcx>| { let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span)); - let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]); self.infcx - .type_implements_trait( - drop_trait, - ty, - ty_params, - self.tcx.param_env(closure_def_id), - ) + .type_implements_trait(drop_trait, [ty], self.tcx.param_env(closure_def_id)) .must_apply_modulo_regions() }; @@ -2184,7 +2168,7 @@ fn determine_place_ancestry_relation<'tcx>( place_a: &Place<'tcx>, place_b: &Place<'tcx>, ) -> PlaceAncestryRelation { - // If Place A and Place B, don't start off from the same root variable, they are divergent. + // If Place A and Place B don't start off from the same root variable, they are divergent. if place_a.base != place_b.base { return PlaceAncestryRelation::Divergent; } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 1e26daa9c..52ffd286e 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -83,10 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.typeck_results.treat_byte_string_as_slice = mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - if self.is_tainted_by_errors() { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - wbcx.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + if let Some(e) = self.tainted_by_errors() { + wbcx.typeck_results.tainted_by_errors = Some(e); } debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); @@ -363,9 +361,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { intravisit::walk_ty(self, hir_ty); - let ty = self.fcx.node_ty(hir_ty.hir_id); - let ty = self.resolve(ty, &hir_ty.span); - self.write_ty_to_typeck_results(hir_ty.hir_id, ty); + // If there are type checking errors, Type privacy pass will stop, + // so we may not get the type from hid_id, see #104513 + if let Some(ty) = self.fcx.node_ty_opt(hir_ty.hir_id) { + let ty = self.resolve(ty, &hir_ty.span); + self.write_ty_to_typeck_results(hir_ty.hir_id, ty); + } } fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { @@ -514,7 +515,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() { if cfg!(debug_assertions) && c_sig.needs_infer() { span_bug!( - self.fcx.tcx.hir().span_if_local(def_id).unwrap(), + self.fcx.tcx.def_span(def_id), "writeback: `{:?}` has inference variables", c_sig ); @@ -564,7 +565,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { opaque_type_key, self.fcx.infcx.tcx, true, - decl.origin, ); self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); @@ -674,10 +674,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // We may have introduced e.g. `ty::Error`, if inference failed, make sure // to mark the `TypeckResults` as tainted in that case, so that downstream // users of the typeck results don't produce extra errors, or worse, ICEs. - if resolver.replaced_with_error { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - self.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + if let Some(e) = resolver.replaced_with_error { + self.typeck_results.tainted_by_errors = Some(e); } x @@ -708,8 +706,8 @@ struct Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, - /// Set to `true` if any `Ty` or `ty::Const` had to be replaced with an `Error`. - replaced_with_error: bool, + /// Set to `Some` if any `Ty` or `ty::Const` had to be replaced with an `Error`. + replaced_with_error: Option<ErrorGuaranteed>, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -718,12 +716,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, ) -> Resolver<'cx, 'tcx> { - Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } + Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: None } } - fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) { - if !self.tcx.sess.has_errors().is_some() { - self.infcx + fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed { + match self.tcx.sess.has_errors() { + Some(e) => e, + None => self + .infcx .err_ctxt() .emit_inference_failure_err( Some(self.body.id()), @@ -732,7 +732,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { E0282, false, ) - .emit(); + .emit(), } } } @@ -773,9 +773,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_error(t); - self.replaced_with_error = true; - self.tcx().ty_error() + let e = self.report_error(t); + self.replaced_with_error = Some(e); + self.tcx().ty_error_with_guaranteed(e) } } } @@ -790,9 +790,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - self.report_error(ct); - self.replaced_with_error = true; - self.tcx().const_error(ct.ty()) + let e = self.report_error(ct); + self.replaced_with_error = Some(e); + self.tcx().const_error_with_guaranteed(ct.ty(), e) } } } |