diff options
Diffstat (limited to '')
105 files changed, 30861 insertions, 1720 deletions
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 69ad623b7..129f8d235 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index be5b7eccb..4ef4aad90 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -45,8 +45,6 @@ pub enum NonMacroAttrKind { /// Single-segment custom attribute registered by a derive macro /// but used before that derive macro was expanded (deprecated). DeriveHelperCompat, - /// Single-segment custom attribute registered with `#[register_attr]`. - Registered, } /// What kind of definition something is; e.g., `mod` vs `struct`. @@ -111,6 +109,8 @@ pub enum DefKind { InlineConst, /// Opaque type, aka `impl Trait`. OpaqueTy, + /// A return-position `impl Trait` in a trait definition + ImplTraitPlaceholder, Field, /// Lifetime parameter: the `'a` in `struct Foo<'a> { ... }` LifetimeParam, @@ -140,6 +140,7 @@ impl DefKind { panic!("impossible struct constructor") } DefKind::OpaqueTy => "opaque type", + DefKind::ImplTraitPlaceholder => "opaque type in trait", DefKind::TyAlias => "type alias", DefKind::TraitAlias => "trait alias", DefKind::AssocTy => "associated type", @@ -219,7 +220,8 @@ impl DefKind { | DefKind::Use | DefKind::ForeignMod | DefKind::GlobalAsm - | DefKind::Impl => None, + | DefKind::Impl + | DefKind::ImplTraitPlaceholder => None, } } @@ -256,6 +258,7 @@ impl DefKind { | DefKind::Use | DefKind::ForeignMod | DefKind::OpaqueTy + | DefKind::ImplTraitPlaceholder | DefKind::Impl | DefKind::Field | DefKind::TyParam @@ -310,72 +313,76 @@ pub enum Res<Id = hir::HirId> { /// /// **Belongs to the type namespace.** PrimTy(hir::PrimTy), - /// The `Self` type, optionally with the [`DefId`] of the trait it belongs to and - /// optionally with the [`DefId`] of the item introducing the `Self` type alias. + + /// The `Self` type, as used within a trait. + /// + /// **Belongs to the type namespace.** + /// + /// See the examples on [`Res::SelfTyAlias`] for details. + SelfTyParam { + /// The trait this `Self` is a generic parameter for. + trait_: DefId, + }, + + /// The `Self` type, as used somewhere other than within a trait. /// /// **Belongs to the type namespace.** /// /// Examples: /// ``` - /// struct Bar(Box<Self>); - /// // `Res::SelfTy { trait_: None, alias_of: Some(Bar) }` + /// struct Bar(Box<Self>); // SelfTyAlias /// /// trait Foo { - /// fn foo() -> Box<Self>; - /// // `Res::SelfTy { trait_: Some(Foo), alias_of: None }` + /// fn foo() -> Box<Self>; // SelfTyParam /// } /// /// impl Bar { /// fn blah() { - /// let _: Self; - /// // `Res::SelfTy { trait_: None, alias_of: Some(::{impl#0}) }` + /// let _: Self; // SelfTyAlias /// } /// } /// /// impl Foo for Bar { - /// fn foo() -> Box<Self> { - /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }` - /// let _: Self; - /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }` + /// fn foo() -> Box<Self> { // SelfTyAlias + /// let _: Self; // SelfTyAlias /// /// todo!() /// } /// } /// ``` - /// /// *See also [`Res::SelfCtor`].* /// - /// ----- - /// - /// HACK(min_const_generics): self types also have an optional requirement to **not** mention - /// any generic parameters to allow the following with `min_const_generics`: - /// ``` - /// # struct Foo; - /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] { todo!() } } - /// - /// struct Bar([u8; baz::<Self>()]); - /// const fn baz<T>() -> usize { 10 } - /// ``` - /// We do however allow `Self` in repeat expression even if it is generic to not break code - /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint: - /// ``` - /// fn foo<T>() { - /// let _bar = [1_u8; std::mem::size_of::<*mut T>()]; - /// } - /// ``` - // FIXME(generic_const_exprs): Remove this bodge once that feature is stable. - SelfTy { - /// The trait this `Self` is a generic arg for. - trait_: Option<DefId>, + SelfTyAlias { /// The item introducing the `Self` type alias. Can be used in the `type_of` query - /// to get the underlying type. Additionally whether the `Self` type is disallowed - /// from mentioning generics (i.e. when used in an anonymous constant). - alias_to: Option<(DefId, bool)>, + /// to get the underlying type. + alias_to: DefId, + + /// Whether the `Self` type is disallowed from mentioning generics (i.e. when used in an + /// anonymous constant). + /// + /// HACK(min_const_generics): self types also have an optional requirement to **not** + /// mention any generic parameters to allow the following with `min_const_generics`: + /// ``` + /// # struct Foo; + /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] { todo!() } } + /// + /// struct Bar([u8; baz::<Self>()]); + /// const fn baz<T>() -> usize { 10 } + /// ``` + /// We do however allow `Self` in repeat expression even if it is generic to not break code + /// which already works on stable while causing the `const_evaluatable_unchecked` future + /// compat lint: + /// ``` + /// fn foo<T>() { + /// let _bar = [1_u8; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + // FIXME(generic_const_exprs): Remove this bodge once that feature is stable. + forbid_generic: bool, + + /// Is this within an `impl Foo for bar`? + is_trait_impl: bool, }, - /// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`. - /// - /// **Belongs to the type namespace.** - ToolMod, // Value namespace /// The `Self` constructor, along with the [`DefId`] @@ -383,13 +390,19 @@ pub enum Res<Id = hir::HirId> { /// /// **Belongs to the value namespace.** /// - /// *See also [`Res::SelfTy`].* + /// *See also [`Res::SelfTyParam`] and [`Res::SelfTyAlias`].* SelfCtor(DefId), + /// A local variable or function parameter. /// /// **Belongs to the value namespace.** Local(Id), + /// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`. + /// + /// **Belongs to the type namespace.** + ToolMod, + // Macro namespace /// An attribute that is *not* implemented via macro. /// E.g., `#[inline]` and `#[rustfmt::skip]`, which are essentially directives, @@ -451,11 +464,21 @@ impl PartialRes { pub fn unresolved_segments(&self) -> usize { self.unresolved_segments } + + #[inline] + pub fn full_res(&self) -> Option<Res<NodeId>> { + (self.unresolved_segments == 0).then_some(self.base_res) + } + + #[inline] + pub fn expect_full_res(&self) -> Res<NodeId> { + self.full_res().expect("unexpected unresolved segments") + } } /// Different kinds of symbols can coexist even if they share the same textual name. /// Therefore, they each have a separate universe (known as a "namespace"). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Namespace { /// The type namespace includes `struct`s, `enum`s, `union`s, `trait`s, and `mod`s /// (and, by extension, crates). @@ -564,15 +587,11 @@ impl NonMacroAttrKind { NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => { "derive helper attribute" } - NonMacroAttrKind::Registered => "explicitly registered attribute", } } pub fn article(self) -> &'static str { - match self { - NonMacroAttrKind::Registered => "an", - _ => "a", - } + "a" } /// Users of some attributes cannot mark them as used, so they are considered always used. @@ -581,7 +600,7 @@ impl NonMacroAttrKind { NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => true, - NonMacroAttrKind::Builtin(..) | NonMacroAttrKind::Registered => false, + NonMacroAttrKind::Builtin(..) => false, } } } @@ -603,7 +622,8 @@ impl<Id> Res<Id> { Res::Local(..) | Res::PrimTy(..) - | Res::SelfTy { .. } + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } | Res::SelfCtor(..) | Res::ToolMod | Res::NonMacroAttr(..) @@ -626,7 +646,7 @@ impl<Id> Res<Id> { Res::SelfCtor(..) => "self constructor", Res::PrimTy(..) => "builtin type", Res::Local(..) => "local variable", - Res::SelfTy { .. } => "self type", + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => "self type", Res::ToolMod => "tool module", Res::NonMacroAttr(attr_kind) => attr_kind.descr(), Res::Err => "unresolved item", @@ -649,7 +669,10 @@ impl<Id> Res<Id> { Res::SelfCtor(id) => Res::SelfCtor(id), Res::PrimTy(id) => Res::PrimTy(id), Res::Local(id) => Res::Local(map(id)), - Res::SelfTy { trait_, alias_to } => Res::SelfTy { trait_, alias_to }, + Res::SelfTyParam { trait_ } => Res::SelfTyParam { trait_ }, + Res::SelfTyAlias { alias_to, forbid_generic, is_trait_impl } => { + Res::SelfTyAlias { alias_to, forbid_generic, is_trait_impl } + } Res::ToolMod => Res::ToolMod, Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind), Res::Err => Res::Err, @@ -662,7 +685,10 @@ impl<Id> Res<Id> { Res::SelfCtor(id) => Res::SelfCtor(id), Res::PrimTy(id) => Res::PrimTy(id), Res::Local(id) => Res::Local(map(id)?), - Res::SelfTy { trait_, alias_to } => Res::SelfTy { trait_, alias_to }, + Res::SelfTyParam { trait_ } => Res::SelfTyParam { trait_ }, + Res::SelfTyAlias { alias_to, forbid_generic, is_trait_impl } => { + Res::SelfTyAlias { alias_to, forbid_generic, is_trait_impl } + } Res::ToolMod => Res::ToolMod, Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind), Res::Err => Res::Err, @@ -689,7 +715,9 @@ impl<Id> Res<Id> { pub fn ns(&self) -> Option<Namespace> { match self { Res::Def(kind, ..) => kind.ns(), - Res::PrimTy(..) | Res::SelfTy { .. } | Res::ToolMod => Some(Namespace::TypeNS), + Res::PrimTy(..) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::ToolMod => { + Some(Namespace::TypeNS) + } Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS), Res::NonMacroAttr(..) => Some(Namespace::MacroNS), Res::Err => None, diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index c2c551e78..d85ac960f 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -15,7 +15,6 @@ use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::{self, Write}; use std::hash::Hash; -use tracing::debug; /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. /// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` diff --git a/compiler/rustc_hir/src/errors.rs b/compiler/rustc_hir/src/errors.rs new file mode 100644 index 000000000..e593ed104 --- /dev/null +++ b/compiler/rustc_hir/src/errors.rs @@ -0,0 +1,10 @@ +use crate::LangItem; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] +pub struct LangItemError(pub LangItem); + +impl ToString for LangItemError { + fn to_string(&self) -> String { + format!("requires `{}` lang_item", self.0.name()) + } +} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 617433a98..ef00c1ffc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,13 +1,13 @@ use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::DefId; -pub(crate) use crate::hir_id::{HirId, ItemLocalId}; +pub(crate) use crate::hir_id::{HirId, ItemLocalId, OwnerId}; use crate::intravisit::FnKind; use crate::LangItem; use rustc_ast as ast; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy}; -pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; +pub use rustc_ast::{BindingAnnotation, BorrowKind, ByRef, ImplPolarity, IsAuto}; pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::fingerprint::Fingerprint; @@ -139,11 +139,10 @@ impl LifetimeName { match self { LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, - // It might seem surprising that `Fresh` counts as - // *not* elided -- but this is because, as far as the code - // in the compiler is concerned -- `Fresh` variants act - // equivalently to "some fresh name". They correspond to - // early-bound regions on an impl, in other words. + // It might seem surprising that `Fresh` counts as not *elided* + // -- but this is because, as far as the code in the compiler is + // concerned -- `Fresh` variants act equivalently to "some fresh name". + // They correspond to early-bound regions on an impl, in other words. LifetimeName::Error | LifetimeName::Param(..) | LifetimeName::Static => false, } } @@ -202,13 +201,8 @@ impl Path<'_> { pub struct PathSegment<'hir> { /// The identifier portion of this path segment. pub ident: Ident, - // `id` and `res` are optional. We currently only use these in save-analysis, - // any path segments without these will not have save-analysis info and - // therefore will not have 'jump to def' in IDEs, but otherwise will not be - // affected. (In general, we don't bother to get the defs for synthesized - // segments, only for segments which have come from the AST). - pub hir_id: Option<HirId>, - pub res: Option<Res>, + pub hir_id: HirId, + pub res: Res, /// Type/lifetime parameters attached to this path. They come in /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that @@ -226,12 +220,12 @@ pub struct PathSegment<'hir> { impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. - pub fn from_ident(ident: Ident) -> PathSegment<'hir> { - PathSegment { ident, hir_id: None, res: None, infer_args: true, args: None } + pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { + PathSegment { ident, hir_id, res, infer_args: true, args: None } } pub fn invalid() -> Self { - Self::from_ident(Ident::empty()) + Self::new(Ident::empty(), HirId::INVALID, Res::Err) } pub fn args(&self) -> &GenericArgs<'hir> { @@ -264,8 +258,8 @@ impl InferArg { #[derive(Debug, HashStable_Generic)] pub enum GenericArg<'hir> { - Lifetime(Lifetime), - Type(Ty<'hir>), + Lifetime(&'hir Lifetime), + Type(&'hir Ty<'hir>), Const(ConstArg), Infer(InferArg), } @@ -280,7 +274,7 @@ impl GenericArg<'_> { } } - pub fn id(&self) -> HirId { + pub fn hir_id(&self) -> HirId { match self { GenericArg::Lifetime(l) => l.hir_id, GenericArg::Type(t) => t.hir_id, @@ -305,9 +299,9 @@ impl GenericArg<'_> { pub fn to_ord(&self) -> ast::ParamKindOrd { match self { GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime, - GenericArg::Type(_) => ast::ParamKindOrd::Type, - GenericArg::Const(_) => ast::ParamKindOrd::Const, - GenericArg::Infer(_) => ast::ParamKindOrd::Infer, + GenericArg::Type(_) | GenericArg::Const(_) | GenericArg::Infer(_) => { + ast::ParamKindOrd::TypeOrConst + } } } @@ -435,7 +429,7 @@ pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), // FIXME(davidtwco): Introduce `PolyTraitRef::LangItem` LangItemTrait(LangItem, Span, HirId, &'hir GenericArgs<'hir>), - Outlives(Lifetime), + Outlives(&'hir Lifetime), } impl GenericBound<'_> { @@ -581,8 +575,7 @@ impl<'hir> Generics<'hir> { if self.has_where_clause_predicates { self.predicates .iter() - .filter(|p| p.in_where_clause()) - .last() + .rfind(|&p| p.in_where_clause()) .map_or(end, |p| p.span()) .shrink_to_hi() .to(end) @@ -738,6 +731,7 @@ pub enum PredicateOrigin { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). #[derive(Debug, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { + pub hir_id: HirId, pub span: Span, /// Origin of the predicate. pub origin: PredicateOrigin, @@ -761,7 +755,7 @@ impl<'hir> WhereBoundPredicate<'hir> { pub struct WhereRegionPredicate<'hir> { pub span: Span, pub in_where_clause: bool, - pub lifetime: Lifetime, + pub lifetime: &'hir Lifetime, pub bounds: GenericBounds<'hir>, } @@ -778,7 +772,6 @@ impl<'hir> WhereRegionPredicate<'hir> { /// An equality predicate (e.g., `T = int`); currently unsupported. #[derive(Debug, HashStable_Generic)] pub struct WhereEqPredicate<'hir> { - pub hir_id: HirId, pub span: Span, pub lhs_ty: &'hir Ty<'hir>, pub rhs_ty: &'hir Ty<'hir>, @@ -841,7 +834,16 @@ impl<'tcx> OwnerNodes<'tcx> { impl fmt::Debug for OwnerNodes<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OwnerNodes") + // Do not print all the pointers to all the nodes, as it would be unreadable. .field("node", &self.nodes[ItemLocalId::from_u32(0)]) + .field( + "parents", + &self + .nodes + .iter_enumerated() + .map(|(id, parented_node)| (id, parented_node.as_ref().map(|node| node.parent))) + .collect::<Vec<_>>(), + ) .field("bodies", &self.bodies) .field("local_id_to_def_id", &self.local_id_to_def_id) .field("hash_without_bodies", &self.hash_without_bodies) @@ -1050,30 +1052,6 @@ pub struct PatField<'hir> { pub span: Span, } -/// Explicit binding annotations given in the HIR for a binding. Note -/// that this is not the final binding *mode* that we infer after type -/// inference. -#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] -pub enum BindingAnnotation { - /// No binding annotation given: this means that the final binding mode - /// will depend on whether we have skipped through a `&` reference - /// when matching. For example, the `x` in `Some(x)` will have binding - /// mode `None`; if you do `let Some(x) = &Some(22)`, it will - /// ultimately be inferred to be by-reference. - /// - /// Note that implicit reference skipping is not implemented yet (#42640). - Unannotated, - - /// Annotated with `mut x` -- could be either ref or not, similar to `None`. - Mutable, - - /// Annotated as `ref`, like `ref x` - Ref, - - /// Annotated as `ref mut x`. - RefMut, -} - #[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] pub enum RangeEnd { Included, @@ -1089,6 +1067,35 @@ impl fmt::Display for RangeEnd { } } +// Equivalent to `Option<usize>`. That type takes up 16 bytes on 64-bit, but +// this type only takes up 4 bytes, at the cost of being restricted to a +// maximum value of `u32::MAX - 1`. In practice, this is more than enough. +#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable_Generic)] +pub struct DotDotPos(u32); + +impl DotDotPos { + // Panics if n >= u32::MAX. + pub fn new(n: Option<usize>) -> Self { + match n { + Some(n) => { + assert!(n < u32::MAX as usize); + Self(n as u32) + } + None => Self(u32::MAX), + } + } + + pub fn as_opt_usize(&self) -> Option<usize> { + if self.0 == u32::MAX { None } else { Some(self.0 as usize) } + } +} + +impl fmt::Debug for DotDotPos { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_opt_usize().fmt(f) + } +} + #[derive(Debug, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). @@ -1105,9 +1112,9 @@ pub enum PatKind<'hir> { Struct(QPath<'hir>, &'hir [PatField<'hir>], bool), /// A tuple struct/variant pattern `Variant(x, y, .., z)`. - /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position. + /// If the `..` pattern fragment is present, then `DotDotPos` denotes its position. /// `0 <= position <= subpats.len()` - TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], Option<usize>), + TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], DotDotPos), /// An or-pattern `A | B | C`. /// Invariant: `pats.len() >= 2`. @@ -1119,7 +1126,7 @@ pub enum PatKind<'hir> { /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position. /// `0 <= position <= subpats.len()` - Tuple(&'hir [Pat<'hir>], Option<usize>), + Tuple(&'hir [Pat<'hir>], DotDotPos), /// A `box` pattern. Box(&'hir Pat<'hir>), @@ -1322,7 +1329,7 @@ pub enum StmtKind<'hir> { Semi(&'hir Expr<'hir>), } -/// Represents a `let` statement (i.e., `let <pat>:<ty> = <expr>;`). +/// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`). #[derive(Debug, HashStable_Generic)] pub struct Local<'hir> { pub pat: &'hir Pat<'hir>, @@ -1439,7 +1446,7 @@ pub struct BodyId { #[derive(Debug, HashStable_Generic)] pub struct Body<'hir> { pub params: &'hir [Param<'hir>], - pub value: Expr<'hir>, + pub value: &'hir Expr<'hir>, pub generator_kind: Option<GeneratorKind>, } @@ -1626,7 +1633,7 @@ pub struct AnonConst { } /// An expression. -#[derive(Debug)] +#[derive(Debug, HashStable_Generic)] pub struct Expr<'hir> { pub hir_id: HirId, pub kind: ExprKind<'hir>, @@ -1882,11 +1889,11 @@ pub enum ExprKind<'hir> { /// /// The `PathSegment` represents the method name and its generic arguments /// (within the angle brackets). - /// The first element of the `&[Expr]` is the expression that evaluates + /// The `&Expr` is the expression that evaluates /// to the object on which the method is being called on (the receiver), - /// and the remaining elements are the rest of the arguments. + /// and the `&[Expr]` is the rest of the arguments. /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as - /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d], span)`. + /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, x, [a, b, c, d], span)`. /// The final `Span` represents the span of the function and arguments /// (e.g. `foo::<Bar, Baz>(a, b, c, d)` in `x.foo::<Bar, Baz>(a, b, c, d)` /// @@ -1894,7 +1901,7 @@ pub enum ExprKind<'hir> { /// the `hir_id` of the `MethodCall` node itself. /// /// [`type_dependent_def_id`]: ../../rustc_middle/ty/struct.TypeckResults.html#method.type_dependent_def_id - MethodCall(&'hir PathSegment<'hir>, &'hir [Expr<'hir>], Span), + MethodCall(&'hir PathSegment<'hir>, &'hir Expr<'hir>, &'hir [Expr<'hir>], Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(&'hir [Expr<'hir>]), /// A binary operation (e.g., `a + b`, `a * b`). @@ -2200,14 +2207,14 @@ pub struct FnSig<'hir> { // so it can fetched later. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct TraitItemId { - pub def_id: LocalDefId, + pub owner_id: OwnerId, } impl TraitItemId { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } } @@ -2218,7 +2225,7 @@ impl TraitItemId { #[derive(Debug, HashStable_Generic)] pub struct TraitItem<'hir> { pub ident: Ident, - pub def_id: LocalDefId, + pub owner_id: OwnerId, pub generics: &'hir Generics<'hir>, pub kind: TraitItemKind<'hir>, pub span: Span, @@ -2229,11 +2236,11 @@ impl TraitItem<'_> { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } pub fn trait_item_id(&self) -> TraitItemId { - TraitItemId { def_id: self.def_id } + TraitItemId { owner_id: self.owner_id } } } @@ -2264,14 +2271,14 @@ pub enum TraitItemKind<'hir> { // so it can fetched later. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct ImplItemId { - pub def_id: LocalDefId, + pub owner_id: OwnerId, } impl ImplItemId { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } } @@ -2279,7 +2286,7 @@ impl ImplItemId { #[derive(Debug, HashStable_Generic)] pub struct ImplItem<'hir> { pub ident: Ident, - pub def_id: LocalDefId, + pub owner_id: OwnerId, pub generics: &'hir Generics<'hir>, pub kind: ImplItemKind<'hir>, pub defaultness: Defaultness, @@ -2291,11 +2298,11 @@ impl ImplItem<'_> { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } pub fn impl_item_id(&self) -> ImplItemId { - ImplItemId { def_id: self.def_id } + ImplItemId { owner_id: self.owner_id } } } @@ -2308,7 +2315,7 @@ pub enum ImplItemKind<'hir> { /// An associated function implementation with the given signature and body. Fn(FnSig<'hir>, BodyId), /// An associated type. - TyAlias(&'hir Ty<'hir>), + Type(&'hir Ty<'hir>), } // The name of the associated type for `Fn` return types. @@ -2380,7 +2387,7 @@ impl TypeBinding<'_> { } } -#[derive(Debug)] +#[derive(Debug, HashStable_Generic)] pub struct Ty<'hir> { pub hir_id: HirId, pub kind: TyKind<'hir>, @@ -2397,11 +2404,44 @@ impl<'hir> Ty<'hir> { return None; }; match path.res { - Res::Def(DefKind::TyParam, def_id) - | Res::SelfTy { trait_: Some(def_id), alias_to: None } => Some((def_id, segment.ident)), + Res::Def(DefKind::TyParam, def_id) | Res::SelfTyParam { trait_: def_id } => { + Some((def_id, segment.ident)) + } _ => None, } } + + 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 + } + + pub fn find_self_aliases(&self) -> Vec<Span> { + use crate::intravisit::Visitor; + struct MyVisitor(Vec<Span>); + impl<'v> Visitor<'v> for MyVisitor { + fn visit_ty(&mut self, t: &'v Ty<'v>) { + if matches!( + &t.kind, + TyKind::Path(QPath::Resolved( + _, + Path { res: crate::def::Res::SelfTyAlias { .. }, .. }, + )) + ) { + self.0.push(t.span); + return; + } + crate::intravisit::walk_ty(self, t); + } + } + + let mut my_visitor = MyVisitor(vec![]); + my_visitor.visit_ty(self); + my_visitor.0 + } } /// Not represented directly in the AST; referred to by name through a `ty_path`. @@ -2506,6 +2546,7 @@ pub struct OpaqueTy<'hir> { pub generics: &'hir Generics<'hir>, pub bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, + pub in_trait: bool, } /// From whence the opaque type came. @@ -2529,7 +2570,7 @@ pub enum TyKind<'hir> { /// A raw pointer (i.e., `*const T` or `*mut T`). Ptr(MutTy<'hir>), /// A reference (i.e., `&'a T` or `&'a mut T`). - Rptr(Lifetime, MutTy<'hir>), + Rptr(&'hir Lifetime, MutTy<'hir>), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(&'hir BareFnTy<'hir>), /// The never type (`!`). @@ -2545,10 +2586,12 @@ pub enum TyKind<'hir> { /// /// The generic argument list contains the lifetimes (and in the future /// possibly parameters) that are actually bound on the `impl Trait`. - OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), + /// + /// The last parameter specifies whether this opaque appears in a trait definition. + OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], Lifetime, TraitObjectSyntax), + TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), /// Unused for now. Typeof(AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been @@ -2562,23 +2605,23 @@ pub enum TyKind<'hir> { pub enum InlineAsmOperand<'hir> { In { reg: InlineAsmRegOrRegClass, - expr: Expr<'hir>, + expr: &'hir Expr<'hir>, }, Out { reg: InlineAsmRegOrRegClass, late: bool, - expr: Option<Expr<'hir>>, + expr: Option<&'hir Expr<'hir>>, }, InOut { reg: InlineAsmRegOrRegClass, late: bool, - expr: Expr<'hir>, + expr: &'hir Expr<'hir>, }, SplitInOut { reg: InlineAsmRegOrRegClass, late: bool, - in_expr: Expr<'hir>, - out_expr: Option<Expr<'hir>>, + in_expr: &'hir Expr<'hir>, + out_expr: Option<&'hir Expr<'hir>>, }, Const { anon_const: AnonConst, @@ -2643,7 +2686,7 @@ pub struct FnDecl<'hir> { } /// Represents what type of implicit self a function has, if any. -#[derive(Copy, Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum ImplicitSelfKind { /// Represents a `fn x(self);`. Imm, @@ -2871,14 +2914,14 @@ impl<'hir> VariantData<'hir> { // so it can fetched later. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash, HashStable_Generic)] pub struct ItemId { - pub def_id: LocalDefId, + pub owner_id: OwnerId, } impl ItemId { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } } @@ -2888,7 +2931,7 @@ impl ItemId { #[derive(Debug, HashStable_Generic)] pub struct Item<'hir> { pub ident: Ident, - pub def_id: LocalDefId, + pub owner_id: OwnerId, pub kind: ItemKind<'hir>, pub span: Span, pub vis_span: Span, @@ -2898,11 +2941,11 @@ impl Item<'_> { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } pub fn item_id(&self) -> ItemId { - ItemId { def_id: self.def_id } + ItemId { owner_id: self.owner_id } } } @@ -2992,7 +3035,7 @@ pub enum ItemKind<'hir> { /// A MBE macro definition (`macro_rules!` or `macro`). Macro(ast::MacroDef, MacroKind), /// A module. - Mod(Mod<'hir>), + Mod(&'hir Mod<'hir>), /// An external module, e.g. `extern { .. }`. ForeignMod { abi: Abi, items: &'hir [ForeignItemRef] }, /// Module-level inline assembly (from `global_asm!`). @@ -3115,14 +3158,14 @@ pub enum AssocItemKind { // so it can fetched later. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct ForeignItemId { - pub def_id: LocalDefId, + pub owner_id: OwnerId, } impl ForeignItemId { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } } @@ -3143,7 +3186,7 @@ pub struct ForeignItemRef { pub struct ForeignItem<'hir> { pub ident: Ident, pub kind: ForeignItemKind<'hir>, - pub def_id: LocalDefId, + pub owner_id: OwnerId, pub span: Span, pub vis_span: Span, } @@ -3152,11 +3195,11 @@ impl ForeignItem<'_> { #[inline] pub fn hir_id(&self) -> HirId { // Items are always HIR owners. - HirId::make_owner(self.def_id) + HirId::make_owner(self.owner_id.def_id) } pub fn foreign_item_id(&self) -> ForeignItemId { - ForeignItemId { def_id: self.def_id } + ForeignItemId { owner_id: self.owner_id } } } @@ -3217,7 +3260,7 @@ impl<'hir> OwnerNode<'hir> { } } - pub fn fn_decl(&self) -> Option<&FnDecl<'hir>> { + pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> { match self { OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) @@ -3246,12 +3289,12 @@ impl<'hir> OwnerNode<'hir> { Node::generics(self.into()) } - pub fn def_id(self) -> LocalDefId { + pub fn def_id(self) -> OwnerId { match self { - OwnerNode::Item(Item { def_id, .. }) - | OwnerNode::TraitItem(TraitItem { def_id, .. }) - | OwnerNode::ImplItem(ImplItem { def_id, .. }) - | OwnerNode::ForeignItem(ForeignItem { def_id, .. }) => *def_id, + OwnerNode::Item(Item { owner_id, .. }) + | OwnerNode::TraitItem(TraitItem { owner_id, .. }) + | OwnerNode::ImplItem(ImplItem { owner_id, .. }) + | OwnerNode::ForeignItem(ForeignItem { owner_id, .. }) => *owner_id, OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner, } } @@ -3332,12 +3375,14 @@ pub enum Node<'hir> { Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), Expr(&'hir Expr<'hir>), + ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), PathSegment(&'hir PathSegment<'hir>), Ty(&'hir Ty<'hir>), TypeBinding(&'hir TypeBinding<'hir>), TraitRef(&'hir TraitRef<'hir>), Pat(&'hir Pat<'hir>), + PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), Local(&'hir Local<'hir>), @@ -3388,6 +3433,8 @@ impl<'hir> Node<'hir> { | Node::Block(..) | Node::Ctor(..) | Node::Pat(..) + | Node::PatField(..) + | Node::ExprField(..) | Node::Arm(..) | Node::Local(..) | Node::Crate(..) @@ -3397,19 +3444,20 @@ impl<'hir> Node<'hir> { } } - pub fn fn_decl(&self) -> Option<&'hir FnDecl<'hir>> { + pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), - Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { + Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) + | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { Some(fn_decl) } _ => None, } } - pub fn fn_sig(&self) -> Option<&'hir FnSig<'hir>> { + pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) @@ -3490,17 +3538,35 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. - rustc_data_structures::static_assert_size!(Block<'static>, 48); - rustc_data_structures::static_assert_size!(Expr<'static>, 56); - rustc_data_structures::static_assert_size!(ForeignItem<'static>, 72); - rustc_data_structures::static_assert_size!(GenericBound<'_>, 48); - rustc_data_structures::static_assert_size!(Generics<'static>, 56); - rustc_data_structures::static_assert_size!(ImplItem<'static>, 88); - rustc_data_structures::static_assert_size!(Impl<'static>, 80); - rustc_data_structures::static_assert_size!(Item<'static>, 80); - rustc_data_structures::static_assert_size!(Pat<'static>, 88); - rustc_data_structures::static_assert_size!(QPath<'static>, 24); - rustc_data_structures::static_assert_size!(TraitItem<'static>, 96); - rustc_data_structures::static_assert_size!(Ty<'static>, 72); + // tidy-alphabetical-start + static_assert_size!(Block<'_>, 48); + static_assert_size!(Body<'_>, 32); + static_assert_size!(Expr<'_>, 64); + static_assert_size!(ExprKind<'_>, 48); + static_assert_size!(FnDecl<'_>, 40); + static_assert_size!(ForeignItem<'_>, 72); + static_assert_size!(ForeignItemKind<'_>, 40); + static_assert_size!(GenericArg<'_>, 24); + static_assert_size!(GenericBound<'_>, 48); + static_assert_size!(Generics<'_>, 56); + static_assert_size!(Impl<'_>, 80); + static_assert_size!(ImplItem<'_>, 80); + static_assert_size!(ImplItemKind<'_>, 32); + static_assert_size!(Item<'_>, 80); + static_assert_size!(ItemKind<'_>, 48); + static_assert_size!(Local<'_>, 64); + static_assert_size!(Param<'_>, 32); + static_assert_size!(Pat<'_>, 72); + static_assert_size!(Path<'_>, 40); + static_assert_size!(PathSegment<'_>, 48); + static_assert_size!(PatKind<'_>, 48); + static_assert_size!(QPath<'_>, 24); + static_assert_size!(Res, 12); + static_assert_size!(Stmt<'_>, 32); + static_assert_size!(StmtKind<'_>, 16); + static_assert_size!(TraitItem<'_>, 88); + static_assert_size!(TraitItemKind<'_>, 48); + 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 346ac9e96..752f760ea 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -1,6 +1,43 @@ -use crate::def_id::{LocalDefId, CRATE_DEF_ID}; +use crate::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_span::{def_id::DefPathHash, HashStableContext}; use std::fmt; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Encodable, Decodable)] +pub struct OwnerId { + pub def_id: LocalDefId, +} + +impl From<OwnerId> for HirId { + fn from(owner: OwnerId) -> HirId { + HirId { owner, local_id: ItemLocalId::from_u32(0) } + } +} + +impl OwnerId { + #[inline] + pub fn to_def_id(self) -> DefId { + self.def_id.to_def_id() + } +} + +impl<CTX: HashStableContext> HashStable<CTX> for OwnerId { + #[inline] + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); + } +} + +impl<CTX: HashStableContext> ToStableHashKey<CTX> for OwnerId { + type KeyType = DefPathHash; + + #[inline] + fn to_stable_hash_key(&self, hcx: &CTX) -> DefPathHash { + hcx.def_path_hash(self.to_def_id()) + } +} + /// Uniquely identifies a node in the HIR of the current crate. It is /// composed of the `owner`, which is the `LocalDefId` of the directly enclosing /// `hir::Item`, `hir::TraitItem`, or `hir::ImplItem` (i.e., the closest "item-like"), @@ -15,19 +52,23 @@ use std::fmt; #[derive(Encodable, Decodable, HashStable_Generic)] #[rustc_pass_by_value] pub struct HirId { - pub owner: LocalDefId, + pub owner: OwnerId, pub local_id: ItemLocalId, } impl HirId { + /// Signal local id which should never be used. + pub const INVALID: HirId = + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::INVALID }; + #[inline] - pub fn expect_owner(self) -> LocalDefId { + pub fn expect_owner(self) -> OwnerId { assert_eq!(self.local_id.index(), 0); self.owner } #[inline] - pub fn as_owner(self) -> Option<LocalDefId> { + pub fn as_owner(self) -> Option<OwnerId> { if self.local_id.index() == 0 { Some(self.owner) } else { None } } @@ -38,11 +79,14 @@ impl HirId { #[inline] pub fn make_owner(owner: LocalDefId) -> Self { - Self { owner, local_id: ItemLocalId::from_u32(0) } + Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::from_u32(0) } } pub fn index(self) -> (usize, usize) { - (rustc_index::vec::Idx::index(self.owner), rustc_index::vec::Idx::index(self.local_id)) + ( + rustc_index::vec::Idx::index(self.owner.def_id), + rustc_index::vec::Idx::index(self.local_id), + ) } } @@ -64,8 +108,13 @@ impl PartialOrd for HirId { } } -rustc_data_structures::define_id_collections!(HirIdMap, HirIdSet, HirId); -rustc_data_structures::define_id_collections!(ItemLocalMap, ItemLocalSet, ItemLocalId); +rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); +rustc_data_structures::define_id_collections!( + ItemLocalMap, + ItemLocalSet, + ItemLocalMapEntry, + ItemLocalId +); rustc_index::newtype_index! { /// An `ItemLocalId` uniquely identifies something within a given "item-like"; @@ -86,4 +135,7 @@ impl ItemLocalId { } /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. -pub const CRATE_HIR_ID: HirId = HirId { owner: CRATE_DEF_ID, local_id: ItemLocalId::from_u32(0) }; +pub const CRATE_HIR_ID: HirId = + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) }; + +pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index e676acebe..be77e6fd3 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -298,7 +298,7 @@ pub trait Visitor<'v>: Sized { fn visit_id(&mut self, _hir_id: HirId) { // Nothing to do. } - fn visit_name(&mut self, _span: Span, _name: Symbol) { + fn visit_name(&mut self, _name: Symbol) { // Nothing to do. } fn visit_ident(&mut self, ident: Ident) { @@ -325,6 +325,9 @@ pub trait Visitor<'v>: Sized { fn visit_pat(&mut self, p: &'v Pat<'v>) { walk_pat(self, p) } + fn visit_pat_field(&mut self, f: &'v PatField<'v>) { + walk_pat_field(self, f) + } fn visit_array_length(&mut self, len: &'v ArrayLen) { walk_array_len(self, len) } @@ -337,6 +340,9 @@ pub trait Visitor<'v>: Sized { fn visit_let_expr(&mut self, lex: &'v Let<'v>) { walk_let_expr(self, lex) } + fn visit_expr_field(&mut self, field: &'v ExprField<'v>) { + walk_expr_field(self, field) + } fn visit_ty(&mut self, t: &'v Ty<'v>) { walk_ty(self, t) } @@ -355,8 +361,8 @@ pub trait Visitor<'v>: Sized { 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, s: Span, id: HirId) { - walk_fn(self, fk, fd, b, s, id) + 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) { walk_use(self, path, hir_id) @@ -382,33 +388,20 @@ pub trait Visitor<'v>: Sized { fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) { walk_param_bound(self, bounds) } - fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>, m: TraitBoundModifier) { - walk_poly_trait_ref(self, t, m) - } - fn visit_variant_data( - &mut self, - s: &'v VariantData<'v>, - _: Symbol, - _: &'v Generics<'v>, - _parent_id: HirId, - _: Span, - ) { + fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>) { + walk_poly_trait_ref(self, t) + } + fn visit_variant_data(&mut self, s: &'v VariantData<'v>) { walk_struct_def(self, s) } fn visit_field_def(&mut self, s: &'v FieldDef<'v>) { walk_field_def(self, s) } - fn visit_enum_def( - &mut self, - enum_definition: &'v EnumDef<'v>, - generics: &'v Generics<'v>, - item_id: HirId, - _: Span, - ) { - walk_enum_def(self, enum_definition, generics, item_id) + fn visit_enum_def(&mut self, enum_definition: &'v EnumDef<'v>, item_id: HirId) { + walk_enum_def(self, enum_definition, item_id) } - fn visit_variant(&mut self, v: &'v Variant<'v>, g: &'v Generics<'v>, item_id: HirId) { - walk_variant(self, v, g, item_id) + fn visit_variant(&mut self, v: &'v Variant<'v>) { + walk_variant(self, v) } fn visit_label(&mut self, label: &'v Label) { walk_label(self, label) @@ -427,17 +420,18 @@ pub trait Visitor<'v>: Sized { fn visit_lifetime(&mut self, lifetime: &'v Lifetime) { walk_lifetime(self, lifetime) } - fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, span: Span) { - walk_qpath(self, qpath, id, span) + // The span is that of the surrounding type/pattern/expr/whatever. + 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) { walk_path(self, path) } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment<'v>) { - walk_path_segment(self, path_span, path_segment) + fn visit_path_segment(&mut self, path_segment: &'v PathSegment<'v>) { + walk_path_segment(self, path_segment) } - fn visit_generic_args(&mut self, path_span: Span, generic_args: &'v GenericArgs<'v>) { - walk_generic_args(self, path_span, generic_args) + fn visit_generic_args(&mut self, generic_args: &'v GenericArgs<'v>) { + walk_generic_args(self, generic_args) } fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding<'v>) { walk_assoc_type_binding(self, type_binding) @@ -479,7 +473,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { } pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) { - visitor.visit_name(ident.span, ident.name); + visitor.visit_name(ident.name); } pub fn walk_label<'v, V: Visitor<'v>>(visitor: &mut V, label: &'v Label) { @@ -501,11 +495,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime } } -pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>( - visitor: &mut V, - trait_ref: &'v PolyTraitRef<'v>, - _modifier: TraitBoundModifier, -) { +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); } @@ -526,7 +516,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { ItemKind::ExternCrate(orig_name) => { visitor.visit_id(item.hir_id()); if let Some(orig_name) = orig_name { - visitor.visit_name(item.span, orig_name); + visitor.visit_name(orig_name); } } ItemKind::Use(ref path, _) => { @@ -572,7 +562,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { ItemKind::Enum(ref enum_definition, ref generics) => { visitor.visit_generics(generics); // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`. - visitor.visit_enum_def(enum_definition, generics, item.hir_id(), item.span) + visitor.visit_enum_def(enum_definition, item.hir_id()) } ItemKind::Impl(Impl { unsafety: _, @@ -595,13 +585,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { | ItemKind::Union(ref struct_definition, ref generics) => { visitor.visit_generics(generics); visitor.visit_id(item.hir_id()); - visitor.visit_variant_data( - struct_definition, - item.ident.name, - generics, - item.hir_id(), - item.span, - ); + visitor.visit_variant_data(struct_definition); } ItemKind::Trait(.., ref generics, bounds, trait_item_refs) => { visitor.visit_id(item.hir_id()); @@ -649,28 +633,16 @@ pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: pub fn walk_enum_def<'v, V: Visitor<'v>>( visitor: &mut V, enum_definition: &'v EnumDef<'v>, - generics: &'v Generics<'v>, item_id: HirId, ) { visitor.visit_id(item_id); - walk_list!(visitor, visit_variant, enum_definition.variants, generics, item_id); + walk_list!(visitor, visit_variant, enum_definition.variants); } -pub fn walk_variant<'v, V: Visitor<'v>>( - visitor: &mut V, - variant: &'v Variant<'v>, - generics: &'v Generics<'v>, - parent_item_id: HirId, -) { +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, - variant.ident.name, - generics, - parent_item_id, - variant.span, - ); + visitor.visit_variant_data(&variant.data); walk_list!(visitor, visit_anon_const, &variant.disr_expr); } @@ -695,7 +667,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { TyKind::Path(ref qpath) => { visitor.visit_qpath(qpath, typ.hir_id, typ.span); } - TyKind::OpaqueDef(item_id, lifetimes) => { + TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { visitor.visit_nested_item(item_id); walk_list!(visitor, visit_generic_arg, lifetimes); } @@ -705,7 +677,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { } TyKind::TraitObject(bounds, ref lifetime, _syntax) => { for bound in bounds { - visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None); + visitor.visit_poly_trait_ref(bound); } visitor.visit_lifetime(lifetime); } @@ -718,12 +690,7 @@ 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, - span: Span, -) { +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); @@ -731,7 +698,7 @@ pub fn walk_qpath<'v, V: Visitor<'v>>( } QPath::TypeRelative(ref qself, ref segment) => { visitor.visit_ty(qself); - visitor.visit_path_segment(span, segment); + visitor.visit_path_segment(segment); } QPath::LangItem(..) => {} } @@ -739,27 +706,19 @@ pub fn walk_qpath<'v, V: Visitor<'v>>( pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) { for segment in path.segments { - visitor.visit_path_segment(path.span, segment); + visitor.visit_path_segment(segment); } } -pub fn walk_path_segment<'v, V: Visitor<'v>>( - visitor: &mut V, - path_span: Span, - segment: &'v PathSegment<'v>, -) { +pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V, segment: &'v PathSegment<'v>) { visitor.visit_ident(segment.ident); - walk_list!(visitor, visit_id, segment.hir_id); + visitor.visit_id(segment.hir_id); if let Some(ref args) = segment.args { - visitor.visit_generic_args(path_span, args); + visitor.visit_generic_args(args); } } -pub fn walk_generic_args<'v, V: Visitor<'v>>( - visitor: &mut V, - _path_span: Span, - generic_args: &'v GenericArgs<'v>, -) { +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); } @@ -770,7 +729,7 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( ) { visitor.visit_id(type_binding.hir_id); visitor.visit_ident(type_binding.ident); - visitor.visit_generic_args(type_binding.span, type_binding.gen_args); + 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), @@ -792,11 +751,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { } PatKind::Struct(ref qpath, fields, _) => { visitor.visit_qpath(qpath, pattern.hir_id, pattern.span); - for field in fields { - visitor.visit_id(field.hir_id); - visitor.visit_ident(field.ident); - visitor.visit_pat(&field.pat) - } + walk_list!(visitor, visit_pat_field, fields); } PatKind::Or(pats) => walk_list!(visitor, visit_pat, pats), PatKind::Tuple(tuple_elements, _) => { @@ -823,6 +778,12 @@ 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) +} + 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); @@ -842,12 +803,12 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v 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, modifier); + GenericBound::Trait(ref typ, _modifier) => { + visitor.visit_poly_trait_ref(typ); } - GenericBound::LangItemTrait(_, span, hir_id, args) => { + GenericBound::LangItemTrait(_, _span, hir_id, args) => { visitor.visit_id(hir_id); - visitor.visit_generic_args(span, args); + visitor.visit_generic_args(args); } GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), } @@ -886,23 +847,28 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( ) { match *predicate { WherePredicate::BoundPredicate(WhereBoundPredicate { + hir_id, ref bounded_ty, bounds, bound_generic_params, - .. + origin: _, + span: _, }) => { + visitor.visit_id(hir_id); visitor.visit_ty(bounded_ty); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } - WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => { + WherePredicate::RegionPredicate(WhereRegionPredicate { + ref lifetime, + bounds, + span: _, + in_where_clause: _, + }) => { visitor.visit_lifetime(lifetime); walk_list!(visitor, visit_param_bound, bounds); } - WherePredicate::EqPredicate(WhereEqPredicate { - hir_id, ref lhs_ty, ref rhs_ty, .. - }) => { - visitor.visit_id(hir_id); + WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, span: _ }) => { visitor.visit_ty(lhs_ty); visitor.visit_ty(rhs_ty); } @@ -936,7 +902,6 @@ pub fn walk_fn<'v, V: Visitor<'v>>( function_kind: FnKind<'v>, function_declaration: &'v FnDecl<'v>, body_id: BodyId, - _span: Span, id: HirId, ) { visitor.visit_id(id); @@ -947,7 +912,7 @@ pub fn walk_fn<'v, V: Visitor<'v>>( 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, def_id: _ } = *trait_item; + 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); @@ -987,7 +952,7 @@ pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref: pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) { // N.B., deliberately force a compilation error if/when new fields are added. let ImplItem { - def_id: _, + owner_id: _, ident, ref generics, ref kind, @@ -1014,7 +979,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt impl_item.hir_id(), ); } - ImplItemKind::TyAlias(ref ty) => { + ImplItemKind::Type(ref ty) => { visitor.visit_id(impl_item.hir_id()); visitor.visit_ty(ty); } @@ -1090,6 +1055,12 @@ pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) 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_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { visitor.visit_id(expression.hir_id); match expression.kind { @@ -1104,11 +1075,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Struct(ref qpath, fields, ref optional_base) => { visitor.visit_qpath(qpath, expression.hir_id, expression.span); - for field in fields { - visitor.visit_id(field.hir_id); - visitor.visit_ident(field.ident); - visitor.visit_expr(&field.expr) - } + walk_list!(visitor, visit_expr_field, fields); walk_list!(visitor, visit_expr, optional_base); } ExprKind::Tup(subexpressions) => { @@ -1118,8 +1085,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(callee_expression); walk_list!(visitor, visit_expr, arguments); } - ExprKind::MethodCall(ref segment, arguments, _) => { - visitor.visit_path_segment(expression.span, segment); + 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) => { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c337be12a..ca615a491 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -8,6 +8,7 @@ //! * Functions called by the compiler itself. use crate::def_id::DefId; +use crate::errors::LangItemError; use crate::{MethodKind, Target}; use rustc_ast as ast; @@ -115,9 +116,9 @@ macro_rules! language_item_table { /// 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 message as a string. - pub fn require(&self, it: LangItem) -> Result<DefId, String> { - self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", 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. @@ -192,7 +193,8 @@ language_item_table! { DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); // language items relating to transmutability - TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6); + 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); @@ -236,7 +238,6 @@ language_item_table! { Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None; Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); - GeneratorReturn, sym::generator_return, generator_return, Target::AssocTy, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; @@ -267,8 +268,6 @@ language_item_table! { 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; - ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4); - ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4); Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); @@ -290,6 +289,8 @@ language_item_table! { Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None; + Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0); + SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; // Language items from AST lowering diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 0f9e6fa7b..1c4aa420c 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -4,18 +4,22 @@ #![feature(associated_type_defaults)] #![feature(closure_track_caller)] -#![feature(const_btree_new)] -#![feature(let_else)] +#![feature(const_btree_len)] #![feature(once_cell)] #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] #![recursion_limit = "256"] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_macros; #[macro_use] +extern crate tracing; + +#[macro_use] extern crate rustc_data_structures; extern crate self as rustc_hir; @@ -25,6 +29,7 @@ pub mod def; pub mod def_path_hash_map; pub mod definitions; pub mod diagnostic_items; +pub mod errors; pub use rustc_span::def_id; mod hir; pub mod hir_id; diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 93112199b..0c1819bb0 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,6 +1,6 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::DefId; -use crate::hir::{self, HirId, PatKind}; +use crate::hir::{self, BindingAnnotation, ByRef, HirId, PatKind}; use rustc_data_structures::fx::FxHashSet; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::Ident; @@ -35,7 +35,7 @@ pub trait EnumerateAndAdjustIterator { fn enumerate_and_adjust( self, expected_len: usize, - gap_pos: Option<usize>, + gap_pos: hir::DotDotPos, ) -> EnumerateAndAdjust<Self> where Self: Sized; @@ -45,7 +45,7 @@ impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T { fn enumerate_and_adjust( self, expected_len: usize, - gap_pos: Option<usize>, + gap_pos: hir::DotDotPos, ) -> EnumerateAndAdjust<Self> where Self: Sized, @@ -53,7 +53,7 @@ impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T { let actual_len = self.len(); EnumerateAndAdjust { enumerate: self.enumerate(), - gap_pos: gap_pos.unwrap_or(expected_len), + gap_pos: gap_pos.as_opt_usize().unwrap_or(expected_len), gap_len: expected_len - actual_len, } } @@ -93,12 +93,7 @@ impl hir::Pat<'_> { pub fn simple_ident(&self) -> Option<Ident> { match self.kind { - PatKind::Binding( - hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, - _, - ident, - None, - ) => Some(ident), + PatKind::Binding(BindingAnnotation(ByRef::No, _), _, ident, None) => Some(ident), _ => None, } } @@ -135,11 +130,11 @@ 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 { + hir::BindingAnnotation::REF => match result { None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not), _ => {} }, - hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut), + hir::BindingAnnotation::REF_MUT => result = Some(hir::Mutability::Mut), _ => {} }); result diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 8ccd59e8e..23423e8f3 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,8 +1,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use crate::hir::{ - AttributeMap, BodyId, Crate, Expr, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, - Ty, + AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; use crate::hir_id::{HirId, ItemLocalId}; use rustc_span::def_id::DefPathHash; @@ -14,8 +13,6 @@ pub trait HashStableContext: rustc_ast::HashStableContext + rustc_target::HashStableContext { fn hash_body_id(&mut self, _: BodyId, hasher: &mut StableHasher); - fn hash_hir_expr(&mut self, _: &Expr<'_>, hasher: &mut StableHasher); - fn hash_hir_ty(&mut self, _: &Ty<'_>, hasher: &mut StableHasher); } impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId { @@ -23,7 +20,7 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId { #[inline] fn to_stable_hash_key(&self, hcx: &HirCtx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.to_stable_hash_key(hcx); + let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); (def_path_hash, self.local_id) } } @@ -52,7 +49,7 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for ItemId { #[inline] fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { - self.def_id.to_stable_hash_key(hcx) + self.owner_id.def_id.to_stable_hash_key(hcx) } } @@ -61,7 +58,7 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for TraitItemId { #[inline] fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { - self.def_id.to_stable_hash_key(hcx) + self.owner_id.def_id.to_stable_hash_key(hcx) } } @@ -70,7 +67,7 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for ImplItemId { #[inline] fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { - self.def_id.to_stable_hash_key(hcx) + self.owner_id.def_id.to_stable_hash_key(hcx) } } @@ -79,7 +76,7 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for ForeignItemId #[inline] fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { - self.def_id.to_stable_hash_key(hcx) + self.owner_id.def_id.to_stable_hash_key(hcx) } } @@ -96,18 +93,6 @@ impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for BodyId { // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Expr<'_> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - hcx.hash_hir_expr(self, hasher) - } -} - -impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Ty<'_> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - hcx.hash_hir_ty(self, hasher) - } -} - impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'tcx> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { // We ignore the `nodes` and `bodies` fields since these refer to information included in diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 6236dea10..5917d5e34 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -36,6 +36,7 @@ pub enum Target { GlobalAsm, TyAlias, OpaqueTy, + ImplTraitPlaceholder, Enum, Variant, Struct, @@ -56,6 +57,8 @@ pub enum Target { GenericParam(GenericParamKind), MacroDef, Param, + PatField, + ExprField, } impl Display for Target { @@ -77,7 +80,13 @@ impl Target { ItemKind::ForeignMod { .. } => Target::ForeignMod, ItemKind::GlobalAsm(..) => Target::GlobalAsm, ItemKind::TyAlias(..) => Target::TyAlias, - ItemKind::OpaqueTy(..) => Target::OpaqueTy, + ItemKind::OpaqueTy(ref opaque) => { + if opaque.in_trait { + Target::ImplTraitPlaceholder + } else { + Target::OpaqueTy + } + } ItemKind::Enum(..) => Target::Enum, ItemKind::Struct(..) => Target::Struct, ItemKind::Union(..) => Target::Union, @@ -101,6 +110,7 @@ impl Target { DefKind::GlobalAsm => Target::GlobalAsm, DefKind::TyAlias => Target::TyAlias, DefKind::OpaqueTy => Target::OpaqueTy, + DefKind::ImplTraitPlaceholder => Target::ImplTraitPlaceholder, DefKind::Enum => Target::Enum, DefKind::Struct => Target::Struct, DefKind::Union => Target::Union, @@ -155,6 +165,7 @@ impl Target { Target::GlobalAsm => "global asm", Target::TyAlias => "type alias", Target::OpaqueTy => "opaque type", + Target::ImplTraitPlaceholder => "opaque type in trait", Target::Enum => "enum", Target::Variant => "enum variant", Target::Struct => "struct", @@ -183,6 +194,8 @@ impl Target { }, Target::MacroDef => "macro def", Target::Param => "function param", + Target::PatField => "pattern field", + Target::ExprField => "struct field", } } } diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index b6a85c047..da9c9c121 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -18,6 +18,12 @@ pub static WEAK_ITEMS_REFS: LazyLock<FxIndexMap<Symbol, LangItem>> = LazyLock::n 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, _)| { diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml new file mode 100644 index 000000000..0761d8cdb --- /dev/null +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "rustc_hir_analysis" +version = "0.0.0" +edition = "2021" + +[lib] +test = false +doctest = false + +[dependencies] +rustc_arena = { path = "../rustc_arena" } +tracing = "0.1" +rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } +rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_graphviz = { path = "../rustc_graphviz" } +rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } +rustc_target = { path = "../rustc_target" } +rustc_session = { path = "../rustc_session" } +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } +rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_lint = { path = "../rustc_lint" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_type_ir = { path = "../rustc_type_ir" } +rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_typeck/README.md b/compiler/rustc_hir_analysis/README.md index b61dbd8c9..b61dbd8c9 100644 --- a/compiler/rustc_typeck/README.md +++ b/compiler/rustc_hir_analysis/README.md diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs new file mode 100644 index 000000000..a9152bdc5 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -0,0 +1,411 @@ +use crate::astconv::AstConv; +use crate::errors::{ManualImplementation, MissingTypeParams}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty; +use rustc_session::parse::feature_err; +use rustc_span::lev_distance::find_best_match_for_name; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, Symbol, DUMMY_SP}; + +use std::collections::BTreeSet; + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + /// On missing type parameters, emit an E0393 error and provide a structured suggestion using + /// the type parameter's name as a placeholder. + pub(crate) fn complain_about_missing_type_params( + &self, + missing_type_params: Vec<Symbol>, + def_id: DefId, + span: Span, + empty_generic_args: bool, + ) { + if missing_type_params.is_empty() { + return; + } + + self.tcx().sess.emit_err(MissingTypeParams { + span, + def_span: self.tcx().def_span(def_id), + span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), + missing_type_params, + empty_generic_args, + }); + } + + /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit + /// an error and attempt to build a reasonable structured suggestion. + pub(crate) fn complain_about_internal_fn_trait( + &self, + span: Span, + trait_def_id: DefId, + trait_segment: &'_ hir::PathSegment<'_>, + is_impl: bool, + ) { + if self.tcx().features().unboxed_closures { + return; + } + + let trait_def = self.tcx().trait_def(trait_def_id); + if !trait_def.paren_sugar { + if trait_segment.args().parenthesized { + // For now, require that parenthetical notation be used only with `Fn()` etc. + let mut err = feature_err( + &self.tcx().sess.parse_sess, + sym::unboxed_closures, + span, + "parenthetical notation is only stable when used with `Fn`-family traits", + ); + err.emit(); + } + + return; + } + + let sess = self.tcx().sess; + + if !trait_segment.args().parenthesized { + // For now, require that parenthetical notation be used only with `Fn()` etc. + let mut err = feature_err( + &sess.parse_sess, + sym::unboxed_closures, + span, + "the precise format of `Fn`-family traits' type parameters is subject to change", + ); + // Do not suggest the other syntax if we are in trait impl: + // the desugaring would contain an associated type constraint. + if !is_impl { + let args = trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| sess.source_map().span_to_snippet(e.span)) + .collect::<Result<Vec<_>, _>>() + .map(|a| a.join(", ")), + _ => sess.source_map().span_to_snippet(ty.span), + } + .map(|s| format!("({})", s)) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + let ret = trait_segment + .args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { term }) => { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(c) => self.tcx().hir().span(c.hir_id), + }; + sess.source_map().span_to_snippet(span).ok() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + err.span_suggestion( + span, + "use parenthetical notation instead", + format!("{}{} -> {}", trait_segment.ident, args, ret), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + } + + if is_impl { + let trait_name = self.tcx().def_path_str(trait_def_id); + self.tcx().sess.emit_err(ManualImplementation { span, trait_name }); + } + } + + pub(crate) fn complain_about_assoc_type_not_found<I>( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: &str, + assoc_name: Ident, + span: Span, + ) -> ErrorGuaranteed + where + I: Iterator<Item = ty::PolyTraitRef<'tcx>>, + { + // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a + // valid span, so we point at the whole path segment instead. + let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span }; + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0220, + "associated type `{}` not found for `{}`", + assoc_name, + ty_param_name + ); + + let all_candidate_names: Vec<_> = all_candidates() + .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None }, + ) + .collect(); + + if let (Some(suggested_name), true) = ( + find_best_match_for_name(&all_candidate_names, assoc_name.name, None), + assoc_name.span != DUMMY_SP, + ) { + err.span_suggestion( + assoc_name.span, + "there is an associated type with a similar name", + suggested_name, + Applicability::MaybeIncorrect, + ); + return err.emit(); + } + + // If we didn't find a good item in the supertraits (or couldn't get + // the supertraits), like in ItemCtxt, then look more generally from + // all visible traits. If there's one clear winner, just suggest that. + + let visible_traits: Vec<_> = self + .tcx() + .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() + } + }) + .collect(); + + let wider_candidate_names: Vec<_> = visible_traits + .iter() + .flat_map(|trait_def_id| { + self.tcx().associated_items(*trait_def_id).in_definition_order() + }) + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None }, + ) + .collect(); + + if let (Some(suggested_name), true) = ( + find_best_match_for_name(&wider_candidate_names, assoc_name.name, None), + assoc_name.span != DUMMY_SP, + ) { + if let [best_trait] = visible_traits + .iter() + .filter(|trait_def_id| { + self.tcx() + .associated_items(*trait_def_id) + .filter_by_name_unhygienic(suggested_name) + .any(|item| item.kind == ty::AssocKind::Type) + }) + .collect::<Vec<_>>()[..] + { + err.span_label( + assoc_name.span, + format!( + "there is a similarly named associated type `{suggested_name}` in the trait `{}`", + self.tcx().def_path_str(*best_trait) + ), + ); + return err.emit(); + } + } + + err.span_label(span, format!("associated type `{}` not found", assoc_name)); + err.emit() + } + + /// When there are any missing associated types, emit an E0191 error and attempt to supply a + /// reasonable suggestion on how to write it. For the case of multiple associated types in the + /// same trait bound have the same name (as they come from different supertraits), we instead + /// emit a generic note suggesting using a `where` clause to constraint instead. + pub(crate) fn complain_about_missing_associated_types( + &self, + associated_types: FxHashMap<Span, BTreeSet<DefId>>, + potential_assoc_types: Vec<Span>, + trait_bounds: &[hir::PolyTraitRef<'_>], + ) { + if associated_types.values().all(|v| v.is_empty()) { + return; + } + let tcx = self.tcx(); + // FIXME: Marked `mut` so that we can replace the spans further below with a more + // appropriate one, but this should be handled earlier in the span assignment. + let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types + .into_iter() + .map(|(span, def_ids)| { + (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) + }) + .collect(); + let mut names = vec![]; + + // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and + // `issue-22560.rs`. + let mut trait_bound_spans: Vec<Span> = vec![]; + for (span, items) in &associated_types { + if !items.is_empty() { + trait_bound_spans.push(*span); + } + for assoc_item in items { + let trait_def_id = assoc_item.container_id(tcx); + names.push(format!( + "`{}` (from trait `{}`)", + assoc_item.name, + tcx.def_path_str(trait_def_id), + )); + } + } + if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { + match bound.trait_ref.path.segments { + // FIXME: `trait_ref.path.span` can point to a full path with multiple + // segments, even though `trait_ref.path.segments` is of length `1`. Work + // around that bug here, even though it should be fixed elsewhere. + // This would otherwise cause an invalid suggestion. For an example, look at + // `src/test/ui/issues/issue-28344.rs` where instead of the following: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^ help: specify the associated type: + // | `BitXor<Output = Type>` + // + // we would output: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^^^^^^^^ help: specify the associated type: + // | `BitXor::bitor<Output = Type>` + [segment] if segment.args.is_none() => { + trait_bound_spans = vec![segment.ident.span]; + associated_types = associated_types + .into_iter() + .map(|(_, items)| (segment.ident.span, items)) + .collect(); + } + _ => {} + } + } + names.sort(); + trait_bound_spans.sort(); + let mut err = struct_span_err!( + tcx.sess, + trait_bound_spans, + E0191, + "the value of the associated type{} {} must be specified", + pluralize!(names.len()), + names.join(", "), + ); + let mut suggestions = vec![]; + let mut types_count = 0; + let mut where_constraints = vec![]; + let mut already_has_generics_args_suggestion = false; + for (span, assoc_items) in &associated_types { + let mut names: FxHashMap<_, usize> = FxHashMap::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut dupes = false; + for item in assoc_items { + let prefix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + dupes = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + err.span_label(sp, format!("`{}{}` defined here", prefix, item.name)); + } + } + if potential_assoc_types.len() == assoc_items.len() { + // When the amount of missing associated types equals the number of + // extra type arguments present. A suggesting to replace the generic args with + // associated types is already emitted. + already_has_generics_args_suggestion = true; + } else if let (Ok(snippet), false) = + (tcx.sess.source_map().span_to_snippet(*span), dupes) + { + let types: Vec<_> = + assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); + let code = if snippet.ends_with('>') { + // The user wrote `Trait<'a>` or similar and we don't have a type we can + // suggest, but at least we can clue them to the correct syntax + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the + // suggestion. + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator<Item = Type>`. + format!("{}<{}>", snippet, types.join(", ")) + }; + suggestions.push((*span, code)); + } else if dupes { + where_constraints.push(*span); + } + } + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ + using the fully-qualified path to the associated types"; + if !where_constraints.is_empty() && suggestions.is_empty() { + // If there are duplicates associated type names and a single trait bound do not + // use structured suggestion, it means that there are multiple supertraits with + // the same associated type name. + err.help(where_msg); + } + if suggestions.len() != 1 || already_has_generics_args_suggestion { + // We don't need this label if there's an inline suggestion, show otherwise. + for (span, assoc_items) in &associated_types { + let mut names: FxHashMap<_, usize> = FxHashMap::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut label = vec![]; + for item in assoc_items { + let postfix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.name, postfix)); + } + if !label.is_empty() { + err.span_label( + *span, + format!( + "associated type{} {} must be specified", + pluralize!(label.len()), + label.join(", "), + ), + ); + } + } + } + if !suggestions.is_empty() { + err.multipart_suggestion( + &format!("specify the associated type{}", pluralize!(types_count)), + suggestions, + Applicability::HasPlaceholders, + ); + if !where_constraints.is_empty() { + err.span_help(where_constraints, where_msg); + } + } + err.emit(); + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs new file mode 100644 index 000000000..47915b4bd --- /dev/null +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -0,0 +1,662 @@ +use super::IsMethodCall; +use crate::astconv::{ + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, GenericArgPosition, +}; +use crate::errors::AssocTypeBindingNotAllowed; +use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; +use rustc_ast::ast::ParamKindOrd; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; +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, +}; +use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; +use rustc_span::{symbol::kw, Span}; +use smallvec::SmallVec; + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + /// Report an error that a generic argument did not match the generic parameter that was + /// expected. + fn generic_arg_mismatch_err( + tcx: TyCtxt<'_>, + arg: &GenericArg<'_>, + param: &GenericParamDef, + possible_ordering_error: bool, + help: Option<&str>, + ) { + let sess = tcx.sess; + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + param.kind.descr(), + ); + + if let GenericParamDefKind::Const { .. } = param.kind { + if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { + err.help("const arguments cannot yet be inferred with `_`"); + if sess.is_nightly_build() { + err.help( + "add `#![feature(generic_arg_infer)]` to the crate attributes to enable", + ); + } + } + } + + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ + surround it with braces", + suggestions, + Applicability::MaybeIncorrect, + ); + }; + + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), + .. + }), + GenericParamDefKind::Const { .. }, + ) => match path.res { + Res::Err => { + add_braces_suggestion(arg, &mut err); + err.set_primary_message( + "unresolved item provided when a constant was expected", + ) + .emit(); + return; + } + 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)); + if param_type.is_suggestable(tcx, false) { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type parameter to be a `const` generic", + format!("const {}: {}", param_name, param_type), + Applicability::MaybeIncorrect, + ); + }; + } + } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const { .. }, + ) => add_braces_suggestion(arg, &mut err), + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { + let body = tcx.hir().body(cnst.value.body); + if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = + body.value.kind + { + if let Res::Def(DefKind::Fn { .. }, id) = path.res { + err.help(&format!( + "`{}` is a function item, not a type", + tcx.item_name(id) + )); + err.help("function item types cannot be named directly"); + } + } + } + _ => {} + } + + let kind_ord = param.kind.to_ord(); + let arg_ord = arg.to_ord(); + + // This note is only true when generic parameters are strictly ordered by their kind. + if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + if let Some(help) = help { + err.help(help); + } + } + + err.emit(); + } + + /// Creates the relevant generic argument substitutions + /// corresponding to a set of generic parameters. This is a + /// rather complex function. Let us try to explain the role + /// of each of its parameters: + /// + /// To start, we are given the `def_id` of the thing we are + /// creating the substitutions for, and a partial set of + /// substitutions `parent_substs`. In general, the substitutions + /// for an item begin with substitutions for all the "parents" of + /// that item -- e.g., for a method it might include the + /// parameters from the impl. + /// + /// Therefore, the method begins by walking down these parents, + /// starting with the outermost parent and proceed inwards until + /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` + /// first to see if the parent's substitutions are listed in there. If so, + /// we can append those and move on. Otherwise, it invokes the + /// three callback functions: + /// + /// - `args_for_def_id`: given the `DefId` `P`, supplies back the + /// generic arguments that were given to that parent from within + /// the path; so e.g., if you have `<T as Foo>::Bar`, the `DefId` + /// might refer to the trait `Foo`, and the arguments might be + /// `[T]`. The boolean value indicates whether to infer values + /// for arguments whose values were not explicitly provided. + /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, + /// instantiate a `GenericArg`. + /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then + /// creates a suitable inference variable. + pub fn create_substs_for_generic_args<'a>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + has_self: bool, + self_ty: Option<Ty<'tcx>>, + arg_count: &GenericArgCountResult, + ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, + ) -> SubstsRef<'tcx> { + // Collect the segments of the path; we need to substitute arguments + // for parameters throughout the entire path (wherever there are + // generic parameters). + let mut parent_defs = tcx.generics_of(def_id); + let count = parent_defs.count(); + let mut stack = vec![(def_id, parent_defs)]; + while let Some(def_id) = parent_defs.parent { + parent_defs = tcx.generics_of(def_id); + stack.push((def_id, parent_defs)); + } + + // We manually build up the substitution, rather than using convenience + // methods in `subst.rs`, so that we can iterate over the arguments and + // parameters in lock-step linearly, instead of trying to match each pair. + let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); + // Iterate over each segment of the path. + while let Some((def_id, defs)) = stack.pop() { + let mut params = defs.params.iter().peekable(); + + // If we have already computed substitutions for parents, we can use those directly. + while let Some(¶m) = params.peek() { + if let Some(&kind) = parent_substs.get(param.index as usize) { + substs.push(kind); + params.next(); + } else { + break; + } + } + + // `Self` is handled first, unless it's been handled in `parent_substs`. + if has_self { + if let Some(¶m) = params.peek() { + if param.index == 0 { + if let GenericParamDefKind::Type { .. } = param.kind { + substs.push( + self_ty + .map(|ty| ty.into()) + .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), + ); + params.next(); + } + } + } + } + + // Check whether this segment takes generic arguments and the user has provided any. + let (generic_args, infer_args) = ctx.args_for_def_id(def_id); + + let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); + let mut args = args_iter.clone().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&arg), Some(¶m)) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | ( + GenericArg::Type(_) | GenericArg::Infer(_), + GenericParamDefKind::Type { .. }, + _, + ) + | ( + GenericArg::Const(_) | GenericArg::Infer(_), + GenericParamDefKind::Const { .. }, + _, + ) => { + substs.push(ctx.provided_kind(param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + _, + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetimes. + substs.push(ctx.inferred_kind(None, param, infer_args)); + force_infer_lt = Some((arg, param)); + params.next(); + } + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args.next(); + } + (_, _, _) => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count.correct.is_ok() { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .iter() + .map(|param| (param.kind.to_ord(), param.clone())) + .collect::<Vec<(ParamKindOrd, GenericParamDef)>>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec<ParamKindOrd>, + Vec<GenericParamDef>, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + Self::generic_arg_mismatch_err( + tcx, + arg, + param, + !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord)) + .collect::<Vec<String>>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::<Vec<String>>() + .join(", ") + )), + ); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + } + } + + (Some(&arg), None) => { + // We should never be able to reach this point with well-formed input. + // There are three situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the + // user. + // 3. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let (provided_arg, param) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None); + } + + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); + params.next(); + } + + (None, None) => break, + } + } + } + + tcx.intern_substs(&substs) + } + + /// Checks that the correct number of generic arguments have been provided. + /// Used specifically for function calls. + pub fn check_generic_arg_count_for_call( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + generics: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: IsMethodCall, + ) -> GenericArgCountResult { + let empty_args = hir::GenericArgs::none(); + let gen_args = seg.args.unwrap_or(&empty_args); + let gen_pos = if is_method_call == IsMethodCall::Yes { + GenericArgPosition::MethodCall + } else { + GenericArgPosition::Value + }; + let has_self = generics.parent.is_none() && generics.has_self; + + Self::check_generic_arg_count( + tcx, + span, + def_id, + seg, + generics, + gen_args, + gen_pos, + has_self, + seg.infer_args, + ) + } + + /// Checks that the correct number of generic arguments have been provided. + /// This is used both for datatypes and function calls. + #[instrument(skip(tcx, gen_pos), level = "debug")] + pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + seg: &hir::PathSegment<'_>, + gen_params: &ty::Generics, + gen_args: &hir::GenericArgs<'_>, + gen_pos: GenericArgPosition, + has_self: bool, + infer_args: bool, + ) -> GenericArgCountResult { + let default_counts = gen_params.own_defaults(); + let param_counts = gen_params.own_counts(); + + // Subtracting from param count to ensure type params synthesized from `impl Trait` + // cannot be explicitly specified. + let synth_type_param_count = gen_params + .params + .iter() + .filter(|param| { + matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }) + }) + .count(); + let named_type_param_count = + param_counts.types - has_self as usize - synth_type_param_count; + let infer_lifetimes = + (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); + + if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { + Self::prohibit_assoc_ty_binding(tcx, b.span); + } + + let explicit_late_bound = + Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + + let mut invalid_args = vec![]; + + let mut check_lifetime_args = + |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return Ok(()); + } + + if late_bounds_ignore { + return Ok(()); + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args] + .iter() + .map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } + } else { + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } + }; + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + has_self as usize, + gen_args, + def_id, + ) + .diagnostic() + .emit(); + + Err(reported) + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = gen_args.num_lifetime_params(); + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return Ok(()); + } + + let num_default_params = expected_max - expected_min; + + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided - expected_max; + + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; + + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, + } + } else { + let num_missing_args = expected_max - provided; + + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; + + debug!(?gen_args_info); + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); + + Err(reported) + }; + + let args_correct = { + let expected_min = if infer_args { + 0 + } else { + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts + }; + debug!(?expected_min); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + + check_types_and_consts( + expected_min, + param_counts.consts + named_type_param_count, + param_counts.consts + named_type_param_count + synth_type_param_count, + gen_args.num_generic_params(), + param_counts.lifetimes + has_self as usize, + gen_args.num_lifetime_params(), + ) + }; + + GenericArgCountResult { + explicit_late_bound, + correct: lifetimes_correct.and(args_correct).map_err(|reported| { + GenericArgCountMismatch { reported: Some(reported), invalid_args } + }), + } + } + + /// Emits an error regarding forbidden type binding associations + pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); + } + + /// Prohibits explicit lifetime arguments if late-bound lifetime parameters + /// are present. This is used both for datatypes and function calls. + pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + ) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); + + if infer_lifetimes { + return ExplicitLateBound::No; + } + + if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + + if position == GenericArgPosition::Value + && args.num_lifetime_params() != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); + } else { + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].hir_id(), + multispan, + msg, + |lint| lint, + ); + } + + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs new file mode 100644 index 000000000..38f195dab --- /dev/null +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -0,0 +1,3136 @@ +//! Conversion from AST representation of types to the `ty.rs` representation. +//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an +//! instance of `AstConv`. + +mod errors; +mod generics; + +use crate::bounds::Bounds; +use crate::collect::HirPlaceholderCollector; +use crate::errors::{ + AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, + TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, +}; +use crate::middle::resolve_lifetime as rl; +use crate::require_c_abi_if_c_variadic; +use rustc_ast::TraitObjectSyntax; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{ + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError, + MultiSpan, +}; +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}; +use rustc_middle::ty::DynKind; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{ + self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable, +}; +use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; +use rustc_span::edition::Edition; +use rustc_span::lev_distance::find_best_match_for_name; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::{sym, Span}; +use rustc_target::spec::abi; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::astconv_object_safety_violations; +use rustc_trait_selection::traits::error_reporting::{ + report_object_safety_error, suggestions::NextTypeParamName, +}; +use rustc_trait_selection::traits::wf::object_region_bounds; + +use smallvec::{smallvec, SmallVec}; +use std::collections::BTreeSet; +use std::slice; + +#[derive(Debug)] +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>; + + /// 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 + /// matches `assoc_name`. This is a subset of the full set of + /// predicates. + /// + /// This is used for one specific purpose: resolving "short-hand" + /// associated type references like `T::Item`. In principle, we + /// would do that by first getting the full set of predicates in + /// scope and then filtering down to find those that apply to `T`, + /// but this can lead to cycle errors. The problem is that we have + /// to do this resolution *in order to create the predicates in + /// the first place*. Hence, we have this "special pass". + fn get_type_parameter_bounds( + &self, + span: Span, + def_id: DefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx>; + + /// Returns the lifetime to use when a lifetime is omitted (and not elided). + fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) + -> Option<ty::Region<'tcx>>; + + /// Returns the type to use when a type is omitted. + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; + + /// Returns `true` if `_` is allowed in type signatures in the current context. + fn allow_ty_infer(&self) -> bool; + + /// Returns the const to use when a const is omitted. + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> Const<'tcx>; + + /// Projecting an associated type from a (potentially) + /// higher-ranked trait reference is more complicated, because of + /// the possibility of late-bound regions appearing in the + /// associated type binding. This is not legal in function + /// signatures for that reason. In a function body, we can always + /// handle it because we can use inference variables to remove the + /// late-bound regions. + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx>; + + /// Normalize an associated type coming from the user. + 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 record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); +} + +#[derive(Debug)] +struct ConvertedBinding<'a, 'tcx> { + hir_id: hir::HirId, + item_name: Ident, + kind: ConvertedBindingKind<'a, 'tcx>, + gen_args: &'a GenericArgs<'a>, + span: Span, +} + +#[derive(Debug)] +enum ConvertedBindingKind<'a, 'tcx> { + Equality(ty::Term<'tcx>), + Constraint(&'a [hir::GenericBound<'a>]), +} + +/// New-typed boolean indicating whether explicit late-bound lifetimes +/// are present in a set of generic arguments. +/// +/// For example if we have some method `fn f<'a>(&'a self)` implemented +/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` +/// is late-bound so should not be provided explicitly. Thus, if `f` is +/// instantiated with some generic arguments providing `'a` explicitly, +/// we taint those arguments with `ExplicitLateBound::Yes` so that we +/// can provide an appropriate diagnostic later. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum ExplicitLateBound { + Yes, + No, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum IsMethodCall { + Yes, + No, +} + +/// Denotes the "position" of a generic argument, indicating if it is a generic type, +/// generic function or generic method call. +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum GenericArgPosition { + Type, + Value, // e.g., functions + MethodCall, +} + +/// A marker denoting that the generic arguments that were +/// provided did not match the respective generic parameters. +#[derive(Clone, Default, Debug)] +pub struct GenericArgCountMismatch { + /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). + pub reported: Option<ErrorGuaranteed>, + /// A list of spans of arguments provided that were not valid. + pub invalid_args: Vec<Span>, +} + +/// Decorates the result of a generic argument count mismatch +/// check with whether explicit late bounds were provided. +#[derive(Clone, Debug)] +pub struct GenericArgCountResult { + pub explicit_late_bound: ExplicitLateBound, + pub correct: Result<(), GenericArgCountMismatch>, +} + +pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> { + fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool); + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx>; + + fn inferred_kind( + &mut self, + substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> subst::GenericArg<'tcx>; +} + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + #[instrument(level = "debug", skip(self), ret)] + pub fn ast_region_to_region( + &self, + lifetime: &hir::Lifetime, + def: Option<&ty::GenericParamDef>, + ) -> ty::Region<'tcx> { + let tcx = self.tcx(); + let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id)); + + match tcx.named_region(lifetime.hir_id) { + Some(rl::Region::Static) => tcx.lifetimes.re_static, + + Some(rl::Region::LateBound(debruijn, index, def_id)) => { + let name = lifetime_name(def_id.expect_local()); + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(index), + kind: ty::BrNamed(def_id, name), + }; + tcx.mk_region(ty::ReLateBound(debruijn, br)) + } + + Some(rl::Region::EarlyBound(def_id)) => { + let name = tcx.hir().ty_param_name(def_id.expect_local()); + let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name })) + } + + Some(rl::Region::Free(scope, id)) => { + let name = lifetime_name(id.expect_local()); + tcx.mk_region(ty::ReFree(ty::FreeRegion { + scope, + bound_region: ty::BrNamed(id, name), + })) + + // (*) -- not late-bound, won't change + } + + None => { + self.re_infer(def, lifetime.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"); + + // Supply some dummy value. We don't have an + // `re_error`, annoyingly, so use `'static`. + tcx.lifetimes.re_static + }) + } + } + } + + /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, + /// returns an appropriate set of substitutions for this particular reference to `I`. + pub fn ast_path_substs_for_ty( + &self, + span: Span, + def_id: DefId, + item_segment: &hir::PathSegment<'_>, + ) -> SubstsRef<'tcx> { + let (substs, _) = self.create_substs_for_ast_path( + span, + def_id, + &[], + item_segment, + item_segment.args(), + item_segment.infer_args, + None, + None, + ); + if let Some(b) = item_segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } + + substs + } + + /// Given the type/lifetime/const arguments provided to some path (along with + /// an implicit `Self`, if this is a trait reference), returns the complete + /// set of substitutions. This may involve applying defaulted type parameters. + /// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`. + /// + /// Example: + /// + /// ```ignore (illustrative) + /// T: std::ops::Index<usize, Output = u32> + /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 + /// ``` + /// + /// 1. The `self_ty` here would refer to the type `T`. + /// 2. The path in question is the path to the trait `std::ops::Index`, + /// which will have been resolved to a `def_id` + /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type + /// parameters are returned in the `SubstsRef`, the associated type bindings like + /// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`. + /// + /// Note that the type listing given here is *exactly* what the user provided. + /// + /// For (generic) associated types + /// + /// ```ignore (illustrative) + /// <Vec<u8> as Iterable<u8>>::Iter::<'a> + /// ``` + /// + /// We have the parent substs are the substs for the parent trait: + /// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated + /// type itself: `['a]`. The returned `SubstsRef` concatenates these two + /// lists: `[Vec<u8>, u8, 'a]`. + #[instrument(level = "debug", skip(self, span), ret)] + fn create_substs_for_ast_path<'a>( + &self, + span: Span, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + seg: &hir::PathSegment<'_>, + generic_args: &'a hir::GenericArgs<'_>, + infer_args: bool, + self_ty: Option<Ty<'tcx>>, + constness: Option<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, + // whatever & would get replaced with). + + let tcx = self.tcx(); + let generics = tcx.generics_of(def_id); + debug!("generics: {:?}", generics); + + if generics.has_self { + if generics.parent.is_some() { + // The parent is a trait so it should have at least one subst + // for the `Self` type. + assert!(!parent_substs.is_empty()) + } else { + // This item (presumably a trait) needs a self-type. + assert!(self_ty.is_some()); + } + } else { + assert!(self_ty.is_none() && parent_substs.is_empty()); + } + + let arg_count = Self::check_generic_arg_count( + tcx, + span, + def_id, + seg, + generics, + generic_args, + GenericArgPosition::Type, + self_ty.is_some(), + infer_args, + ); + + // Skip processing if type has no generic parameters. + // Traits always have `Self` as a generic parameter, which means they will not return early + // here and so associated type bindings will be handled regardless of whether there are any + // non-`Self` generic parameters. + if generics.params.is_empty() { + return (tcx.intern_substs(parent_substs), arg_count); + } + + struct SubstsForAstPathCtxt<'a, 'tcx> { + astconv: &'a (dyn AstConv<'tcx> + 'a), + def_id: DefId, + generic_args: &'a GenericArgs<'a>, + span: Span, + inferred_params: Vec<Span>, + infer_args: bool, + } + + impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> { + fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) { + if did == self.def_id { + (Some(self.generic_args), self.infer_args) + } else { + // The last component of this tuple is unimportant. + (None, false) + } + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx> { + let tcx = self.astconv.tcx(); + + let mut handle_ty_args = |has_default, ty: &hir::Ty<'_>| { + if has_default { + tcx.check_optional_stability( + param.def_id, + Some(arg.hir_id()), + arg.span(), + None, + AllowUnstable::No, + |_, _| { + // Default generic parameters may not be marked + // with stability attributes, i.e. when the + // default parameter was defined at the same time + // as the rest of the type. As such, we ignore missing + // stability attributes. + }, + ); + } + if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) { + self.inferred_params.push(ty.span); + tcx.ty_error().into() + } else { + self.astconv.ast_ty_to_ty(ty).into() + } + }; + + match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + self.astconv.ast_region_to_region(lt, Some(param)).into() + } + (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { + handle_ty_args(has_default, ty) + } + (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { + handle_ty_args(has_default, &inf.to_ty()) + } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { + ty::Const::from_opt_const_arg_anon_const( + tcx, + ty::WithOptConstParam { + did: tcx.hir().local_def_id(ct.value.hir_id), + const_param_did: Some(param.def_id), + }, + ) + .into() + } + (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { + let ty = tcx.at(self.span).type_of(param.def_id); + if self.astconv.allow_ty_infer() { + self.astconv.ct_infer(ty, Some(param), inf.span).into() + } else { + self.inferred_params.push(inf.span); + tcx.const_error(ty).into() + } + } + _ => unreachable!(), + } + } + + fn inferred_kind( + &mut self, + substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> subst::GenericArg<'tcx> { + let tcx = self.astconv.tcx(); + match param.kind { + GenericParamDefKind::Lifetime => self + .astconv + .re_infer(Some(param), self.span) + .unwrap_or_else(|| { + debug!(?param, "unelided lifetime in signature"); + + // This indicates an illegal lifetime in a non-assoc-trait position + tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature"); + + // Supply some dummy value. We don't have an + // `re_error`, annoyingly, so use `'static`. + tcx.lifetimes.re_static + }) + .into(), + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // No type parameter provided, but a default exists. + let substs = substs.unwrap(); + if substs.iter().any(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => ty.references_error(), + _ => false, + }) { + // Avoid ICE #86756 when type error recovery goes awry. + return tcx.ty_error().into(); + } + self.astconv + .normalize_ty( + self.span, + EarlyBinder(tcx.at(self.span).type_of(param.def_id)) + .subst(tcx, substs), + ) + .into() + } else if infer_args { + self.astconv.ty_infer(Some(param), self.span).into() + } else { + // We've already errored above about the mismatch. + tcx.ty_error().into() + } + } + GenericParamDefKind::Const { has_default } => { + let ty = tcx.at(self.span).type_of(param.def_id); + if !infer_args && has_default { + tcx.bound_const_param_default(param.def_id) + .subst(tcx, substs.unwrap()) + .into() + } else { + if infer_args { + self.astconv.ct_infer(ty, Some(param), self.span).into() + } else { + // We've already errored above about the mismatch. + tcx.const_error(ty).into() + } + } + } + } + } + } + + let mut substs_ctx = SubstsForAstPathCtxt { + astconv: self, + def_id, + span, + generic_args, + inferred_params: vec![], + infer_args, + }; + let substs = Self::create_substs_for_generic_args( + tcx, + def_id, + parent_substs, + self_ty.is_some(), + self_ty, + &arg_count, + &mut substs_ctx, + ); + + if let Some(ty::BoundConstness::ConstIfConst) = constness + && generics.has_self && !tcx.has_attr(def_id, sym::const_trait) + { + tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } ); + } + + (substs, arg_count) + } + + fn create_assoc_bindings_for_generic_args<'a>( + &self, + generic_args: &'a hir::GenericArgs<'_>, + ) -> Vec<ConvertedBinding<'a, 'tcx>> { + // Convert associated-type bindings or constraints into a separate vector. + // Example: Given this: + // + // T: Iterator<Item = u32> + // + // The `T` is passed in as a self-type; the `Item = u32` is + // not a "type parameter" of the `Iterator` trait, but rather + // a restriction on `<T as Iterator>::Item`, so it is passed + // back separately. + let assoc_bindings = generic_args + .bindings + .iter() + .map(|binding| { + let kind = match binding.kind { + hir::TypeBindingKind::Equality { ref term } => match term { + hir::Term::Ty(ref ty) => { + 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); + ConvertedBindingKind::Equality(c.into()) + } + }, + hir::TypeBindingKind::Constraint { ref bounds } => { + ConvertedBindingKind::Constraint(bounds) + } + }; + ConvertedBinding { + hir_id: binding.hir_id, + item_name: binding.ident, + kind, + gen_args: binding.gen_args, + span: binding.span, + } + }) + .collect(); + + assoc_bindings + } + + pub fn create_substs_for_associated_item( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + parent_substs: SubstsRef<'tcx>, + ) -> SubstsRef<'tcx> { + debug!( + "create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}", + span, item_def_id, item_segment + ); + let (args, _) = self.create_substs_for_ast_path( + span, + item_def_id, + parent_substs, + item_segment, + item_segment.args(), + item_segment.infer_args, + None, + None, + ); + + if let Some(b) = item_segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } + + args + } + + /// Instantiates the path for the given trait reference, assuming that it's + /// bound to a valid trait type. Returns the `DefId` of the defining trait. + /// The type _cannot_ be a type other than a trait type. + /// + /// If the `projections` argument is `None`, then assoc type bindings like `Foo<T = X>` + /// are disallowed. Otherwise, they are pushed onto the vector given. + pub fn instantiate_mono_trait_ref( + &self, + trait_ref: &hir::TraitRef<'_>, + self_ty: Ty<'tcx>, + constness: ty::BoundConstness, + ) -> ty::TraitRef<'tcx> { + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + + self.ast_path_to_mono_trait_ref( + trait_ref.path.span, + trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), + self_ty, + trait_ref.path.segments.last().unwrap(), + true, + Some(constness), + ) + } + + fn instantiate_poly_trait_ref_inner( + &self, + hir_id: hir::HirId, + span: Span, + binding_span: Option<Span>, + constness: ty::BoundConstness, + bounds: &mut Bounds<'tcx>, + speculative: bool, + trait_ref_span: Span, + trait_def_id: DefId, + trait_segment: &hir::PathSegment<'_>, + args: &GenericArgs<'_>, + infer_args: bool, + self_ty: Ty<'tcx>, + ) -> GenericArgCountResult { + let (substs, arg_count) = self.create_substs_for_ast_path( + trait_ref_span, + trait_def_id, + &[], + trait_segment, + args, + infer_args, + Some(self_ty), + Some(constness), + ); + + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(hir_id); + debug!(?bound_vars); + + let assoc_bindings = self.create_assoc_bindings_for_generic_args(args); + + let poly_trait_ref = + ty::Binder::bind_with_vars(ty::TraitRef::new(trait_def_id, substs), bound_vars); + + debug!(?poly_trait_ref, ?assoc_bindings); + bounds.trait_bounds.push((poly_trait_ref, span, constness)); + + let mut dup_bindings = FxHashMap::default(); + for binding in &assoc_bindings { + // Specify type to assert that error was already reported in `Err` case. + let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( + hir_id, + poly_trait_ref, + binding, + bounds, + speculative, + &mut dup_bindings, + binding_span.unwrap_or(binding.span), + constness, + ); + // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). + } + + arg_count + } + + /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct + /// a full trait reference. The resulting trait reference is returned. This may also generate + /// auxiliary bounds, which are added to `bounds`. + /// + /// Example: + /// + /// ```ignore (illustrative) + /// poly_trait_ref = Iterator<Item = u32> + /// self_ty = Foo + /// ``` + /// + /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`. + /// + /// **A note on binders:** against our usual convention, there is an implied bounder around + /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. + /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` + /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be + /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, + /// however. + #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))] + pub(crate) fn instantiate_poly_trait_ref( + &self, + trait_ref: &hir::TraitRef<'_>, + span: Span, + constness: ty::BoundConstness, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + speculative: bool, + ) -> GenericArgCountResult { + let hir_id = trait_ref.hir_ref_id; + let binding_span = None; + let trait_ref_span = trait_ref.path.span; + let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); + let trait_segment = trait_ref.path.segments.last().unwrap(); + let args = trait_segment.args(); + let infer_args = trait_segment.infer_args; + + self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); + + self.instantiate_poly_trait_ref_inner( + hir_id, + span, + binding_span, + constness, + bounds, + speculative, + trait_ref_span, + trait_def_id, + trait_segment, + args, + infer_args, + self_ty, + ) + } + + pub(crate) fn instantiate_lang_item_trait_ref( + &self, + lang_item: hir::LangItem, + span: Span, + hir_id: hir::HirId, + args: &GenericArgs<'_>, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + ) { + let binding_span = Some(span); + let constness = ty::BoundConstness::NotConst; + let speculative = false; + let trait_ref_span = span; + let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span)); + let trait_segment = &hir::PathSegment::invalid(); + let infer_args = false; + + self.instantiate_poly_trait_ref_inner( + hir_id, + span, + binding_span, + constness, + bounds, + speculative, + trait_ref_span, + trait_def_id, + trait_segment, + args, + infer_args, + self_ty, + ); + } + + fn ast_path_to_mono_trait_ref( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + trait_segment: &hir::PathSegment<'_>, + is_impl: bool, + constness: Option<ty::BoundConstness>, + ) -> ty::TraitRef<'tcx> { + let (substs, _) = self.create_substs_for_ast_trait_ref( + span, + trait_def_id, + self_ty, + trait_segment, + is_impl, + constness, + ); + if let Some(b) = trait_segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + } + ty::TraitRef::new(trait_def_id, substs) + } + + #[instrument(level = "debug", skip(self, span))] + fn create_substs_for_ast_trait_ref<'a>( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + trait_segment: &'a hir::PathSegment<'a>, + is_impl: bool, + constness: Option<ty::BoundConstness>, + ) -> (SubstsRef<'tcx>, GenericArgCountResult) { + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); + + self.create_substs_for_ast_path( + span, + trait_def_id, + &[], + trait_segment, + trait_segment.args(), + trait_segment.infer_args, + Some(self_ty), + constness, + ) + } + + fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { + self.tcx() + .associated_items(trait_def_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) + .is_some() + } + fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { + self.tcx() + .associated_items(trait_def_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id) + .is_some() + } + + // 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>])>, + span: Span, + ) { + let tcx = self.tcx(); + + // Try to find an unbound in bounds. + let mut unbound = None; + let mut search_bounds = |ast_bounds: &'hir [hir::GenericBound<'hir>]| { + for ab in ast_bounds { + if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab { + if unbound.is_none() { + unbound = Some(&ptr.trait_ref); + } else { + tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span }); + } + } + } + }; + 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) { + search_bounds(pred.bounds); + } + } + } + } + + let sized_def_id = tcx.lang_items().require(LangItem::Sized); + match (&sized_def_id, unbound) { + (Ok(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 + return; + } + (_, Some(_)) => { + // There was a `?Trait` bound, but it was not `?Sized`; warn. + tcx.sess.span_warn( + span, + "default bound relaxed for a type parameter, but \ + this does nothing because the given bound is not \ + a default; only `?Sized` is supported", + ); + // Otherwise, add implicitly sized if `Sized` is available. + } + _ => { + // There was no `?Sized` bound; add implicitly sized if `Sized` is available. + } + } + if sized_def_id.is_err() { + // No lang item for `Sized`, so we can't add it as a bound. + return; + } + bounds.implicitly_sized = Some(span); + } + + /// This helper takes a *converted* parameter type (`param_ty`) + /// and an *unconverted* list of bounds: + /// + /// ```text + /// fn foo<T: Debug> + /// ^ ^^^^^ `ast_bounds` parameter, in HIR form + /// | + /// `param_ty`, in ty form + /// ``` + /// + /// It adds these `ast_bounds` into the `bounds` structure. + /// + /// **A note on binders:** there is an implied binder around + /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` + /// for more details. + #[instrument(level = "debug", skip(self, ast_bounds, bounds))] + pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>( + &self, + param_ty: Ty<'tcx>, + ast_bounds: I, + bounds: &mut Bounds<'tcx>, + bound_vars: &'tcx ty::List<ty::BoundVariableKind>, + ) { + for ast_bound in ast_bounds { + match ast_bound { + hir::GenericBound::Trait(poly_trait_ref, modifier) => { + let constness = match modifier { + hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst, + hir::TraitBoundModifier::None => ty::BoundConstness::NotConst, + hir::TraitBoundModifier::Maybe => continue, + }; + + let _ = self.instantiate_poly_trait_ref( + &poly_trait_ref.trait_ref, + poly_trait_ref.span, + constness, + param_ty, + bounds, + false, + ); + } + &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { + self.instantiate_lang_item_trait_ref( + lang_item, span, hir_id, args, param_ty, bounds, + ); + } + 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)); + } + } + } + } + + /// Translates a list of bounds from the HIR into the `Bounds` data structure. + /// The self-type for the bounds is given by `param_ty`. + /// + /// Example: + /// + /// ```ignore (illustrative) + /// fn foo<T: Bar + Baz>() { } + /// // ^ ^^^^^^^^^ ast_bounds + /// // param_ty + /// ``` + /// + /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be + /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the + /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. + /// + /// `span` should be the declaration size of the parameter. + pub(crate) fn compute_bounds( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + ) -> Bounds<'tcx> { + self.compute_bounds_inner(param_ty, ast_bounds) + } + + /// Convert the bounds in `ast_bounds` that refer to traits which define an associated type + /// named `assoc_name` into ty::Bounds. Ignore the rest. + pub(crate) fn compute_bounds_that_match_assoc_type( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + assoc_name: Ident, + ) -> Bounds<'tcx> { + let mut result = Vec::new(); + + for ast_bound in ast_bounds { + if let Some(trait_ref) = ast_bound.trait_ref() + && let Some(trait_did) = trait_ref.trait_def_id() + && self.tcx().trait_may_define_assoc_type(trait_did, assoc_name) + { + result.push(ast_bound.clone()); + } + } + + self.compute_bounds_inner(param_ty, &result) + } + + fn compute_bounds_inner( + &self, + param_ty: Ty<'tcx>, + ast_bounds: &[hir::GenericBound<'_>], + ) -> Bounds<'tcx> { + let mut bounds = Bounds::default(); + + self.add_bounds(param_ty, ast_bounds.iter(), &mut bounds, ty::List::empty()); + debug!(?bounds); + + bounds + } + + /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates + /// onto `bounds`. + /// + /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the + /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* + /// the binder (e.g., `&'a u32`) and hence may reference bound regions. + #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))] + fn add_predicates_for_ast_type_binding( + &self, + hir_ref_id: hir::HirId, + trait_ref: ty::PolyTraitRef<'tcx>, + binding: &ConvertedBinding<'_, 'tcx>, + bounds: &mut Bounds<'tcx>, + speculative: bool, + dup_bindings: &mut FxHashMap<DefId, Span>, + path_span: Span, + constness: ty::BoundConstness, + ) -> Result<(), ErrorGuaranteed> { + // Given something like `U: SomeTrait<T = X>`, we want to produce a + // predicate like `<U as SomeTrait>::T = X`. This is somewhat + // subtle in the event that `T` is defined in a supertrait of + // `SomeTrait`, because in that case we need to upcast. + // + // That is, consider this case: + // + // ``` + // trait SubTrait: SuperTrait<i32> { } + // trait SuperTrait<A> { type T; } + // + // ... B: SubTrait<T = foo> ... + // ``` + // + // We want to produce `<B as SuperTrait<i32>>::T == foo`. + + let tcx = self.tcx(); + + let candidate = + if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { + // Simple case: X is defined in the current trait. + trait_ref + } else { + // Otherwise, we have to walk through the supertraits to find + // those that do. + self.one_bound_for_assoc_type( + || traits::supertraits(tcx, trait_ref), + || trait_ref.print_only_trait_path().to_string(), + binding.item_name, + path_span, + || match binding.kind { + ConvertedBindingKind::Equality(ty) => Some(ty.to_string()), + _ => None, + }, + )? + }; + + let (assoc_ident, def_scope) = + tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), 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 find_item_of_kind = |kind| { + tcx.associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) + }; + let assoc_item = find_item_of_kind(ty::AssocKind::Type) + .or_else(|| find_item_of_kind(ty::AssocKind::Const)) + .expect("missing associated type"); + + if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { + tcx.sess + .struct_span_err( + binding.span, + &format!("{} `{}` is private", assoc_item.kind, binding.item_name), + ) + .span_label(binding.span, &format!("private {}", assoc_item.kind)) + .emit(); + } + tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); + + if !speculative { + dup_bindings + .entry(assoc_item.def_id) + .and_modify(|prev_span| { + self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.item_name, + def_path: tcx.def_path_str(assoc_item.container_id(tcx)), + }); + }) + .or_insert(binding.span); + } + + // Include substitutions for generic parameters of associated types + let projection_ty = candidate.map_bound(|trait_ref| { + let ident = Ident::new(assoc_item.name, binding.item_name.span); + let item_segment = hir::PathSegment { + ident, + hir_id: binding.hir_id, + res: Res::Err, + args: Some(binding.gen_args), + infer_args: false, + }; + + let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item( + path_span, + assoc_item.def_id, + &item_segment, + trait_ref.substs, + ); + + debug!(?substs_trait_ref_and_assoc_item); + + ty::ProjectionTy { + item_def_id: assoc_item.def_id, + substs: substs_trait_ref_and_assoc_item, + } + }); + + if !speculative { + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref or assoc_item. These are not well-formed. + // + // Example: + // + // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad + // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok + if let ConvertedBindingKind::Equality(ty) = binding.kind { + let late_bound_in_trait_ref = + tcx.collect_constrained_late_bound_regions(&projection_ty); + let late_bound_in_ty = + tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty)); + debug!(?late_bound_in_trait_ref); + debug!(?late_bound_in_ty); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + self.validate_late_bound_regions( + late_bound_in_trait_ref, + late_bound_in_ty, + |br_name| { + struct_span_err!( + tcx.sess, + binding.span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + binding.item_name, + br_name + ) + }, + ); + } + } + + match binding.kind { + ConvertedBindingKind::Equality(mut term) => { + // "Desugar" a constraint like `T: Iterator<Item = u32>` this to + // the "projection predicate" for: + // + // `<T as Iterator>::Item = u32` + let assoc_item_def_id = projection_ty.skip_binder().item_def_id; + let def_kind = tcx.def_kind(assoc_item_def_id); + match (def_kind, term.unpack()) { + (hir::def::DefKind::AssocTy, ty::TermKind::Ty(_)) + | (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (), + (_, _) => { + let got = if let Some(_) = term.ty() { "type" } else { "constant" }; + let expected = def_kind.descr(assoc_item_def_id); + tcx.sess + .struct_span_err( + binding.span, + &format!("expected {expected} bound, found {got}"), + ) + .span_note( + tcx.def_span(assoc_item_def_id), + &format!("{expected} defined here"), + ) + .emit(); + term = match def_kind { + hir::def::DefKind::AssocTy => tcx.ty_error().into(), + hir::def::DefKind::AssocConst => tcx + .const_error( + tcx.bound_type_of(assoc_item_def_id) + .subst(tcx, projection_ty.skip_binder().substs), + ) + .into(), + _ => unreachable!(), + }; + } + } + bounds.projection_bounds.push(( + projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate { + projection_ty, + term: term, + }), + binding.span, + )); + } + ConvertedBindingKind::Constraint(ast_bounds) => { + // "Desugar" a constraint like `T: Iterator<Item: Debug>` to + // + // `<T as Iterator>::Item: Debug` + // + // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = tcx.mk_ty(ty::Projection(projection_ty.skip_binder())); + self.add_bounds(param_ty, ast_bounds.iter(), bounds, candidate.bound_vars()); + } + } + Ok(()) + } + + fn ast_path_to_ty( + &self, + span: Span, + did: DefId, + item_segment: &hir::PathSegment<'_>, + ) -> Ty<'tcx> { + let substs = self.ast_path_substs_for_ty(span, did, item_segment); + self.normalize_ty( + span, + EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs), + ) + } + + fn conv_object_ty_poly_trait_ref( + &self, + span: Span, + trait_bounds: &[hir::PolyTraitRef<'_>], + lifetime: &hir::Lifetime, + borrowed: bool, + representation: DynKind, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + let mut bounds = Bounds::default(); + let mut potential_assoc_types = Vec::new(); + let dummy_self = self.tcx().types.trait_object_dummy_self; + for trait_bound in trait_bounds.iter().rev() { + if let GenericArgCountResult { + correct: + Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), + .. + } = self.instantiate_poly_trait_ref( + &trait_bound.trait_ref, + trait_bound.span, + ty::BoundConstness::NotConst, + dummy_self, + &mut bounds, + false, + ) { + potential_assoc_types.extend(cur_potential_assoc_types); + } + } + + // Expand trait aliases recursively and check that only one regular (non-auto) trait + // is used and no 'maybe' bounds are used. + let expanded_traits = + traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b))); + let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits + .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self) + .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + if regular_traits.len() > 1 { + let first_trait = ®ular_traits[0]; + let additional_trait = ®ular_traits[1]; + let mut err = struct_span_err!( + tcx.sess, + additional_trait.bottom().1, + E0225, + "only auto traits can be used as additional traits in a trait object" + ); + additional_trait.label_with_exp_info( + &mut err, + "additional non-auto trait", + "additional use", + ); + first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.help(&format!( + "consider creating a new trait with all of these as supertraits and using that \ + trait here instead: `trait NewTrait: {} {{}}`", + regular_traits + .iter() + .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .collect::<Vec<_>>() + .join(" + "), + )); + err.note( + "auto-traits like `Send` and `Sync` are traits that have special properties; \ + for more information on them, visit \ + <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>", + ); + err.emit(); + } + + if regular_traits.is_empty() && auto_traits.is_empty() { + let trait_alias_span = bounds + .trait_bounds + .iter() + .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(); + } + + // Check that there are no gross object safety violations; + // most importantly, that the supertraits don't contain `Self`, + // to avoid ICEs. + for item in ®ular_traits { + let object_safety_violations = + astconv_object_safety_violations(tcx, item.trait_ref().def_id()); + if !object_safety_violations.is_empty() { + report_object_safety_error( + tcx, + span, + item.trait_ref().def_id(), + &object_safety_violations, + ) + .emit(); + return tcx.ty_error(); + } + } + + // Use a `BTreeSet` to keep output in a more consistent order. + let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default(); + + let regular_traits_refs_spans = bounds + .trait_bounds + .into_iter() + .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id())); + + for (base_trait_ref, span, constness) in regular_traits_refs_spans { + assert_eq!(constness, ty::BoundConstness::NotConst); + + for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) { + debug!( + "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", + obligation.predicate + ); + + let bound_predicate = obligation.predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(pred) => { + let pred = bound_predicate.rebind(pred); + associated_types.entry(span).or_default().extend( + tcx.associated_items(pred.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(|item| item.def_id), + ); + } + ty::PredicateKind::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. + let references_self = match pred.skip_binder().term.unpack() { + ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()), + ty::TermKind::Const(c) => { + c.ty().walk().any(|arg| arg == dummy_self.into()) + } + }; + + // If the projection output contains `Self`, force the user to + // elaborate it explicitly to avoid a lot of complexity. + // + // The "classically useful" case is the following: + // ``` + // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput { + // type MyOutput; + // } + // ``` + // + // Here, the user could theoretically write `dyn MyTrait<Output = X>`, + // but actually supporting that would "expand" to an infinitely-long type + // `fix $ Ï„ → dyn MyTrait<MyOutput = X, Output = <Ï„ as MyTrait>::MyOutput`. + // + // Instead, we force the user to write + // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See + // the discussion in #56288 for alternatives. + if !references_self { + // Include projections defined on supertraits. + bounds.projection_bounds.push((pred, span)); + } + } + _ => (), + } + } + } + + for (projection_bound, _) in &bounds.projection_bounds { + for def_ids in associated_types.values_mut() { + def_ids.remove(&projection_bound.projection_def_id()); + } + } + + self.complain_about_missing_associated_types( + associated_types, + potential_assoc_types, + trait_bounds, + ); + + // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as + // `dyn Trait + Send`. + // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering + // the bounds + let mut duplicates = FxHashSet::default(); + auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); + debug!("regular_traits: {:?}", regular_traits); + debug!("auto_traits: {:?}", auto_traits); + + // Erase the `dummy_self` (`trait_object_dummy_self`) used above. + let existential_trait_refs = regular_traits.iter().map(|i| { + i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| { + assert_eq!(trait_ref.self_ty(), dummy_self); + + // Verify that `dummy_self` did not leak inside default type parameters. This + // could not be done at path creation, since we need to see through trait aliases. + let mut missing_type_params = vec![]; + let mut references_self = false; + let generics = tcx.generics_of(trait_ref.def_id); + let substs: Vec<_> = trait_ref + .substs + .iter() + .enumerate() + .skip(1) // Remove `Self` for `ExistentialPredicate`. + .map(|(index, arg)| { + if arg == dummy_self.into() { + let param = &generics.params[index]; + missing_type_params.push(param.name); + return tcx.ty_error().into(); + } else if arg.walk().any(|arg| arg == dummy_self.into()) { + references_self = true; + return tcx.ty_error().into(); + } + arg + }) + .collect(); + let substs = tcx.intern_substs(&substs[..]); + + let span = i.bottom().1; + let empty_generic_args = trait_bounds.iter().any(|hir_bound| { + hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) + && hir_bound.span.contains(span) + }); + self.complain_about_missing_type_params( + missing_type_params, + trait_ref.def_id, + span, + empty_generic_args, + ); + + if references_self { + let def_id = i.bottom().0.def_id(); + let mut err = struct_span_err!( + tcx.sess, + i.bottom().1, + E0038, + "the {} `{}` cannot be made into an object", + tcx.def_kind(def_id).descr(def_id), + tcx.item_name(def_id), + ); + err.note( + rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![]) + .error_msg(), + ); + err.emit(); + } + + ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs } + }) + }); + + let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { + bound.map_bound(|mut b| { + assert_eq!(b.projection_ty.self_ty(), dummy_self); + + // Like for trait refs, verify that `dummy_self` did not leak inside default type + // parameters. + let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| { + if arg.walk().any(|arg| arg == dummy_self.into()) { + return true; + } + false + }); + if references_self { + tcx.sess + .delay_span_bug(span, "trait object projection bounds reference `Self`"); + let substs: Vec<_> = b + .projection_ty + .substs + .iter() + .map(|arg| { + if arg.walk().any(|arg| arg == dummy_self.into()) { + return tcx.ty_error().into(); + } + arg + }) + .collect(); + b.projection_ty.substs = tcx.intern_substs(&substs[..]); + } + + ty::ExistentialProjection::erase_self_ty(tcx, b) + }) + }); + + let regular_trait_predicates = existential_trait_refs + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)); + let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| { + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())) + }); + // N.b. principal, projections, auto traits + // FIXME: This is actually wrong with multiple principals in regards to symbol mangling + let mut v = regular_trait_predicates + .chain( + existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain(auto_trait_predicates) + .collect::<SmallVec<[_; 8]>>(); + v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + v.dedup(); + let existential_predicates = tcx.mk_poly_existential_predicates(v.into_iter()); + + // Use explicitly-specified region bound. + let region_bound = if !lifetime.is_elided() { + self.ast_region_to_region(lifetime, None) + } else { + self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { + if tcx.named_region(lifetime.hir_id).is_some() { + self.ast_region_to_region(lifetime, None) + } else { + self.re_infer(None, span).unwrap_or_else(|| { + let mut err = struct_span_err!( + tcx.sess, + span, + E0228, + "the lifetime bound for this object type cannot be deduced \ + from context; please supply an explicit bound" + ); + if borrowed { + // We will have already emitted an error E0106 complaining about a + // missing named lifetime in `&dyn Trait`, so we elide this one. + err.delay_as_bug(); + } else { + err.emit(); + } + tcx.lifetimes.re_static + }) + } + }) + }; + debug!("region_bound: {:?}", region_bound); + + let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation); + debug!("trait_object_type: {:?}", ty); + ty + } + + fn report_ambiguous_associated_type( + &self, + span: Span, + type_str: &str, + trait_str: &str, + name: Symbol, + ) -> ErrorGuaranteed { + let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); + if self + .tcx() + .resolutions(()) + .confused_type_with_std_module + .keys() + .any(|full_span| full_span.contains(span)) + { + err.span_suggestion( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", type_str, trait_str, name), + Applicability::HasPlaceholders, + ); + } + err.emit() + } + + // Search for a bound on a type parameter which includes the associated item + // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter + // This function will fail if there are no suitable bounds or there is + // any ambiguity. + fn find_bound_for_assoc_item( + &self, + ty_param_def_id: LocalDefId, + assoc_name: Ident, + span: Span, + ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { + let tcx = self.tcx(); + + debug!( + "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})", + ty_param_def_id, assoc_name, span, + ); + + let predicates = &self + .get_type_parameter_bounds(span, ty_param_def_id.to_def_id(), assoc_name) + .predicates; + + debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); + + let param_name = tcx.hir().ty_param_name(ty_param_def_id); + self.one_bound_for_assoc_type( + || { + traits::transitive_bounds_that_define_assoc_type( + tcx, + predicates.iter().filter_map(|(p, _)| { + Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref)) + }), + assoc_name, + ) + }, + || param_name.to_string(), + assoc_name, + span, + || None, + ) + } + + // Checks that `bounds` contains exactly one element and reports appropriate + // errors otherwise. + #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)] + fn one_bound_for_assoc_type<I>( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: impl Fn() -> String, + assoc_name: Ident, + span: Span, + is_equality: impl Fn() -> Option<String>, + ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> + where + I: Iterator<Item = ty::PolyTraitRef<'tcx>>, + { + let mut matching_candidates = all_candidates() + .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); + let mut const_candidates = all_candidates() + .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name)); + + let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) { + (Some(bound), _) => (bound, matching_candidates.next()), + (None, Some(bound)) => (bound, const_candidates.next()), + (None, None) => { + let reported = self.complain_about_assoc_type_not_found( + all_candidates, + &ty_param_name(), + assoc_name, + span, + ); + return Err(reported); + } + }; + debug!(?bound); + + if let Some(bound2) = next_cand { + debug!(?bound2); + + let is_equality = is_equality(); + let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates); + let mut err = if is_equality.is_some() { + // More specific Error Index entry. + struct_span_err!( + self.tcx().sess, + span, + E0222, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name() + ) + } else { + struct_span_err!( + self.tcx().sess, + span, + E0221, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name() + ) + }; + err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); + + let mut where_bounds = vec![]; + for bound in bounds { + let bound_id = bound.def_id(); + let bound_span = self + .tcx() + .associated_items(bound_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id) + .and_then(|item| self.tcx().hir().span_if_local(item.def_id)); + + if let Some(bound_span) = bound_span { + err.span_label( + bound_span, + format!( + "ambiguous `{}` from `{}`", + assoc_name, + bound.print_only_trait_path(), + ), + ); + if let Some(constraint) = &is_equality { + where_bounds.push(format!( + " T: {trait}::{assoc} = {constraint}", + trait=bound.print_only_trait_path(), + assoc=assoc_name, + constraint=constraint, + )); + } else { + err.span_suggestion_verbose( + span.with_hi(assoc_name.span.lo()), + "use fully qualified syntax to disambiguate", + format!( + "<{} as {}>::", + ty_param_name(), + bound.print_only_trait_path(), + ), + Applicability::MaybeIncorrect, + ); + } + } else { + err.note(&format!( + "associated type `{}` could derive from `{}`", + ty_param_name(), + bound.print_only_trait_path(), + )); + } + } + if !where_bounds.is_empty() { + err.help(&format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {},\n{}", + ty_param_name(), + where_bounds.join(",\n"), + )); + } + let reported = err.emit(); + if !where_bounds.is_empty() { + return Err(reported); + } + } + + Ok(bound) + } + + // Create a type from a path to an associated type. + // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` + // and item_segment is the path segment for `D`. We return a type and a def for + // the whole path. + // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type + // parameter or `Self`. + // NOTE: When this function starts resolving `Trait::AssocTy` successfully + // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. + #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)] + pub fn associated_path_to_ty( + &self, + hir_ref_id: hir::HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself: &hir::Ty<'_>, + assoc_segment: &hir::PathSegment<'_>, + permit_variants: bool, + ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { + let tcx = self.tcx(); + let assoc_ident = assoc_segment.ident; + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind { + path.res + } else { + Res::Err + }; + + // Check if we have an enum variant. + let mut variant_resolution = None; + if let ty::Adt(adt_def, _) = qself_ty.kind() { + if adt_def.is_enum() { + let variant_def = adt_def + .variants() + .iter() + .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); + if let Some(variant_def) = variant_def { + if permit_variants { + tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); + self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| { + err.note("enum variants can't have type parameters"); + let type_name = tcx.item_name(adt_def.did()); + let msg = format!( + "you might have meant to specity type parameters on enum \ + `{type_name}`" + ); + let Some(args) = assoc_segment.args else { return; }; + // Get the span of the generics args *including* the leading `::`. + let args_span = assoc_segment.ident.span.shrink_to_hi().to(args.span_ext); + if tcx.generics_of(adt_def.did()).count() == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + args_span, + &format!("{type_name} doesn't have generic parameters"), + "", + Applicability::MachineApplicable, + ); + return; + } + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else { + err.note(&msg); + return; + }; + let (qself_sugg_span, is_self) = if let hir::TyKind::Path( + hir::QPath::Resolved(_, ref path) + ) = qself.kind { + // If the path segment already has type params, we want to overwrite + // them. + match &path.segments[..] { + // `segment` is the previous to last element on the path, + // which would normally be the `enum` itself, while the last + // `_` `PathSegment` corresponds to the variant. + [.., hir::PathSegment { + ident, + args, + res: Res::Def(DefKind::Enum, _), + .. + }, _] => ( + // We need to include the `::` in `Type::Variant::<Args>` + // to point the span to `::<Args>`, not just `<Args>`. + ident.span.shrink_to_hi().to(args.map_or( + ident.span.shrink_to_hi(), + |a| a.span_ext)), + false, + ), + [segment] => ( + // We need to include the `::` in `Type::Variant::<Args>` + // to point the span to `::<Args>`, not just `<Args>`. + segment.ident.span.shrink_to_hi().to(segment.args.map_or( + segment.ident.span.shrink_to_hi(), + |a| a.span_ext)), + kw::SelfUpper == segment.ident.name, + ), + _ => { + err.note(&msg); + return; + } + } + } else { + err.note(&msg); + return; + }; + let suggestion = vec![ + if is_self { + // Account for people writing `Self::Variant::<Args>`, where + // `Self` is the enum, and suggest replacing `Self` with the + // appropriate type: `Type::<Args>::Variant`. + (qself.span, format!("{type_name}{snippet}")) + } else { + (qself_sugg_span, snippet) + }, + (args_span, String::new()), + ]; + err.multipart_suggestion_verbose( + &msg, + suggestion, + Applicability::MaybeIncorrect, + ); + }); + return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); + } else { + variant_resolution = Some(variant_def.def_id); + } + } + } + } + + // Find the type of the associated item, and the trait where the associated + // item is declared. + let bound = match (&qself_ty.kind(), qself_res) { + (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { + // `Self` in an impl of a trait -- we have a concrete self type and a + // trait reference. + let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { + // A cycle error occurred, most likely. + let guar = tcx.sess.delay_span_bug(span, "expected cycle error"); + return Err(guar); + }; + + self.one_bound_for_assoc_type( + || traits::supertraits(tcx, ty::Binder::dummy(trait_ref)), + || "Self".to_string(), + assoc_ident, + span, + || None, + )? + } + ( + &ty::Param(_), + Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), + ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, + _ => { + let reported = if variant_resolution.is_some() { + // Variant in type position + let msg = format!("expected type, found variant `{}`", assoc_ident); + tcx.sess.span_err(span, &msg) + } else if qself_ty.is_enum() { + let mut err = struct_span_err!( + tcx.sess, + assoc_ident.span, + E0599, + "no variant named `{}` found for enum `{}`", + assoc_ident, + qself_ty, + ); + + let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(suggested_name) = find_best_match_for_name( + &adt_def + .variants() + .iter() + .map(|variant| variant.name) + .collect::<Vec<Symbol>>(), + assoc_ident.name, + None, + ) { + err.span_suggestion( + assoc_ident.span, + "there is a variant with a similar name", + suggested_name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_label( + assoc_ident.span, + format!("variant not found in `{}`", qself_ty), + ); + } + + if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { + err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); + } + + err.emit() + } else if let Some(reported) = qself_ty.error_reported() { + reported + } else { + // Don't print `TyErr` to the user. + self.report_ambiguous_associated_type( + span, + &qself_ty.to_string(), + "Trait", + assoc_ident.name, + ) + }; + return Err(reported); + } + }; + + 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 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.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, + hir_ref_id, + span, + "ambiguous associated item", + |lint| { + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + kind.descr(def_id) + ); + lint.span_note(tcx.def_span(def_id), ¬e_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(kind, item.def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, + ); + + lint + }, + ); + } + Ok((ty, kind, item.def_id)) + } + + fn qpath_to_ty( + &self, + span: Span, + opt_self_ty: Option<Ty<'tcx>>, + item_def_id: DefId, + trait_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'_>, + constness: ty::BoundConstness, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + let trait_def_id = tcx.parent(item_def_id); + + debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); + + let Some(self_ty) = opt_self_ty else { + let path_str = tcx.def_path_str(trait_def_id); + + let def_id = self.item_def_id(); + + 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)) + }) + .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_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 { + "Self" + } else { + "Type" + }; + + self.report_ambiguous_associated_type( + span, + type_name, + &path_str, + item_segment.ident.name, + ); + return tcx.ty_error(); + }; + + debug!("qpath_to_ty: self_type={:?}", self_ty); + + let trait_ref = self.ast_path_to_mono_trait_ref( + span, + trait_def_id, + self_ty, + trait_segment, + false, + Some(constness), + ); + + let item_substs = self.create_substs_for_associated_item( + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + debug!("qpath_to_ty: trait_ref={:?}", trait_ref); + + self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) + } + + pub fn prohibit_generics<'a>( + &self, + segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, + extend: impl Fn(&mut Diagnostic), + ) -> bool { + let args = segments.clone().flat_map(|segment| segment.args().args); + + let (lt, ty, ct, inf) = + args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { + hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), + hir::GenericArg::Type(_) => (lt, true, ct, inf), + hir::GenericArg::Const(_) => (lt, ty, true, inf), + hir::GenericArg::Infer(_) => (lt, ty, ct, true), + }); + let mut emitted = false; + if lt || ty || ct || inf { + let types_and_spans: Vec<_> = segments + .clone() + .flat_map(|segment| { + if segment.args().args.is_empty() { + None + } else { + Some(( + match segment.res { + Res::PrimTy(ty) => format!("{} `{}`", segment.res.descr(), ty.name()), + Res::Def(_, def_id) + if let Some(name) = self.tcx().opt_item_name(def_id) => { + format!("{} `{name}`", segment.res.descr()) + } + Res::Err => "this type".to_string(), + _ => segment.res.descr().to_string(), + }, + segment.ident.span, + )) + } + }) + .collect(); + let this_type = match &types_and_spans[..] { + [.., _, (last, _)] => format!( + "{} and {last}", + types_and_spans[..types_and_spans.len() - 1] + .iter() + .map(|(x, _)| x.as_str()) + .intersperse(&", ") + .collect::<String>() + ), + [(only, _)] => only.to_string(), + [] => "this type".to_string(), + }; + + let arg_spans: Vec<Span> = args.map(|arg| arg.span()).collect(); + + let mut kinds = Vec::with_capacity(4); + if lt { + kinds.push("lifetime"); + } + if ty { + kinds.push("type"); + } + if ct { + kinds.push("const"); + } + if inf { + kinds.push("generic"); + } + let (kind, s) = match kinds[..] { + [.., _, last] => ( + format!( + "{} and {last}", + kinds[..kinds.len() - 1] + .iter() + .map(|&x| x) + .intersperse(", ") + .collect::<String>() + ), + "s", + ), + [only] => (format!("{only}"), ""), + [] => unreachable!(), + }; + let last_span = *arg_spans.last().unwrap(); + let span: MultiSpan = arg_spans.into(); + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0109, + "{kind} arguments are not allowed on {this_type}", + ); + err.span_label(last_span, format!("{kind} argument{s} not allowed")); + for (what, span) in types_and_spans { + err.span_label(span, format!("not allowed on {what}")); + } + extend(&mut err); + err.emit(); + emitted = true; + } + + for segment in segments { + // Only emit the first error to avoid overloading the user with error messages. + if let Some(b) = segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + return true; + } + } + emitted + } + + // FIXME(eddyb, varkor) handle type paths here too, not just value ones. + pub fn def_ids_for_value_path_segments( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option<Ty<'tcx>>, + kind: DefKind, + def_id: DefId, + ) -> Vec<PathSeg> { + // We need to extract the type parameters supplied by the user in + // the path `path`. Due to the current setup, this is a bit of a + // tricky-process; the problem is that resolve only tells us the + // end-point of the path resolution, and not the intermediate steps. + // Luckily, we can (at least for now) deduce the intermediate steps + // just from the end-point. + // + // There are basically five cases to consider: + // + // 1. Reference to a constructor of a struct: + // + // struct Foo<T>(...) + // + // In this case, the parameters are declared in the type space. + // + // 2. Reference to a constructor of an enum variant: + // + // enum E<T> { Foo(...) } + // + // In this case, the parameters are defined in the type space, + // but may be specified either on the type or the variant. + // + // 3. Reference to a fn item or a free constant: + // + // fn foo<T>() { } + // + // In this case, the path will again always have the form + // `a::b::foo::<T>` where only the final segment should have + // type parameters. However, in this case, those parameters are + // declared on a value, and hence are in the `FnSpace`. + // + // 4. Reference to a method or an associated constant: + // + // impl<A> SomeStruct<A> { + // fn foo<B>(...) + // } + // + // Here we can have a path like + // `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters + // may appear in two places. The penultimate segment, + // `SomeStruct::<A>`, contains parameters in TypeSpace, and the + // final segment, `foo::<B>` contains parameters in fn space. + // + // The first step then is to categorize the segments appropriately. + + let tcx = self.tcx(); + + assert!(!segments.is_empty()); + let last = segments.len() - 1; + + let mut path_segs = vec![]; + + match kind { + // Case 1. Reference to a struct constructor. + DefKind::Ctor(CtorOf::Struct, ..) => { + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + let generics_def_id = generics.parent.unwrap_or(def_id); + path_segs.push(PathSeg(generics_def_id, last)); + } + + // Case 2. Reference to a variant constructor. + DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { + let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap()); + let (generics_def_id, index) = if let Some(adt_def) = adt_def { + debug_assert!(adt_def.is_enum()); + (adt_def.did(), last) + } else if last >= 1 && segments[last - 1].args.is_some() { + // Everything but the penultimate segment should have no + // parameters at all. + let mut def_id = def_id; + + // `DefKind::Ctor` -> `DefKind::Variant` + if let DefKind::Ctor(..) = kind { + def_id = tcx.parent(def_id); + } + + // `DefKind::Variant` -> `DefKind::Enum` + let enum_def_id = tcx.parent(def_id); + (enum_def_id, last - 1) + } else { + // FIXME: lint here recommending `Enum::<...>::Variant` form + // instead of `Enum::Variant::<...>` form. + + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + (generics.parent.unwrap_or(def_id), last) + }; + path_segs.push(PathSeg(generics_def_id, index)); + } + + // Case 3. Reference to a top-level value. + DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static(_) => { + path_segs.push(PathSeg(def_id, last)); + } + + // Case 4. Reference to a method or associated const. + DefKind::AssocFn | DefKind::AssocConst => { + if segments.len() >= 2 { + let generics = tcx.generics_of(def_id); + path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); + } + path_segs.push(PathSeg(def_id, last)); + } + + kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), + } + + debug!("path_segs = {:?}", path_segs); + + path_segs + } + + // Check a type `Path` and convert it to a `Ty`. + pub fn res_to_ty( + &self, + opt_self_ty: Option<Ty<'tcx>>, + path: &hir::Path<'_>, + permit_variants: bool, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + debug!( + "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})", + path.res, opt_self_ty, path.segments + ); + + let span = path.span; + 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()); + 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"); + }); + let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); + self.normalize_ty(span, tcx.mk_opaque(did, substs)) + } + Res::Def( + DefKind::Enum + | DefKind::TyAlias + | DefKind::Struct + | DefKind::Union + | DefKind::ForeignTy, + did, + ) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {}); + self.ast_path_to_ty(span, did, path.segments.last().unwrap()) + } + Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { + // Convert "variant type" as if it were a real type. + // The resulting `Ty` is type of the variant's enum for now. + assert_eq!(opt_self_ty, None); + + let path_segs = + self.def_ids_for_value_path_segments(path.segments, None, kind, def_id); + let generic_segs: FxHashSet<_> = + path_segs.iter().map(|PathSeg(_, index)| index).collect(); + self.prohibit_generics( + path.segments.iter().enumerate().filter_map(|(index, seg)| { + if !generic_segs.contains(&index) { Some(seg) } else { None } + }), + |err| { + err.note("enum variants can't have type parameters"); + }, + ); + + let PathSeg(def_id, index) = path_segs.last().unwrap(); + self.ast_path_to_ty(span, *def_id, &path.segments[*index]) + } + Res::Def(DefKind::TyParam, def_id) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments.iter(), |err| { + if let Some(span) = tcx.def_ident_span(def_id) { + let name = tcx.item_name(def_id); + err.span_note(span, &format!("type parameter `{name}` defined here")); + } + }); + + let def_id = def_id.expect_local(); + let item_def_id = tcx.hir().ty_param_owner(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id)) + } + Res::SelfTyParam { .. } => { + // `Self` in trait or type alias. + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments.iter(), |err| { + if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments[..] { + err.span_suggestion_verbose( + ident.span.shrink_to_hi().to(args.span_ext), + "the `Self` type doesn't accept type parameters", + "", + Applicability::MaybeIncorrect, + ); + } + }); + tcx.types.self_param + } + Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { + // `Self` in impl (we know the concrete type). + assert_eq!(opt_self_ty, None); + // Try to evaluate any array length constants. + let ty = tcx.at(span).type_of(def_id); + let span_of_impl = tcx.span_of_impl(def_id); + self.prohibit_generics(path.segments.iter(), |err| { + let def_id = match *ty.kind() { + ty::Adt(self_def, _) => self_def.did(), + _ => return, + }; + + let type_name = tcx.item_name(def_id); + let span_of_ty = tcx.def_ident_span(def_id); + let generics = tcx.generics_of(def_id).count(); + + let msg = format!("`Self` is of type `{ty}`"); + if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { + let mut span: MultiSpan = vec![t_sp].into(); + span.push_span_label( + i_sp, + &format!("`Self` is on type `{type_name}` in this `impl`"), + ); + let mut postfix = ""; + if generics == 0 { + postfix = ", which doesn't have generic parameters"; + } + span.push_span_label( + t_sp, + &format!("`Self` corresponds to this type{postfix}"), + ); + err.span_note(span, &msg); + } else { + err.note(&msg); + } + for segment in path.segments { + if let Some(args) = segment.args && segment.ident.name == kw::SelfUpper { + if generics == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + "the `Self` type doesn't accept type parameters", + "", + Applicability::MachineApplicable, + ); + return; + } else { + err.span_suggestion_verbose( + segment.ident.span, + format!( + "the `Self` type doesn't accept type parameters, use the \ + concrete type's name `{type_name}` instead if you want to \ + specify its type parameters" + ), + type_name, + Applicability::MaybeIncorrect, + ); + } + } + } + }); + // HACK(min_const_generics): Forbid generic `Self` types + // here as we can't easily do that during nameres. + // + // We do this before normalization as we otherwise allow + // ```rust + // trait AlwaysApplicable { type Assoc; } + // impl<T: ?Sized> AlwaysApplicable for T { type Assoc = usize; } + // + // trait BindsParam<T> { + // type ArrayTy; + // } + // impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc { + // type ArrayTy = [u8; Self::MAX]; + // } + // ``` + // Note that the normalization happens in the param env of + // the anon const, which is empty. This is why the + // `AlwaysApplicable` impl needs a `T: ?Sized` bound for + // this to compile if we were to normalize here. + if forbid_generic && ty.needs_subst() { + let mut err = tcx.sess.struct_span_err( + path.span, + "generic `Self` types are currently not permitted in anonymous constants", + ); + if let Some(hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Impl(ref impl_), + .. + })) = tcx.hir().get_if_local(def_id) + { + err.span_note(impl_.self_ty.span, "not a concrete type"); + } + err.emit(); + tcx.ty_error() + } else { + self.normalize_ty(span, ty) + } + } + Res::Def(DefKind::AssocTy, def_id) => { + debug_assert!(path.segments.len() >= 2); + self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {}); + // HACK: until we support `<Type as ~const Trait>`, assume all of them are. + let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; + self.qpath_to_ty( + span, + opt_self_ty, + def_id, + &path.segments[path.segments.len() - 2], + path.segments.last().unwrap(), + constness, + ) + } + Res::PrimTy(prim_ty) => { + assert_eq!(opt_self_ty, None); + self.prohibit_generics(path.segments.iter(), |err| { + let name = prim_ty.name_str(); + for segment in path.segments { + if let Some(args) = segment.args { + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + &format!("primitive type `{name}` doesn't have generic parameters"), + "", + Applicability::MaybeIncorrect, + ); + } + } + }); + match prim_ty { + hir::PrimTy::Bool => tcx.types.bool, + hir::PrimTy::Char => tcx.types.char, + hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)), + hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)), + hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)), + hir::PrimTy::Str => tcx.types.str_, + } + } + Res::Err => { + self.set_tainted_by_errors(); + self.tcx().ty_error() + } + _ => span_bug!(span, "unexpected resolution: {:?}", path.res), + } + } + + /// Parses the programmer's textual representation of a type into our + /// internal notion of a type. + pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + self.ast_ty_to_ty_inner(ast_ty, false, false) + } + + /// Parses the programmer's textual representation of a type into our + /// internal notion of a type. This is meant to be used within a path. + pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + self.ast_ty_to_ty_inner(ast_ty, false, true) + } + + /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait + /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. + #[instrument(level = "debug", skip(self), ret)] + fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> { + let tcx = self.tcx(); + + let result_ty = match ast_ty.kind { + hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)), + hir::TyKind::Ptr(ref mt) => { + tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl }) + } + hir::TyKind::Rptr(ref region, ref mt) => { + let r = self.ast_region_to_region(region, None); + debug!(?r); + let t = self.ast_ty_to_ty_inner(mt.ty, true, false); + tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) + } + hir::TyKind::Never => tcx.types.never, + hir::TyKind::Tup(fields) => tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(t))), + hir::TyKind::BareFn(bf) => { + require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span); + + tcx.mk_fn_ptr(self.ty_of_fn( + ast_ty.hir_id, + bf.unsafety, + bf.abi, + bf.decl, + None, + Some(ast_ty), + )) + } + hir::TyKind::TraitObject(bounds, ref lifetime, repr) => { + self.maybe_lint_bare_trait(ast_ty, in_path); + let repr = match repr { + TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, + TraitObjectSyntax::DynStar => ty::DynStar, + }; + self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr) + } + hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { + debug!(?maybe_qself, ?path); + let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); + self.res_to_ty(opt_self_ty, path, false) + } + hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { + let opaque_ty = tcx.hir().item(item_id); + let def_id = item_id.owner_id.to_def_id(); + + match opaque_ty.kind { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + self.impl_trait_ty_to_ty(def_id, lifetimes, origin, in_trait) + } + ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), + } + } + hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { + debug!(?qself, ?segment); + let ty = self.ast_ty_to_ty_inner(qself, false, true); + self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false) + .map(|(ty, _, _)| ty) + .unwrap_or_else(|_| tcx.ty_error()) + } + hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => { + let def_id = tcx.require_lang_item(lang_item, Some(span)); + let (substs, _) = self.create_substs_for_ast_path( + span, + def_id, + &[], + &hir::PathSegment::invalid(), + &GenericArgs::none(), + true, + None, + None, + ); + EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id))) + .subst(tcx, substs) + } + hir::TyKind::Array(ref ty, ref length) => { + 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) + } + }; + + let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length)); + 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 = tcx.fold_regions(ty_erased, |r, _| { + if r.is_erased() { tcx.lifetimes.re_static } else { r } + }); + let span = ast_ty.span; + tcx.sess.emit_err(TypeofReservedKeywordUsed { + span, + ty, + opt_sugg: Some((span, Applicability::MachineApplicable)) + .filter(|_| ty.is_suggestable(tcx, false)), + }); + + ty + } + hir::TyKind::Infer => { + // Infer also appears as the type of arguments or return + // values in an ExprKind::Closure, or as + // the type of local variables. Both of these cases are + // handled specially and will not descend into this routine. + self.ty_infer(None, ast_ty.span) + } + hir::TyKind::Err => tcx.ty_error(), + }; + + self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); + result_ty + } + + #[instrument(level = "debug", skip(self), ret)] + fn impl_trait_ty_to_ty( + &self, + def_id: DefId, + lifetimes: &[hir::GenericArg<'_>], + origin: OpaqueTyOrigin, + in_trait: bool, + ) -> Ty<'tcx> { + debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); + let tcx = self.tcx(); + + let generics = tcx.generics_of(def_id); + + debug!("impl_trait_ty_to_ty: generics={:?}", generics); + 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!() + } + } 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), + } + } + }); + debug!("impl_trait_ty_to_ty: substs={:?}", substs); + + if in_trait { tcx.mk_projection(def_id, substs) } else { tcx.mk_opaque(def_id, substs) } + } + + pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { + match ty.kind { + hir::TyKind::Infer if expected_ty.is_some() => { + self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span); + expected_ty.unwrap() + } + _ => self.ast_ty_to_ty(ty), + } + } + + #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)] + pub fn ty_of_fn( + &self, + hir_id: hir::HirId, + unsafety: hir::Unsafety, + abi: abi::Abi, + decl: &hir::FnDecl<'_>, + generics: Option<&hir::Generics<'_>>, + hir_ty: Option<&hir::Ty<'_>>, + ) -> ty::PolyFnSig<'tcx> { + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(hir_id); + debug!(?bound_vars); + + // We proactively collect all the inferred type params to emit a single error per fn def. + let mut visitor = HirPlaceholderCollector::default(); + let mut infer_replacements = vec![]; + + if let Some(generics) = generics { + walk_generics(&mut visitor, generics); + } + + let input_tys: Vec<_> = decl + .inputs + .iter() + .enumerate() + .map(|(i, a)| { + if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() { + if let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return suggested_ty; + } + } + + // Only visit the type looking for `_` if we didn't fix the type above + visitor.visit_ty(a); + self.ty_of_arg(a, None) + }) + .collect(); + + let output_ty = match decl.output { + hir::FnRetTy::Return(output) => { + if let hir::TyKind::Infer = output.kind + && !self.allow_ty_infer() + && let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) + { + infer_replacements.push((output.span, suggested_ty.to_string())); + suggested_ty + } else { + visitor.visit_ty(output); + self.ast_ty_to_ty(output) + } + } + hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(), + }; + + debug!(?output_ty); + + let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi); + let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + + if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { + // We always collect the spans for placeholder types when evaluating `fn`s, but we + // only want to emit an error complaining about them if infer types (`_`) are not + // allowed. `allow_ty_infer` gates this behavior. We check for the presence of + // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. + + let mut diag = crate::collect::placeholder_type_error_diag( + tcx, + generics, + visitor.0, + infer_replacements.iter().map(|(s, _)| *s).collect(), + true, + hir_ty, + "function", + ); + + if !infer_replacements.is_empty() { + diag.multipart_suggestion( + &format!( + "try replacing `_` with the type{} in the corresponding trait method signature", + rustc_errors::pluralize!(infer_replacements.len()), + ), + infer_replacements, + Applicability::MachineApplicable, + ); + } + + diag.emit(); + } + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not well-formed. + // + // Example: + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + let inputs = bare_fn_ty.inputs(); + let late_bound_in_args = + tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned())); + let output = bare_fn_ty.output(); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); + + self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { + struct_span_err!( + tcx.sess, + decl.output.span(), + E0581, + "return type references {}, which is not constrained by the fn input types", + br_name + ) + }); + + bare_fn_ty + } + + /// Given a fn_hir_id for a impl function, suggest the type that is found on the + /// corresponding function in the trait that the impl implements, if it exists. + /// If arg_idx is Some, then it corresponds to an input type index, otherwise it + /// corresponds to the return type. + fn suggest_trait_fn_ty_for_impl_fn_infer( + &self, + fn_hir_id: hir::HirId, + arg_idx: Option<usize>, + ) -> Option<Ty<'tcx>> { + let tcx = self.tcx(); + let hir = tcx.hir(); + + let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = + hir.get(fn_hir_id) else { return None }; + let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) = + hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") }; + + let trait_ref = self.instantiate_mono_trait_ref( + i.of_trait.as_ref()?, + self.ast_ty_to_ty(i.self_ty), + ty::BoundConstness::NotConst, + ); + + let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + tcx, + *ident, + ty::AssocKind::Fn, + trait_ref.def_id, + )?; + + let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst( + tcx, + trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), + ); + + let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() }; + + Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty)) + } + + fn validate_late_bound_regions( + &self, + constrained_regions: FxHashSet<ty::BoundRegionKind>, + referenced_regions: FxHashSet<ty::BoundRegionKind>, + generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) { + for br in referenced_regions.difference(&constrained_regions) { + let br_name = match *br { + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) | ty::BrEnv => { + "an anonymous lifetime".to_string() + } + ty::BrNamed(_, name) => format!("lifetime `{}`", name), + }; + + let mut err = generate_err(&br_name); + + 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 + // input. See #47511 and #62200 for examples. In this case, + // though we can easily give a hint that ought to be + // relevant. + err.note( + "lifetimes appearing in an associated or opaque type are not considered constrained", + ); + err.note("consider introducing a named lifetime parameter"); + } + + err.emit(); + } + } + + /// Given the bounds on an object, determines what single region bound (if any) we can + /// use to summarize this type. The basic idea is that we will use the bound the user + /// provided, if they provided one, and otherwise search the supertypes of trait bounds + /// for region bounds. It may be that we can derive no bound at all, in which case + /// we return `None`. + fn compute_object_lifetime_bound( + &self, + span: Span, + existential_predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + ) -> Option<ty::Region<'tcx>> // if None, use the default + { + let tcx = self.tcx(); + + debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = object_region_bounds(tcx, existential_predicates); + + // If there are no derived region bounds, then report back that we + // can find no region bound. The caller will use the default. + if derived_region_bounds.is_empty() { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|r| r.is_static()) { + return Some(tcx.lifetimes.re_static); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = derived_region_bounds[0]; + if derived_region_bounds[1..].iter().any(|r1| r != *r1) { + tcx.sess.emit_err(AmbiguousLifetimeBound { span }); + } + Some(r) + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_lint_blanket_trait_impl(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) { + let tcx = self.tcx(); + let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + self_ty: impl_self_ty, of_trait: Some(of_trait_ref), generics, .. + }), + .. + }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id + { + if !of_trait_ref.trait_def_id().map_or(false, |def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return; }; + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; }; + // check if the trait has generics, to make a correct suggestion + let param_name = generics.params.next_type_param_name(None); + + let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {}: {}", param_name, impl_trait_name)) + } else { + (generics.span, format!("<{}: {}>", param_name, impl_trait_name)) + }; + diag.multipart_suggestion( + format!("alternatively use a blanket \ + implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`"), + vec![ + (self_ty.span, param_name), + add_generic_sugg, + ], + Applicability::MaybeIncorrect, + ); + } + } + + fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) { + let tcx = self.tcx(); + if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + self_ty.kind + { + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(self_ty.span) + .ok() + .map_or(false, |s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = Vec::from_iter([( + self_ty.span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]); + + if is_global || needs_bracket { + sugg.push(( + self_ty.span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if self_ty.span.edition() >= Edition::Edition2021 { + let msg = "trait objects must include the `dyn` keyword"; + let label = "add `dyn` keyword before this trait"; + let mut diag = + rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg); + diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable); + // check if the impl trait that we are considering is a impl of a local trait + self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag); + diag.emit(); + } else { + let msg = "trait objects without an explicit `dyn` are deprecated"; + tcx.struct_span_lint_hir( + BARE_TRAIT_OBJECTS, + self_ty.hir_id, + self_ty.span, + msg, + |lint| { + lint.multipart_suggestion_verbose( + "use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + self.maybe_lint_blanket_trait_impl(&self_ty, lint); + lint + }, + ); + } + } + } +} diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 6a28bb16a..6a28bb16a 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs new file mode 100644 index 000000000..b70ac0205 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -0,0 +1,1443 @@ +use crate::check::intrinsicck::InlineAsmCtxt; + +use super::compare_method::check_type_bounds; +use super::compare_method::{compare_impl_method, compare_ty_impl}; +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_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ItemKind, Node, PathSegment}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::traits::Obligation; +use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; +use rustc_middle::hir::nested_filter; +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_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::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::{self, ObligationCtxt}; + +use std::ops::ControlFlow; + +pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { + match tcx.sess.target.is_abi_supported(abi) { + Some(true) => (), + Some(false) => { + struct_span_err!( + tcx.sess, + span, + E0570, + "`{abi}` is not a supported ABI for the current target", + ) + .emit(); + } + None => { + tcx.struct_span_lint_hir( + UNSUPPORTED_CALLING_CONVENTIONS, + hir_id, + span, + "use of calling convention not supported on this target", + |lint| lint, + ); + } + } + + // This ABI is only allowed on function pointers + if abi == Abi::CCmseNonSecureCall { + struct_span_err!( + tcx.sess, + span, + E0781, + "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers" + ) + .emit(); + } +} + +fn check_struct(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 + + if def.repr().simd() { + check_simd(tcx, span, def_id); + } + + check_transparent(tcx, span, def); + check_packed(tcx, span, def); +} + +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_union_fields(tcx, span, def_id); + check_packed(tcx, span, def); +} + +/// Check that the fields of the `union` do not need dropping. +fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { + let item_type = tcx.type_of(item_def_id); + if let ty::Adt(def, substs) = item_type.kind() { + assert!(def.is_union()); + + fn allowed_union_field<'tcx>( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + ) -> bool { + // We don't just accept all !needs_drop fields, due to semver concerns. + match ty.kind() { + ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) + ty::Tuple(tys) => { + // allow tuples of allowed types + tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span)) + } + ty::Array(elem, _len) => { + // Like `Copy`, we do *not* special-case length 0. + allowed_union_field(*elem, tcx, param_env, span) + } + _ => { + // Fallback case: allow `ManuallyDrop` and things that are `Copy`. + ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) + || ty.is_copy_modulo_regions(tcx, param_env) + } + } + } + + let param_env = tcx.param_env(item_def_id); + for field in &def.non_enum_variant().fields { + let field_ty = field.ty(tcx, substs); + + if !allowed_union_field(field_ty, tcx, param_env, span) { + let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { + // We are currently checking the type this field came from, so it must be local. + Some(Node::Field(field)) => (field.span, field.ty.span), + _ => unreachable!("mir field has to correspond to hir field"), + }; + struct_span_err!( + tcx.sess, + field_span, + E0740, + "unions cannot contain fields that may need dropping" + ) + .note( + "a type is guaranteed not to need dropping \ + when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type", + ) + .multipart_suggestion_verbose( + "when the type does not implement `Copy`, \ + wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped", + vec![ + (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()), + (ty_span.shrink_to_hi(), ">".into()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + return false; + } else if field_ty.needs_drop(tcx, param_env) { + // This should never happen. But we can get here e.g. in case of name resolution errors. + tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields"); + } + } + } else { + span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); + } + true +} + +/// Check that a `static` is inhabited. +fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { + // Make sure statics are inhabited. + // Other parts of the compiler assume that there are no uninhabited places. In principle it + // would be enough to check this for `extern` statics, as statics with an initializer will + // have UB during initialization if they are uninhabited, but there also seems to be no good + // reason to allow any statics to be uninhabited. + let ty = tcx.type_of(def_id); + let span = tcx.def_span(def_id); + let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) { + Ok(l) => l, + // Foreign statics that overflow their allowed size should emit an error + Err(LayoutError::SizeOverflow(_)) + if { + let node = tcx.hir().get_by_def_id(def_id); + matches!( + node, + hir::Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Static(..), + .. + }) + ) + } => + { + tcx.sess + .struct_span_err(span, "extern static is too large for the current architecture") + .emit(); + return; + } + // Generic statics are rejected, but we still reach this case. + Err(e) => { + tcx.sess.delay_span_bug(span, &e.to_string()); + return; + } + }; + if layout.abi.is_uninhabited() { + tcx.struct_span_lint_hir( + UNINHABITED_STATIC, + tcx.hir().local_def_id_to_hir_id(def_id), + span, + "static of uninhabited type", + |lint| { + lint + .note("uninhabited statics cannot be initialized, and any access would be an immediate error") + }, + ); + } +} + +/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` +/// projections that would result in "inheriting lifetimes". +fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { + let item = tcx.hir().item(id); + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { + tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item"); + return; + }; + + // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting + // `async-std` (and `pub async fn` in general). + // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // See https://github.com/rust-lang/rust/issues/75100 + if tcx.sess.opts.actually_rustdoc { + return; + } + + 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.type_of(item.owner_id.def_id).references_error() { + return; + } + if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() { + return; + } + 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))] +pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + span: Span, +) { + 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, + selftys: Vec<(Span, Option<String>)>, + } + + impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'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); + if t == self.opaque_identity_ty { + ControlFlow::CONTINUE + } else { + t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics)) + .map_break(|FoundParentLifetime| t) + } + } + } + + impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { + [PathSegment { res: Res::SelfTyParam { .. }, .. }] => { + let impl_ty_name = None; + self.selftys.push((path.span, impl_ty_name)); + } + [PathSegment { res: Res::SelfTyAlias { alias_to: def_id, .. }, .. }] => { + let impl_ty_name = Some(self.tcx.def_path_str(*def_id)); + self.selftys.push((path.span, impl_ty_name)); + } + _ => {} + }, + _ => {} + } + hir::intravisit::walk_ty(self, arg); + } + } + + if let ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), + .. + }) = item.kind + { + 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), + tcx, + selftys: vec![], + }; + let prohibit_opaque = 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); + let is_async = match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..)) + } + _ => unreachable!(), + }; + + let mut err = struct_span_err!( + tcx.sess, + 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" }, + ); + + for (span, name) in visitor.selftys { + err.span_suggestion( + span, + "consider spelling out the type instead", + name.unwrap_or_else(|| format!("{:?}", ty)), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + } + } +} + +/// Checks that an opaque type does not contain cycles. +pub(super) fn check_opaque_for_cycles<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) -> Result<(), ErrorGuaranteed> { + if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() { + let reported = match origin { + hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span), + _ => opaque_type_cycle_error(tcx, def_id, span), + }; + Err(reported) + } else { + Ok(()) + } +} + +/// Check that the concrete type behind `impl Trait` actually implements `Trait`. +/// +/// This is mostly checked at the places that specify the opaque type, but we +/// check those cases in the `param_env` of that function, which may have +/// bounds not on this opaque type: +/// +/// ```ignore (illustrative) +/// type X<T> = impl Clone; +/// fn f<T: Clone>(t: T) -> X<T> { +/// t +/// } +/// ``` +/// +/// Without this check the above code is incorrectly accepted: we would ICE if +/// some tried, for example, to clone an `Option<X<&mut ()>>`. +#[instrument(level = "debug", skip(tcx))] +fn check_opaque_meets_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let defining_use_anchor = match *origin { + hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, + hir::OpaqueTyOrigin::TyAlias => def_id, + }; + let param_env = tcx.param_env(defining_use_anchor); + + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)) + .build(); + let ocx = ObligationCtxt::new(&infcx); + let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + + // `ReErased` regions appear in the "parent_substs" of closures/generators. + // We're ignoring them here and replacing them with fresh region variables. + // See tests in ui/type-alias-impl-trait/closure_{parent_substs,wf_outlives}.rs. + // + // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it + // here rather than using ReErased. + let hidden_ty = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); + let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() { + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)), + _ => re, + }); + + 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), + Err(ty_err) => { + tcx.sess.delay_span_bug( + span, + &format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"), + ); + } + } + + // 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)); + + // 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); + } + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + defining_use_anchor, + &outlives_environment, + ); + } + } + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); +} + +fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { + debug!( + "check_item_type(it.def_id={:?}, it.name={})", + id.owner_id, + tcx.def_path_str(id.owner_id.to_def_id()) + ); + let _indenter = indenter(); + match tcx.def_kind(id.owner_id) { + DefKind::Static(..) => { + 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); + } + 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); + } + DefKind::Fn => {} // entirely within check_item_body + DefKind::Impl => { + let it = tcx.hir().item(id); + let hir::ItemKind::Impl(ref impl_) = it.kind else { + return; + }; + debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id); + if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) { + check_impl_items_against_trait( + tcx, + it.span, + it.owner_id.def_id, + impl_trait_ref, + &impl_.items, + ); + check_on_unimplemented(tcx, it); + } + } + DefKind::Trait => { + let it = tcx.hir().item(id); + let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else { + return; + }; + check_on_unimplemented(tcx, it); + + for item in items.iter() { + let item = tcx.hir().trait_item(item.id); + match item.kind { + hir::TraitItemKind::Fn(ref sig, _) => { + let abi = sig.header.abi; + fn_maybe_err(tcx, item.ident.span, abi); + } + hir::TraitItemKind::Type(.., Some(default)) => { + let assoc_item = tcx.associated_item(item.owner_id); + let trait_substs = + InternalSubsts::identity_for_item(tcx, it.owner_id.to_def_id()); + let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( + tcx, + assoc_item, + assoc_item, + default.span, + ty::TraitRef { def_id: it.owner_id.to_def_id(), substs: trait_substs }, + ); + } + _ => {} + } + } + } + DefKind::Struct => { + check_struct(tcx, id.owner_id.def_id); + } + DefKind::Union => { + check_union(tcx, id.owner_id.def_id); + } + DefKind::OpaqueTy => { + check_opaque(tcx, id); + } + DefKind::ImplTraitPlaceholder => { + let parent = tcx.impl_trait_in_trait_parent(id.owner_id.to_def_id()); + // Only check the validity of this opaque type if the function has a default body + if let hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), + .. + }) = tcx.hir().get_by_def_id(parent.expect_local()) + { + check_opaque(tcx, id); + } + } + DefKind::TyAlias => { + let pty_ty = tcx.type_of(id.owner_id); + let generics = tcx.generics_of(id.owner_id); + check_type_params_are_used(tcx, &generics, pty_ty); + } + DefKind::ForeignMod => { + let it = tcx.hir().item(id); + let hir::ItemKind::ForeignMod { abi, items } = it.kind else { + return; + }; + check_abi(tcx, it.hir_id(), it.span, abi); + + if abi == Abi::RustIntrinsic { + for item in items { + let item = tcx.hir().foreign_item(item.id); + intrinsic::check_intrinsic_type(tcx, item); + } + } else if abi == Abi::PlatformIntrinsic { + for item in items { + let item = tcx.hir().foreign_item(item.id); + intrinsic::check_platform_intrinsic_type(tcx, item); + } + } else { + for item in items { + let def_id = item.id.owner_id.def_id; + let generics = tcx.generics_of(def_id); + let own_counts = generics.own_counts(); + if generics.params.len() - own_counts.lifetimes != 0 { + let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { + (_, 0) => ("type", "types", Some("u32")), + // We don't specify an example value, because we can't generate + // a valid value for any type. + (0, _) => ("const", "consts", None), + _ => ("type or const", "types or consts", None), + }; + struct_span_err!( + tcx.sess, + item.span, + E0044, + "foreign items may not have {kinds} parameters", + ) + .span_label(item.span, &format!("can't have {kinds} parameters")) + .help( + // FIXME: once we start storing spans for type arguments, turn this + // into a suggestion. + &format!( + "replace the {} parameters with concrete {}{}", + kinds, + kinds_pl, + egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), + ), + ) + .emit(); + } + + let item = tcx.hir().foreign_item(item.id); + match item.kind { + hir::ForeignItemKind::Fn(ref fn_decl, _, _) => { + require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span); + } + hir::ForeignItemKind::Static(..) => { + check_static_inhabited(tcx, def_id); + } + _ => {} + } + } + } + } + DefKind::GlobalAsm => { + let it = tcx.hir().item(id); + let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) }; + InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id()); + } + _ => {} + } +} + +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()); +} + +pub(super) fn check_specialization_validity<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def: &ty::TraitDef, + trait_item: &ty::AssocItem, + impl_id: DefId, + impl_item: &hir::ImplItemRef, +) { + let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) else { return }; + let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| { + if parent.is_from_trait() { + None + } else { + Some((parent, parent.item(tcx, trait_item.def_id))) + } + }); + + let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { + match parent_item { + // Parent impl exists, and contains the parent item we're trying to specialize, but + // doesn't mark it `default`. + Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { + Some(Err(parent_impl.def_id())) + } + + // Parent impl contains item and makes it specializable. + Some(_) => Some(Ok(())), + + // Parent impl doesn't mention the item. This means it's inherited from the + // grandparent. In that case, if parent is a `default impl`, inherited items use the + // "defaultness" from the grandparent, else they are final. + None => { + if tcx.impl_defaultness(parent_impl.def_id()).is_default() { + None + } else { + Some(Err(parent_impl.def_id())) + } + } + } + }); + + // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the + // item. This is allowed, the item isn't actually getting specialized here. + let result = opt_result.unwrap_or(Ok(())); + + if let Err(parent_impl) = result { + report_forbidden_specialization(tcx, impl_item, parent_impl); + } +} + +fn check_impl_items_against_trait<'tcx>( + tcx: TyCtxt<'tcx>, + full_impl_span: Span, + impl_id: LocalDefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item_refs: &[hir::ImplItemRef], +) { + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { + return; + } + + // Negative impls are not expected to have any items + match tcx.impl_polarity(impl_id) { + ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Negative => { + if let [first_item_ref, ..] = impl_item_refs { + let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; + struct_span_err!( + tcx.sess, + first_item_span, + E0749, + "negative impls cannot have any items" + ) + .emit(); + } + return; + } + } + + let trait_def = tcx.trait_def(impl_trait_ref.def_id); + + for impl_item in impl_item_refs { + let ty_impl_item = tcx.associated_item(impl_item.id.owner_id); + let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { + tcx.associated_item(trait_item_id) + } else { + // Checked in `associated_item`. + tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait"); + continue; + }; + let impl_item_full = tcx.hir().impl_item(impl_item.id); + match impl_item_full.kind { + hir::ImplItemKind::Const(..) => { + let _ = tcx.compare_assoc_const_impl_item_with_trait_item(( + impl_item.id.owner_id.def_id, + ty_impl_item.trait_item_def_id.unwrap(), + )); + } + hir::ImplItemKind::Fn(..) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_impl_method( + tcx, + &ty_impl_item, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } + hir::ImplItemKind::Type(impl_ty) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_ty_impl( + tcx, + &ty_impl_item, + impl_ty.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } + } + + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); + } + + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { + // Check for missing items from trait + let mut missing_items = Vec::new(); + + let mut must_implement_one_of: Option<&[Ident]> = + trait_def.must_implement_one_of.as_deref(); + + for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { + let is_implemented = ancestors + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value()); + + if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + missing_items.push(tcx.associated_item(trait_item_id)); + } + + // true if this item is specifically implemented in this impl + let is_implemented_here = ancestors + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + + if !is_implemented_here { + match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { + EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable( + tcx, + full_impl_span, + trait_item_id, + feature, + reason, + issue, + ), + + // Unmarked default bodies are considered stable (at least for now). + EvalResult::Allow | EvalResult::Unmarked => {} + } + } + + if let Some(required_items) = &must_implement_one_of { + if is_implemented_here { + let trait_item = tcx.associated_item(trait_item_id); + if required_items.contains(&trait_item.ident(tcx)) { + must_implement_one_of = None; + } + } + } + } + + if !missing_items.is_empty() { + missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span); + } + + if let Some(missing_items) = must_implement_one_of { + let attr_span = tcx + .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of) + .map(|attr| attr.span); + + missing_items_must_implement_one_of_err( + tcx, + tcx.def_span(impl_id), + missing_items, + attr_span, + ); + } + } +} + +pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { + let t = tcx.type_of(def_id); + if let ty::Adt(def, substs) = t.kind() + && def.is_struct() + { + let fields = &def.non_enum_variant().fields; + if fields.is_empty() { + struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); + return; + } + let e = fields[0].ty(tcx, substs); + if !fields.iter().all(|f| f.ty(tcx, substs) == e) { + struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") + .span_label(sp, "SIMD elements must have the same type") + .emit(); + return; + } + + let len = if let ty::Array(_ty, c) = e.kind() { + c.try_eval_usize(tcx, tcx.param_env(def.did())) + } else { + Some(fields.len() as u64) + }; + if let Some(len) = len { + if len == 0 { + struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); + return; + } else if len > MAX_SIMD_LANES { + struct_span_err!( + tcx.sess, + sp, + E0075, + "SIMD vector cannot have more than {MAX_SIMD_LANES} elements", + ) + .emit(); + return; + } + } + + // Check that we use types valid for use in the lanes of a SIMD "vector register" + // These are scalar types which directly match a "machine" type + // Yes: Integers, floats, "thin" pointers + // No: char, "fat" pointers, compound types + match e.kind() { + ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok + ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors + ty::Array(t, _clen) + if matches!( + t.kind(), + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) + ) => + { /* struct([f32; 4]) is ok */ } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0077, + "SIMD vector element type should be a \ + primitive scalar (integer/float/pointer) type" + ) + .emit(); + return; + } + } + } +} + +pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { + let repr = def.repr(); + if repr.packed() { + for attr in tcx.get_attrs(def.did(), sym::repr) { + for r in attr::parse_repr_attr(&tcx.sess, attr) { + if let attr::ReprPacked(pack) = r + && let Some(repr_pack) = repr.pack + && pack as u64 != repr_pack.bytes() + { + struct_span_err!( + tcx.sess, + sp, + E0634, + "type has conflicting packed representation hints" + ) + .emit(); + } + } + } + if repr.align.is_some() { + struct_span_err!( + tcx.sess, + sp, + E0587, + "type has conflicting packed and align representation hints" + ) + .emit(); + } else { + if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) { + let mut err = struct_span_err!( + tcx.sess, + sp, + E0588, + "packed type cannot transitively contain a `#[repr(align)]` type" + ); + + err.span_note( + tcx.def_span(def_spans[0].0), + &format!( + "`{}` has a `#[repr(align)]` attribute", + tcx.item_name(def_spans[0].0) + ), + ); + + if def_spans.len() > 2 { + let mut first = true; + for (adt_def, span) in def_spans.iter().skip(1).rev() { + let ident = tcx.item_name(*adt_def); + err.span_note( + *span, + &if first { + format!( + "`{}` contains a field of type `{}`", + tcx.type_of(def.did()), + ident + ) + } else { + format!("...which contains a field of type `{ident}`") + }, + ); + first = false; + } + } + + err.emit(); + } + } + } +} + +pub(super) fn check_packed_inner( + tcx: TyCtxt<'_>, + def_id: DefId, + stack: &mut Vec<DefId>, +) -> Option<Vec<(DefId, Span)>> { + if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() { + if def.is_struct() || def.is_union() { + if def.repr().align.is_some() { + return Some(vec![(def.did(), DUMMY_SP)]); + } + + stack.push(def_id); + for field in &def.non_enum_variant().fields { + if let ty::Adt(def, _) = field.ty(tcx, substs).kind() + && !stack.contains(&def.did()) + && let Some(mut defs) = check_packed_inner(tcx, def.did(), stack) + { + defs.push((def.did(), field.ident(tcx).span)); + return Some(defs); + } + } + stack.pop(); + } + } + + None +} + +pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) { + if !adt.repr().transparent() { + return; + } + + if adt.is_union() && !tcx.features().transparent_unions { + feature_err( + &tcx.sess.parse_sess, + sym::transparent_unions, + sp, + "transparent unions are unstable", + ) + .emit(); + } + + if adt.variants().len() != 1 { + bad_variant_count(tcx, adt, sp, adt.did()); + if adt.variants().is_empty() { + // Don't bother checking the fields. No variants (and thus no fields) exist. + return; + } + } + + // For each field, figure out if it's known to be a ZST and align(1), with "known" + // respecting #[non_exhaustive] attributes. + let field_infos = adt.all_fields().map(|field| { + let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); + let param_env = tcx.param_env(field.did); + let layout = tcx.layout_of(param_env.and(ty)); + // We are currently checking the type this field came from, so it must be local + let span = tcx.hir().span_if_local(field.did).unwrap(); + let zst = layout.map_or(false, |layout| layout.is_zst()); + let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1); + if !zst { + return (span, zst, align1, None); + } + + fn check_non_exhaustive<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> { + match t.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), + ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), + ty::Adt(def, subst) => { + if !def.did().is_local() { + let non_exhaustive = def.is_variant_list_non_exhaustive() + || def + .variants() + .iter() + .any(ty::VariantDef::is_field_list_non_exhaustive); + let has_priv = def.all_fields().any(|f| !f.vis.is_public()); + if non_exhaustive || has_priv { + return ControlFlow::Break(( + def.descr(), + def.did(), + subst, + non_exhaustive, + )); + } + } + def.all_fields() + .map(|field| field.ty(tcx, subst)) + .try_for_each(|t| check_non_exhaustive(tcx, t)) + } + _ => ControlFlow::Continue(()), + } + } + + (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) + }); + + let non_zst_fields = field_infos + .clone() + .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); + } + let incompatible_zst_fields = + field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); + let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; + for (span, zst, align1, non_exhaustive) in field_infos { + if zst && !align1 { + struct_span_err!( + tcx.sess, + span, + E0691, + "zero-sized field in transparent {} has alignment larger than 1", + adt.descr(), + ) + .span_label(span, "has alignment larger than 1") + .emit(); + } + if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive { + tcx.struct_span_lint_hir( + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), + span, + "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types", + |lint| { + let note = if non_exhaustive { + "is marked with `#[non_exhaustive]`" + } else { + "contains private fields" + }; + let field_ty = tcx.def_path_str_with_substs(def_id, substs); + lint + .note(format!("this {descr} contains `{field_ty}`, which {note}, \ + and makes it not a breaking change to become non-zero-sized in the future.")) + }, + ) + } + } +} + +#[allow(trivial_numeric_casts)] +fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'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 let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() { + struct_span_err!( + tcx.sess, + attr.span, + E0084, + "unsupported representation for zero-variant enum" + ) + .span_label(sp, "zero-variant enum") + .emit(); + } + } + + let repr_type_ty = def.repr().discr_type().to_ty(tcx); + if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { + if !tcx.features().repr128 { + feature_err( + &tcx.sess.parse_sess, + sym::repr128, + sp, + "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)); + } + } + + if tcx.adt_def(def_id).repr().int.is_none() { + let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); + + 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)); + + if disr_non_unit || (disr_units && has_non_units) { + let mut err = + struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); + err.emit(); + } + } + + detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); + + check_transparent(tcx, sp, 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, +) { + // 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) => { + // 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 + && 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 + } else { + (tcx.hir().span(expr.hir_id), format!("`{dis}`")) + } + } + None => { + // 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()) + { + let ve_ident = var.ident; + let n = n + 1; + let sp = if n > 1 { "variants" } else { "variant" }; + + err.span_label( + *span, + format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"), + ); + } + + (vs[idx].span, format!("`{dis}`")) + } + }; + + err.span_label(span, format!("{display_discr} assigned here")); + }; + + // 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 + // it with the last element and decrementing the `vec.len` (which is why we have to evaluate + // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional + // 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 mut error: Option<DiagnosticBuilder<'_, _>> = None; + + let mut o = i + 1; + while o < discrs.len() { + let hir_var_o_idx = discrs[o].0.index(); + + 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, + E0081, + "discriminant value `{}` assigned more than once", + discrs[i].1, + ); + + report(discrs[i].1, hir_var_i_idx, &mut ret); + + ret + }); + + report(discrs[o].1, hir_var_o_idx, err); + + // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty + discrs[o] = *discrs.last().unwrap(); + discrs.pop(); + } else { + o += 1; + } + } + + if let Some(mut e) = error { + e.emit(); + } + + i += 1; + } +} + +pub(super) fn check_type_params_are_used<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &ty::Generics, + ty: Ty<'tcx>, +) { + debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); + + assert_eq!(generics.parent, None); + + if generics.own_counts().types == 0 { + return; + } + + let mut params_used = BitSet::new_empty(generics.params.len()); + + if ty.references_error() { + // If there is already another error, do not emit + // an error for not using a type parameter. + assert!(tcx.sess.has_errors().is_some()); + return; + } + + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() + && let ty::Param(param) = leaf_ty.kind() + { + debug!("found use of ty param {:?}", param); + params_used.insert(param.index); + } + } + + for param in &generics.params { + if !params_used.contains(param.index) + && let ty::GenericParamDefKind::Type { .. } = param.kind + { + let span = tcx.def_span(param.def_id); + struct_span_err!( + tcx.sess, + span, + E0091, + "type parameter `{}` is unused", + param.name, + ) + .span_label(span, "unused type parameter") + .emit(); + } + } +} + +pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + let module = tcx.hir_module_items(module_def_id); + for id in module.items() { + check_item_type(tcx, id); + } +} + +fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed { + struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .note( + "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", + ) + .emit() +} + +/// Emit an error for recursive opaque types. +/// +/// If this is a return `impl Trait`, find the item's return expressions and point at them. For +/// direct recursion this is enough, but for indirect recursion also point at the last intermediary +/// `impl Trait`. +/// +/// If all the return expressions evaluate to `!`, then we explain that the error will go away +/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. +fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + + let mut label = false; + if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let typeck_results = tcx.typeck(def_id); + if visitor + .returns + .iter() + .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) + .all(|ty| matches!(ty.kind(), ty::Never)) + { + let spans = visitor + .returns + .iter() + .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) + .map(|expr| expr.span) + .collect::<Vec<Span>>(); + let span_len = spans.len(); + if span_len == 1 { + err.span_label(spans[0], "this returned value is of `!` type"); + } else { + let mut multispan: MultiSpan = spans.clone().into(); + for span in spans { + multispan.push_span_label(span, "this returned value is of `!` type"); + } + err.span_note(multispan, "these returned values have a concrete \"never\" type"); + } + err.help("this error will resolve once the item's body returns a concrete type"); + } else { + let mut seen = FxHashSet::default(); + seen.insert(span); + err.span_label(span, "recursive opaque type"); + label = true; + for (sp, ty) in visitor + .returns + .iter() + .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) + .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) + { + struct OpaqueTypeCollector(Vec<DefId>); + impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + match *t.kind() { + ty::Opaque(def, _) => { + self.0.push(def); + ControlFlow::CONTINUE + } + _ => t.super_visit_with(self), + } + } + } + let mut visitor = OpaqueTypeCollector(vec![]); + ty.visit_with(&mut visitor); + for def_id in visitor.0 { + let ty_span = tcx.def_span(def_id); + if !seen.contains(&ty_span) { + err.span_label(ty_span, &format!("returning this opaque type `{ty}`")); + seen.insert(ty_span); + } + err.span_label(sp, &format!("returning here with type `{ty}`")); + } + } + } + } + if !label { + err.span_label(span, "cannot resolve opaque type"); + } + err.emit() +} diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs new file mode 100644 index 000000000..32f66b06f --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -0,0 +1,1825 @@ +use super::potentially_plural_count; +use crate::errors::LifetimesOrBoundsMismatchOnTrait; +use hir::def_id::{DefId, LocalDefId}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +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::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, +}; +use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; +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, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, +}; +use std::iter; + +/// Checks that a method from an impl conforms to the signature of +/// the same method as declared in the trait. +/// +/// # Parameters +/// +/// - `impl_m`: type of the method we are checking +/// - `impl_m_span`: span to use for reporting errors +/// - `trait_m`: the method in the trait +/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation +pub(crate) fn compare_impl_method<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + trait_m: &ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, + trait_item_span: Option<Span>, +) { + debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); + + let impl_m_span = tcx.def_span(impl_m.def_id); + + if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) { + return; + } + + if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) { + return; + } + + if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) { + return; + } + + if let Err(_) = + compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span) + { + return; + } + + if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) { + return; + } + + if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) + { + return; + } +} + +/// This function is best explained by example. Consider a trait: +/// +/// trait Trait<'t, T> { +/// // `trait_m` +/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self; +/// } +/// +/// And an impl: +/// +/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo { +/// // `impl_m` +/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo; +/// } +/// +/// We wish to decide if those two method types are compatible. +/// For this we have to show that, assuming the bounds of the impl hold, the +/// bounds of `trait_m` imply the bounds of `impl_m`. +/// +/// We start out with `trait_to_impl_substs`, that maps the trait +/// type parameters to impl type parameters. This is taken from the +/// impl trait reference: +/// +/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo} +/// +/// We create a mapping `dummy_substs` that maps from the impl type +/// parameters to fresh types and regions. For type parameters, +/// this is the identity transform, but we could as well use any +/// placeholder types. For regions, we convert from bound to free +/// regions (Note: but only early-bound regions, i.e., those +/// declared on the impl or used in type parameter bounds). +/// +/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 } +/// +/// Now we can apply `placeholder_substs` to the type of the impl method +/// to yield a new function type in terms of our fresh, placeholder +/// types: +/// +/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo +/// +/// We now want to extract and substitute the type of the *trait* +/// method and compare it. To do so, we must create a compound +/// substitution by combining `trait_to_impl_substs` and +/// `impl_to_placeholder_substs`, and also adding a mapping for the method +/// type parameters. We extend the mapping to also include +/// the method parameters. +/// +/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 } +/// +/// Applying this to the trait method type yields: +/// +/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo +/// +/// This type is also the same but the name of the bound region (`'a` +/// vs `'b`). However, the normal subtyping rules on fn types handle +/// this kind of equivalency just fine. +/// +/// We now use these substitutions to ensure that all declared bounds are +/// satisfied by the implementation's method. +/// +/// We do this by creating a parameter environment which contains a +/// substitution corresponding to `impl_to_placeholder_substs`. We then build +/// `trait_to_placeholder_substs` and use it to convert the predicates contained +/// in the `trait_m` generics to the placeholder form. +/// +/// Finally we register each of these predicates as an obligation and check that +/// they hold. +#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))] +fn compare_predicate_entailment<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &AssocItem, + impl_m_span: Span, + trait_m: &AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let trait_to_impl_substs = impl_trait_ref.substs; + + // This node-id should be used for the `body_id` field on each + // `ObligationCause` (and the `FnCtxt`). + // + // 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( + impl_m_span, + impl_m_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_m.def_id.expect_local(), + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, + ); + + // Create mapping from impl to placeholder. + let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); + + // Create mapping from trait to placeholder. + let trait_to_placeholder_substs = + 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)?; + + // Create obligations for each predicate declared by the impl + // definition in the context of the trait's parameter + // environment. We can't just use `impl_env.caller_bounds`, + // however, because we want to replace all late-bound regions with + // region variables. + let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); + + debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds); + + // This is the only tricky bit of the new way we check implementation methods + // We need to build a set of predicates where only the method-level bounds + // are from the trait and we assume all other bounds from the implementation + // to be previously satisfied. + // + // We then register the obligations from the impl_m and check to see + // if all constraints hold. + hybrid_preds + .predicates + .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates); + + // Construct trait parameter environment and then shift it into the placeholder viewpoint. + // The key step here is to update the caller_bounds's predicates to be + // the new hybrid bounds we computed. + let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id); + let param_env = ty::ParamEnv::new( + tcx.intern_predicates(&hybrid_preds.predicates), + Reveal::UserFacing, + hir::Constness::NotConst, + ); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); + + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); + + 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); + + ocx.register_obligations(obligations); + let cause = ObligationCause::new( + span, + impl_m_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_m.def_id.expect_local(), + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, + ); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); + } + + // We now need to check that the signature of the impl method is + // compatible with that of the trait method. We do this by + // checking that `impl_fty <: trait_fty`. + // + // FIXME. Unfortunately, this doesn't quite work right now because + // associated type normalization is not integrated into subtype + // checks. For the comparison to be valid, we need to + // normalize the associated types in the impl/trait methods + // first. However, because function types bind regions, just + // calling `normalize_associated_types_in` would have no effect on + // any associated types appearing in the fn arguments or return + // type. + + // Compute placeholder form of impl and trait method tys. + let tcx = infcx.tcx; + + let mut wf_tys = FxHashSet::default(); + + let impl_sig = infcx.replace_bound_vars_with_fresh_vars( + impl_m_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ); + + 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)); + 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); + let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); + + // Next, add all inputs and output as well-formed tys. Importantly, + // 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); + // 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()); + let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); + + debug!("compare_impl_method: trait_fty={:?}", trait_fty); + + // FIXME: We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // 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)) + }); + } + + 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, + ); + } + } + _ => {} + } + + 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 Err(diag.emit()); + } + + // 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); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); + + Ok(()) +} + +pub fn collect_trait_impl_trait_tys<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Result<&'tcx FxHashMap<DefId, Ty<'tcx>>, ErrorGuaranteed> { + let impl_m = tcx.opt_associated_item(def_id).unwrap(); + let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap(); + let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap(); + let param_env = tcx.param_env(def_id); + + 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()); + let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); + let cause = ObligationCause::new( + return_span, + impl_m_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_m.def_id.expect_local(), + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, + ); + + // Create mapping from impl to placeholder. + let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); + + // Create mapping from trait to placeholder. + let trait_to_placeholder_substs = + impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs); + + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); + + let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); + let impl_sig = ocx.normalize( + norm_cause.clone(), + param_env, + infcx.replace_bound_vars_with_fresh_vars( + return_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ), + ); + let impl_return_ty = impl_sig.output(); + + let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); + let unnormalized_trait_sig = tcx + .liberate_late_bound_regions( + impl_m.def_id, + 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_return_ty = trait_sig.output(); + + let wf_tys = FxHashSet::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); + } + Err(terr) => { + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible return type for trait", + trait_m.name + ); + let hir = tcx.hir(); + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + hir.get_if_local(impl_m.def_id) + .and_then(|node| node.fn_decl()) + .map(|decl| (decl.output.span(), "return type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_return_ty.into(), + found: impl_return_ty.into(), + })), + terr, + false, + false, + ); + return Err(diag.emit()); + } + } + + // 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); + } + Err(terr) => { + let guar = tcx.sess.delay_span_bug( + return_span, + format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + ); + return Err(guar); + } + } + + // Check that all obligations are satisfied by the implementation's + // RPITs. + 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); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); + + let mut collected_tys = FxHashMap::default(); + for (def_id, (ty, substs)) in collector.types { + match infcx.fully_resolve(ty) { + Ok(ty) => { + // `ty` contains free regions that we created earlier while liberating the + // trait fn signature. However, projection normalization expects `ty` to + // contains `def_id`'s early-bound regions. + let id_substs = InternalSubsts::identity_for_item(tcx, def_id); + debug!(?id_substs, ?substs); + let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> = + std::iter::zip(substs, id_substs).collect(); + debug!(?map); + + // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound + // region substs that are synthesized during AST lowering. These are substs + // that are appended to the parent substs (trait and trait method). However, + // we're trying to infer the unsubstituted type value of the RPITIT inside + // the *impl*, so we can later use the impl's method substs to normalize + // an RPITIT to a concrete type (`confirm_impl_trait_in_trait_candidate`). + // + // Due to the design of RPITITs, during AST lowering, we have no idea that + // an impl method corresponds to a trait method with RPITITs in it. Therefore, + // we don't have a list of early-bound region substs for the RPITIT in the impl. + // Since early region parameters are index-based, we can't just rebase these + // (trait method) early-bound region substs onto the impl, and there's no + // guarantee that the indices from the trait substs and impl substs line up. + // So to fix this, we subtract the number of trait substs and add the number of + // impl substs to *renumber* these early-bound regions to their corresponding + // indices in the impl's substitutions list. + // + // Also, we only need to account for a difference in trait and impl substs, + // since we previously enforce that the trait method and impl method have the + // same generics. + 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; }; + let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind()) + else { + tcx + .sess + .delay_span_bug( + return_span, + "expected ReFree to map to ReEarlyBound" + ); + return tcx.lifetimes.re_static; + }; + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: e.def_id, + name: e.name, + index: (e.index as usize - num_trait_substs + num_impl_substs) as u32, + })) + }); + debug!(%ty); + collected_tys.insert(def_id, ty); + } + Err(err) => { + tcx.sess.delay_span_bug( + return_span, + format!("could not fully resolve: {ty} => {err:?}"), + ); + collected_tys.insert(def_id, tcx.ty_error()); + } + } + } + + Ok(&*tcx.arena.alloc(collected_tys)) +} + +struct ImplTraitInTraitCollector<'a, 'tcx> { + ocx: &'a ObligationCtxt<'a, 'tcx>, + types: FxHashMap<DefId, (Ty<'tcx>, ty::SubstsRef<'tcx>)>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, +} + +impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> { + fn new( + ocx: &'a ObligationCtxt<'a, 'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> Self { + ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id } + } +} + +impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.ocx.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Projection(proj) = ty.kind() + && self.tcx().def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + { + if let Some((ty, _)) = self.types.get(&proj.item_def_id) { + return *ty; + } + //FIXME(RPITIT): Deny nested RPITIT in substs too + if proj.substs.has_escaping_bound_vars() { + bug!("FIXME(RPITIT): error here"); + } + // Replace with infer var + let infer_ty = self.ocx.infcx.next_ty_var(TypeVariableOrigin { + span: self.span, + kind: TypeVariableOriginKind::MiscVariable, + }); + self.types.insert(proj.item_def_id, (infer_ty, proj.substs)); + // Recurse into bounds + 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), + self.param_env, + pred, + ); + + self.ocx.register_obligation(traits::Obligation::new( + ObligationCause::new( + self.span, + self.body_id, + ObligationCauseCode::BindingObligation(proj.item_def_id, pred_span), + ), + self.param_env, + pred, + )); + } + infer_ty + } else { + ty.super_fold_with(self) + } + } +} + +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, +) -> Result<(), ErrorGuaranteed> { + let trait_params = trait_generics.own_counts().lifetimes; + let impl_params = impl_generics.own_counts().lifetimes; + + debug!( + "check_region_bounds_on_impl_item: \ + trait_generics={:?} \ + impl_generics={:?}", + trait_generics, impl_generics + ); + + // Must have same number of early-bound lifetime parameters. + // Unfortunately, if the user screws up the bounds, then this + // will change classification between early and late. E.g., + // if in trait we have `<'a,'b:'a>`, and in impl we just have + // `<'a,'b>`, then we have 2 early-bound lifetime parameters + // in trait but 0 in the impl. But if we report "expected 2 + // but found 0" it's confusing, because it looks like there + // are zero. Since I don't quite know how to phrase things at + // the moment, give a kind of vague error message. + if trait_params != impl_params { + let span = tcx + .hir() + .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, + }); + return Err(reported); + } + + Ok(()) +} + +#[instrument(level = "debug", skip(infcx))] +fn extract_spans_for_error_reporting<'tcx>( + infcx: &infer::InferCtxt<'tcx>, + terr: TypeError<'_>, + cause: &ObligationCause<'tcx>, + impl_m: &ty::AssocItem, + trait_m: &ty::AssocItem, +) -> (Span, Option<Span>) { + let tcx = infcx.tcx; + let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) + } + _ => bug!("{:?} is not a method", impl_m), + }; + let trait_args = + trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind { + TraitItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) + } + _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), + }); + + match terr { + TypeError::ArgumentMutability(i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) + } + TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) + } + _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)), + } +} + +fn compare_self_type<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + impl_m_span: Span, + trait_m: &ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + // Try to give more informative error messages about self typing + // mismatches. Note that any mismatch will also be detected + // below, where we construct a canonical function type that + // includes the self parameter as a normal parameter. It's just + // that the error messages you get out of this code are a bit more + // inscrutable, particularly for cases where one method has no + // self. + + let self_string = |method: &ty::AssocItem| { + let untransformed_self_ty = match method.container { + ty::ImplContainer => impl_trait_ref.self_ty(), + ty::TraitContainer => tcx.types.self_param, + }; + let self_arg_ty = tcx.fn_sig(method.def_id).input(0); + let param_env = ty::ParamEnv::reveal_all(); + + let infcx = tcx.infer_ctxt().build(); + let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); + let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); + match ExplicitSelf::determine(self_arg_ty, can_eq_self) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {self_arg_ty}"), + } + }; + + match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) { + (false, false) | (true, true) => {} + + (false, true) => { + let self_descr = self_string(impl_m); + let mut err = struct_span_err!( + tcx.sess, + impl_m_span, + E0185, + "method `{}` has a `{}` declaration in the impl, but not in the trait", + trait_m.name, + self_descr + ); + err.span_label(impl_m_span, format!("`{self_descr}` used in impl")); + if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { + err.span_label(span, format!("trait method declared without `{self_descr}`")); + } else { + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + } + let reported = err.emit(); + return Err(reported); + } + + (true, false) => { + let self_descr = self_string(trait_m); + let mut err = struct_span_err!( + tcx.sess, + impl_m_span, + E0186, + "method `{}` has a `{}` declaration in the trait, but not in the impl", + trait_m.name, + self_descr + ); + err.span_label(impl_m_span, format!("expected `{self_descr}` in impl")); + if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { + err.span_label(span, format!("`{self_descr}` used in trait")); + } else { + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + } + let reported = err.emit(); + return Err(reported); + } + } + + Ok(()) +} + +/// Checks that the number of generics on a given assoc item in a trait impl is the same +/// as the number of generics on the respective assoc item in the trait definition. +/// +/// For example this code emits the errors in the following code: +/// ``` +/// trait Trait { +/// fn foo(); +/// type Assoc<T>; +/// } +/// +/// impl Trait for () { +/// fn foo<T>() {} +/// //~^ error +/// type Assoc = u32; +/// //~^ error +/// } +/// ``` +/// +/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or +/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in +/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters +fn compare_number_of_generics<'tcx>( + tcx: TyCtxt<'tcx>, + impl_: &ty::AssocItem, + _impl_span: Span, + trait_: &ty::AssocItem, + trait_span: Option<Span>, +) -> 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(); + + // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented + // in `compare_generic_param_kinds` which will give a nicer error message than something like: + // "expected 1 type parameter, found 0 type parameters" + if (trait_own_counts.types + trait_own_counts.consts) + == (impl_own_counts.types + impl_own_counts.consts) + { + return Ok(()); + } + + let matchings = [ + ("type", trait_own_counts.types, impl_own_counts.types), + ("const", trait_own_counts.consts, impl_own_counts.consts), + ]; + + let item_kind = assoc_item_kind_str(impl_); + + let mut err_occurred = None; + for (kind, trait_count, impl_count) in matchings { + if impl_count != trait_count { + let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| { + let mut spans = generics + .params + .iter() + .filter(|p| match p.kind { + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Elided, + } => { + // A fn can have an arbitrary number of extra elided lifetimes for the + // same signature. + !matches!(kind, ty::AssocKind::Fn) + } + _ => true, + }) + .map(|p| p.span) + .collect::<Vec<Span>>(); + if spans.is_empty() { + spans = vec![generics.span] + } + spans + }; + let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() { + let trait_item = tcx.hir().expect_trait_item(def_id); + let arg_spans: Vec<Span> = arg_spans(trait_.kind, trait_item.generics); + let impl_trait_spans: Vec<Span> = trait_item + .generics + .params + .iter() + .filter_map(|p| match p.kind { + GenericParamKind::Type { synthetic: true, .. } => Some(p.span), + _ => None, + }) + .collect(); + (Some(arg_spans), impl_trait_spans) + } else { + (trait_span.map(|s| vec![s]), vec![]) + }; + + let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local()); + let impl_item_impl_trait_spans: Vec<Span> = impl_item + .generics + .params + .iter() + .filter_map(|p| match p.kind { + GenericParamKind::Type { synthetic: true, .. } => Some(p.span), + _ => None, + }) + .collect(); + let spans = arg_spans(impl_.kind, impl_item.generics); + let span = spans.first().copied(); + + let mut err = tcx.sess.struct_span_err_with_code( + spans, + &format!( + "{} `{}` has {} {kind} parameter{} but its trait \ + declaration has {} {kind} parameter{}", + item_kind, + trait_.name, + impl_count, + pluralize!(impl_count), + trait_count, + pluralize!(trait_count), + kind = kind, + ), + DiagnosticId::Error("E0049".into()), + ); + + let mut suffix = None; + + if let Some(spans) = trait_spans { + let mut spans = spans.iter(); + if let Some(span) = spans.next() { + err.span_label( + *span, + format!( + "expected {} {} parameter{}", + trait_count, + kind, + pluralize!(trait_count), + ), + ); + } + for span in spans { + err.span_label(*span, ""); + } + } else { + suffix = Some(format!(", expected {trait_count}")); + } + + if let Some(span) = span { + err.span_label( + span, + format!( + "found {} {} parameter{}{}", + impl_count, + kind, + pluralize!(impl_count), + suffix.unwrap_or_else(String::new), + ), + ); + } + + for span in impl_trait_spans.iter().chain(impl_item_impl_trait_spans.iter()) { + err.span_label(*span, "`impl Trait` introduces an implicit type parameter"); + } + + let reported = err.emit(); + err_occurred = Some(reported); + } + } + + if let Some(reported) = err_occurred { Err(reported) } else { Ok(()) } +} + +fn compare_number_of_method_arguments<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + impl_m_span: Span, + trait_m: &ty::AssocItem, + trait_item_span: Option<Span>, +) -> Result<(), ErrorGuaranteed> { + let impl_m_fty = tcx.fn_sig(impl_m.def_id); + let trait_m_fty = tcx.fn_sig(trait_m.def_id); + let trait_number_args = trait_m_fty.inputs().skip_binder().len(); + let impl_number_args = impl_m_fty.inputs().skip_binder().len(); + if trait_number_args != impl_number_args { + let trait_span = if let Some(def_id) = trait_m.def_id.as_local() { + match tcx.hir().expect_trait_item(def_id).kind { + TraitItemKind::Fn(ref trait_m_sig, _) => { + let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 }; + if let Some(arg) = trait_m_sig.decl.inputs.get(pos) { + Some(if pos == 0 { + arg.span + } else { + arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo()) + }) + } else { + trait_item_span + } + } + _ => bug!("{:?} is not a method", impl_m), + } + } else { + trait_item_span + }; + let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref impl_m_sig, _) => { + let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; + if let Some(arg) = impl_m_sig.decl.inputs.get(pos) { + if pos == 0 { + arg.span + } else { + arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo()) + } + } else { + impl_m_span + } + } + _ => bug!("{:?} is not a method", impl_m), + }; + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0050, + "method `{}` has {} but the declaration in trait `{}` has {}", + trait_m.name, + potentially_plural_count(impl_number_args, "parameter"), + tcx.def_path_str(trait_m.def_id), + trait_number_args + ); + if let Some(trait_span) = trait_span { + err.span_label( + trait_span, + format!( + "trait requires {}", + potentially_plural_count(trait_number_args, "parameter") + ), + ); + } else { + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); + } + err.span_label( + impl_span, + format!( + "expected {}, found {}", + potentially_plural_count(trait_number_args, "parameter"), + impl_number_args + ), + ); + let reported = err.emit(); + return Err(reported); + } + + Ok(()) +} + +fn compare_synthetic_generics<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + trait_m: &ty::AssocItem, +) -> Result<(), ErrorGuaranteed> { + // FIXME(chrisvittal) Clean up this function, list of FIXME items: + // 1. Better messages for the span labels + // 2. Explanation as to what is going on + // If we get here, we already have the same number of generics, so the zip will + // be okay. + let mut error_found = None; + 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_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind { + GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, + }); + let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind { + GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, + }); + for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in + iter::zip(impl_m_type_params, trait_m_type_params) + { + if impl_synthetic != trait_synthetic { + let impl_def_id = impl_def_id.expect_local(); + let impl_span = tcx.def_span(impl_def_id); + let trait_span = tcx.def_span(trait_def_id); + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0643, + "method `{}` has incompatible signature for trait", + trait_m.name + ); + err.span_label(trait_span, "declaration in trait here"); + match (impl_synthetic, trait_synthetic) { + // The case where the impl method uses `impl Trait` but the trait method uses + // explicit generics + (true, false) => { + err.span_label(impl_span, "expected generic parameter, found `impl Trait`"); + (|| { + // try taking the name from the trait impl + // FIXME: this is obviously suboptimal since the name can already be used + // as another generic argument + let new_name = tcx.opt_item_name(trait_def_id)?; + let trait_m = trait_m.def_id.as_local()?; + let trait_m = tcx.hir().expect_trait_item(trait_m); + + let impl_m = impl_m.def_id.as_local()?; + let impl_m = tcx.hir().expect_impl_item(impl_m); + + // in case there are no generics, take the spot between the function name + // and the opening paren of the argument list + let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi(); + // in case there are generics, just replace them + let generics_span = + impl_m.generics.span.substitute_dummy(new_generics_span); + // replace with the generics from the trait + let new_generics = + tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?; + + err.multipart_suggestion( + "try changing the `impl Trait` argument to a generic parameter", + vec![ + // replace `impl Trait` with `T` + (impl_span, new_name.to_string()), + // replace impl method generics with trait method generics + // This isn't quite right, as users might have changed the names + // of the generics, but it works for the common case + (generics_span, new_generics), + ], + Applicability::MaybeIncorrect, + ); + Some(()) + })(); + } + // The case where the trait method uses `impl Trait`, but the impl method uses + // explicit generics. + (false, true) => { + err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); + (|| { + let impl_m = impl_m.def_id.as_local()?; + let impl_m = tcx.hir().expect_impl_item(impl_m); + let input_tys = match impl_m.kind { + hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, + _ => unreachable!(), + }; + struct Visitor(Option<Span>, hir::def_id::LocalDefId); + impl<'v> intravisit::Visitor<'v> for Visitor { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + intravisit::walk_ty(self, ty); + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = + ty.kind + && let Res::Def(DefKind::TyParam, def_id) = path.res + && def_id == self.1.to_def_id() + { + self.0 = Some(ty.span); + } + } + } + let mut visitor = Visitor(None, impl_def_id); + for ty in input_tys { + intravisit::Visitor::visit_ty(&mut visitor, ty); + } + let span = visitor.0?; + + let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; + let bounds = bounds.first()?.span().to(bounds.last()?.span()); + let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?; + + err.multipart_suggestion( + "try removing the generic parameter and using `impl Trait` instead", + vec![ + // delete generic parameters + (impl_m.generics.span, String::new()), + // replace param usage with `impl Trait` + (span, format!("impl {bounds}")), + ], + Applicability::MaybeIncorrect, + ); + Some(()) + })(); + } + _ => unreachable!(), + } + let reported = err.emit(); + error_found = Some(reported); + } + } + if let Some(reported) = error_found { Err(reported) } else { Ok(()) } +} + +/// Checks that all parameters in the generics of a given assoc item in a trait impl have +/// the same kind as the respective generic parameter in the trait def. +/// +/// For example all 4 errors in the following code are emitted here: +/// ``` +/// trait Foo { +/// fn foo<const N: u8>(); +/// type bar<const N: u8>; +/// fn baz<const N: u32>(); +/// type blah<T>; +/// } +/// +/// impl Foo for () { +/// fn foo<const N: u64>() {} +/// //~^ error +/// type bar<const N: u64> {} +/// //~^ error +/// fn baz<T>() {} +/// //~^ error +/// type blah<const N: i64> = u32; +/// //~^ error +/// } +/// ``` +/// +/// This function does not handle lifetime parameters +fn compare_generic_param_kinds<'tcx>( + tcx: TyCtxt<'tcx>, + impl_item: &ty::AssocItem, + trait_item: &ty::AssocItem, +) -> Result<(), ErrorGuaranteed> { + assert_eq!(impl_item.kind, trait_item.kind); + + let ty_const_params_of = |def_id| { + tcx.generics_of(def_id).params.iter().filter(|param| { + matches!( + param.kind, + GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. } + ) + }) + }; + + for (param_impl, param_trait) in + iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id)) + { + use GenericParamDefKind::*; + if match (¶m_impl.kind, ¶m_trait.kind) { + (Const { .. }, Const { .. }) + if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) => + { + true + } + (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true, + // this is exhaustive so that anyone adding new generic param kinds knows + // to make sure this error is reported for them. + (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false, + (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(), + } { + let param_impl_span = tcx.def_span(param_impl.def_id); + let param_trait_span = tcx.def_span(param_trait.def_id); + + let mut err = struct_span_err!( + tcx.sess, + param_impl_span, + E0053, + "{} `{}` has an incompatible generic parameter for trait `{}`", + assoc_item_kind_str(&impl_item), + trait_item.name, + &tcx.def_path_str(tcx.parent(trait_item.def_id)) + ); + + let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind { + Const { .. } => { + format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id)) + } + Type { .. } => format!("{} type parameter", prefix), + Lifetime { .. } => unreachable!(), + }; + + let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap(); + err.span_label(trait_header_span, ""); + err.span_label(param_trait_span, make_param_message("expected", param_trait)); + + let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id)); + err.span_label(impl_header_span, ""); + err.span_label(param_impl_span, make_param_message("found", param_impl)); + + let reported = err.emit(); + return Err(reported); + } + } + + Ok(()) +} + +/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead +pub(crate) fn raw_compare_const_impl<'tcx>( + tcx: TyCtxt<'tcx>, + (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId), +) -> Result<(), ErrorGuaranteed> { + let impl_const_item = tcx.associated_item(impl_const_item_def); + let trait_const_item = tcx.associated_item(trait_const_item_def); + let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap(); + debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); + + let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id()); + + let infcx = tcx.infer_ctxt().build(); + let param_env = tcx.param_env(impl_const_item_def.to_def_id()); + let ocx = ObligationCtxt::new(&infcx); + + // The below is for the most part highly similar to the procedure + // for methods above. It is simpler in many respects, especially + // because we shouldn't really have to deal with lifetimes or + // predicates. In fact some of this should probably be put into + // shared functions because of DRY violations... + let trait_to_impl_substs = impl_trait_ref.substs; + + // Create a parameter environment that represents the implementation's + // method. + let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def); + + // Compute placeholder form of impl and trait const tys. + let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()); + let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs); + let mut cause = ObligationCause::new( + impl_c_span, + impl_c_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_const_item_def, + trait_item_def_id: trait_const_item_def, + kind: impl_const_item.kind, + }, + ); + + // There is no "body" here, so just pass dummy id. + let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); + + debug!("compare_const_impl: impl_ty={:?}", impl_ty); + + let trait_ty = ocx.normalize(cause.clone(), 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)); + + if let Err(terr) = err { + debug!( + "checking associated const for compatibility: impl ty {:?}, trait ty {:?}", + impl_ty, trait_ty + ); + + // Locate the Span containing just the type of the offending impl + match tcx.hir().expect_impl_item(impl_const_item_def).kind { + ImplItemKind::Const(ref ty, _) => cause.span = ty.span, + _ => bug!("{:?} is not a impl const", impl_const_item), + } + + let mut diag = struct_span_err!( + tcx.sess, + cause.span, + E0326, + "implemented const `{}` has an incompatible type for trait", + trait_const_item.name + ); + + let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| { + // Add a label to the Span containing just the type of the const + match tcx.hir().expect_trait_item(trait_c_def_id).kind { + TraitItemKind::Const(ref ty, _) => ty.span, + _ => bug!("{:?} is not a trait const", trait_const_item), + } + }); + + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_c_span.map(|span| (span, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_ty.into(), + found: impl_ty.into(), + })), + terr, + false, + false, + ); + return Err(diag.emit()); + }; + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false)); + } + + // FIXME return `ErrorReported` if region obligations error? + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment); + Ok(()) +} + +pub(crate) fn compare_ty_impl<'tcx>( + tcx: TyCtxt<'tcx>, + impl_ty: &ty::AssocItem, + impl_ty_span: Span, + trait_ty: &ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, + trait_item_span: Option<Span>, +) { + 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_generic_param_kinds(tcx, impl_ty, trait_ty)?; + + let sp = tcx.def_span(impl_ty.def_id); + compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?; + + check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) + })(); +} + +/// The equivalent of [compare_predicate_entailment], but for associated types +/// instead of associated functions. +fn compare_type_predicate_entailment<'tcx>( + tcx: TyCtxt<'tcx>, + impl_ty: &ty::AssocItem, + impl_ty_span: Span, + trait_ty: &ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); + 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, + )?; + + let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs); + + if impl_ty_own_bounds.is_empty() { + // Nothing to check. + return Ok(()); + } + + // This `HirId` should be used for the `body_id` field on each + // `ObligationCause` (and the `FnCtxt`). This is what + // `regionck_item` expects. + let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); + debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs); + + // The predicates declared by the impl definition, the trait and the + // associated type in the trait are assumed. + let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); + hybrid_preds + .predicates + .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates); + + debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); + + let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); + let param_env = ty::ParamEnv::new( + tcx.intern_predicates(&hybrid_preds.predicates), + Reveal::UserFacing, + hir::Constness::NotConst, + ); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + 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 cause = ObligationCause::new( + span, + impl_ty_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + kind: impl_ty.kind, + }, + ); + ocx.register_obligations(obligations); + ocx.register_obligation(traits::Obligation::new(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); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); + + Ok(()) +} + +/// Validate that `ProjectionCandidate`s created for this associated type will +/// be valid. +/// +/// Usually given +/// +/// trait X { type Y: Copy } impl X for T { type Y = S; } +/// +/// We are able to normalize `<T as X>::U` to `S`, and so when we check the +/// impl is well-formed we have to prove `S: Copy`. +/// +/// For default associated types the normalization is not possible (the value +/// from the impl could be overridden). We also can't normalize generic +/// associated types (yet) because they contain bound parameters. +#[instrument(level = "debug", skip(tcx))] +pub fn check_type_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ty: &ty::AssocItem, + impl_ty: &ty::AssocItem, + impl_ty_span: Span, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + // Given + // + // impl<A, B> Foo<u32> for (A, B) { + // type Bar<C> =... + // } + // + // - `impl_trait_ref` would be `<(A, B) as Foo<u32>> + // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0) + // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from + // the *trait* with the generic associated type parameters (as bound vars). + // + // A note regarding the use of bound vars here: + // Imagine as an example + // ``` + // trait Family { + // type Member<C: Eq>; + // } + // + // impl Family for VecFamily { + // type Member<C: Eq> = i32; + // } + // ``` + // Here, we would generate + // ```notrust + // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) } + // ``` + // when we really would like to generate + // ```notrust + // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) } + // ``` + // But, this is probably fine, because although the first clause can be used with types C that + // do not implement Eq, for it to cause some kind of problem, there would have to be a + // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type + // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing + // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in + // the trait (notably, that X: Eq and T: Family). + let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id); + let mut substs = smallvec::SmallVec::with_capacity(defs.count()); + if let Some(def_id) = defs.parent { + let parent_defs = tcx.generics_of(def_id); + InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| { + tcx.mk_param_from_def(param) + }); + } + let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = + smallvec::SmallVec::with_capacity(defs.count()); + InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind { + GenericParamDefKind::Type { .. } => { + let kind = ty::BoundTyKind::Param(param.name); + let bound_var = ty::BoundVariableKind::Ty(kind); + bound_vars.push(bound_var); + tcx.mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, + )) + .into() + } + GenericParamDefKind::Lifetime => { + let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name); + let bound_var = ty::BoundVariableKind::Region(kind); + bound_vars.push(bound_var); + tcx.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, + )) + .into() + } + 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), + ), + }) + .into() + } + }); + let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter()); + let impl_ty_substs = tcx.intern_substs(&substs); + let container_id = impl_ty.container_id(tcx); + + let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); + let impl_ty_value = tcx.type_of(impl_ty.def_id); + + let param_env = tcx.param_env(impl_ty.def_id); + + // When checking something like + // + // trait X { type Y: PartialEq<<Self as X>::Y> } + // impl X for T { default type Y = S; } + // + // We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case + // we want <T as X>::Y to normalize to S. This is valid because we are + // checking the default value specifically here. Add this equality to the + // ParamEnv for normalization specifically. + let normalize_param_env = { + let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>(); + match impl_ty_value.kind() { + ty::Projection(proj) + if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs => + { + // Don't include this predicate if the projected type is + // exactly the same as the projection. This can occur in + // (somewhat dubious) code like this: + // + // impl<T> X for T where T: X { type Y = <T as X>::Y; } + } + _ => predicates.push( + ty::Binder::bind_with_vars( + ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + item_def_id: trait_ty.def_id, + substs: rebased_substs, + }, + term: impl_ty_value.into(), + }, + bound_vars, + ) + .to_predicate(tcx), + ), + }; + ty::ParamEnv::new( + tcx.intern_predicates(&predicates), + Reveal::UserFacing, + param_env.constness(), + ) + }; + debug!(?normalize_param_env); + + let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); + let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); + let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); + + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + 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, + ObligationCauseCode::CheckAssociatedTypeBounds { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + }, + ); + let mk_cause = |span: Span| { + let code = if span.is_dummy() { + traits::ItemObligation(trait_ty.def_id) + } else { + traits::BindingObligation(trait_ty.def_id, span) + }; + ObligationCause::new(impl_ty_span, impl_ty_hir_id, code) + }; + + let obligations = tcx + .bound_explicit_item_bounds(trait_ty.def_id) + .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) + }) + .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, + ); + 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); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); + + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); + + let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + for (key, value) in constraints { + infcx + .err_ctxt() + .report_mismatched_types( + &ObligationCause::misc( + value.hidden_type.span, + tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), + ), + tcx.mk_opaque(key.def_id.to_def_id(), key.substs), + value.hidden_type.ty, + TypeError::Mismatch, + ) + .emit(); + } + + Ok(()) +} + +fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str { + match impl_item.kind { + ty::AssocKind::Const => "const", + ty::AssocKind::Fn => "method", + ty::AssocKind::Type => "type", + } +} diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 321064ec0..a74016e22 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,4 +1,4 @@ -// FIXME(@lcnr): Move this module out of `rustc_typeck`. +// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. // // We don't do any drop checking during hir typeck. use crate::hir::def_id::{DefId, LocalDefId}; @@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); let assumptions_in_impl_context = assumptions_in_impl_context.predicates; + debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates); + let self_param_env = tcx.param_env(self_type_did); // An earlier version of this code attempted to do this checking @@ -182,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - // Since struct predicates cannot have ~const, project the impl predicate - // onto one that ignores the constness. This is equivalent to saying that - // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` - // in the impl. - let non_const_a = - ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; - relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() + 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() @@ -196,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts(self_param_env.and((a, 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)), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3f2a0da8d..609095c9c 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -7,10 +7,10 @@ use crate::errors::{ }; use crate::require_same_types; -use rustc_errors::struct_span_err; +use hir::def_id::DefId; +use rustc_errors::{struct_span_err, DiagnosticMessage}; use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>( ) { let (own_counts, span) = match &it.kind { hir::ForeignItemKind::Fn(.., generics) => { - let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts(); + let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts(); (own_counts, generics.span) } _ => { @@ -57,18 +57,25 @@ fn equate_intrinsic_type<'tcx>( { let fty = tcx.mk_fn_ptr(sig); let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType); - require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty); + require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty); } } /// Returns the unsafety of the given intrinsic. -pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { - match intrinsic { +pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety { + let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) { + true => hir::Unsafety::Normal, + false => hir::Unsafety::Unsafe, + }; + let is_in_list = match tcx.item_name(intrinsic_id) { // When adding a new intrinsic to this list, // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. sym::abort + | sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_uninit_valid | sym::size_of | sym::min_align_of | sym::needs_drop @@ -92,8 +99,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_id | sym::likely | sym::unlikely - | sym::ptr_guaranteed_eq - | sym::ptr_guaranteed_ne + | sym::ptr_guaranteed_cmp | sym::minnumf32 | sym::minnumf64 | sym::maxnumf32 @@ -102,16 +108,29 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_name | sym::forget | sym::black_box - | sym::variant_count => hir::Unsafety::Normal, + | sym::variant_count + | sym::ptr_mask => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, + }; + + if has_safe_attr != is_in_list { + tcx.sess.struct_span_err( + tcx.def_span(intrinsic_id), + DiagnosticMessage::Str(format!( + "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`", + tcx.item_name(intrinsic_id) + ))).emit(); } + + is_in_list } /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n))); - let intrinsic_name = tcx.item_name(it.def_id.to_def_id()); + let intrinsic_id = it.owner_id.to_def_id(); + let intrinsic_name = tcx.item_name(intrinsic_id); let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds( @@ -158,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { }; (n_tps, 0, inputs, output, hir::Unsafety::Unsafe) } else { - let unsafety = intrinsic_operation_unsafety(intrinsic_name); + let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, inputs, output) = match intrinsic_name { sym::abort => (0, Vec::new(), tcx.types.never), sym::unreachable => (0, Vec::new(), tcx.types.never), @@ -200,6 +219,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ], tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), ), + sym::ptr_mask => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.usize, + ], + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + ), + sym::copy | sym::copy_nonoverlapping => ( 1, vec![ @@ -289,8 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) } - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) + sym::ptr_guaranteed_cmp => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8) } sym::const_allocate => { @@ -465,7 +493,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), + sym::simd_cast + | sym::simd_as + | sym::simd_cast_ptr + | sym::simd_expose_addr + | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs new file mode 100644 index 000000000..17c4d0d48 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -0,0 +1,437 @@ +use rustc_ast::InlineAsmTemplatePiece; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; +use rustc_session::lint; +use rustc_span::{Symbol, DUMMY_SP}; +use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; + +pub struct InlineAsmCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>, +} + +impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { + pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { + InlineAsmCtxt { + tcx, + param_env: ty::ParamEnv::empty(), + get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")), + } + } + + pub fn new_in_fn( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a, + ) -> Self { + InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) } + } + + // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` + fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { + // Type still may have region variables, but `Sized` does not depend + // on those, so just erase them before querying. + if ty.is_sized(self.tcx, self.param_env) { + return true; + } + if let ty::Foreign(..) = ty.kind() { + return true; + } + false + } + + fn check_asm_operand_type( + &self, + idx: usize, + reg: InlineAsmRegOrRegClass, + expr: &'tcx hir::Expr<'tcx>, + template: &[InlineAsmTemplatePiece], + is_input: bool, + tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>, + target_features: &FxHashSet<Symbol>, + ) -> Option<InlineAsmType> { + let ty = (self.get_operand_ty)(expr); + if ty.has_non_region_infer() { + bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); + } + let asm_ty_isize = match self.tcx.sess.target.pointer_width { + 16 => InlineAsmType::I16, + 32 => InlineAsmType::I32, + 64 => InlineAsmType::I64, + _ => unreachable!(), + }; + + let asm_ty = match *ty.kind() { + // `!` is allowed for input but not for output (issue #87802) + ty::Never if is_input => return None, + ty::Error(_) => return None, + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), + ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), + ty::FnPtr(_) => Some(asm_ty_isize), + ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { + Some(asm_ty_isize) + } + ty::Adt(adt, substs) if adt.repr().simd() => { + let fields = &adt.non_enum_variant().fields; + let elem_ty = fields[0].ty(self.tcx, substs); + match elem_ty.kind() { + ty::Never | ty::Error(_) => return None, + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { + Some(InlineAsmType::VecI8(fields.len() as u64)) + } + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { + Some(InlineAsmType::VecI16(fields.len() as u64)) + } + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { + Some(InlineAsmType::VecI32(fields.len() as u64)) + } + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { + Some(InlineAsmType::VecI64(fields.len() as u64)) + } + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { + Some(InlineAsmType::VecI128(fields.len() as u64)) + } + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { + Some(match self.tcx.sess.target.pointer_width { + 16 => InlineAsmType::VecI16(fields.len() as u64), + 32 => InlineAsmType::VecI32(fields.len() as u64), + 64 => InlineAsmType::VecI64(fields.len() as u64), + _ => unreachable!(), + }) + } + ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)), + ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)), + _ => None, + } + } + ty::Infer(_) => unreachable!(), + _ => None, + }; + let Some(asm_ty) = asm_ty else { + let msg = &format!("cannot use value of type `{ty}` for inline assembly"); + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ); + err.emit(); + return None; + }; + + // Check that the type implements Copy. The only case where this can + // possibly fail is for SIMD types which don't #[derive(Copy)]. + if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { + let msg = "arguments for inline assembly must be copyable"; + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note(&format!("`{ty}` does not implement the Copy trait")); + err.emit(); + } + + // Ideally we wouldn't need to do this, but LLVM's register allocator + // really doesn't like it when tied operands have different types. + // + // This is purely an LLVM limitation, but we have to live with it since + // there is no way to hide this with implicit conversions. + // + // For the purposes of this check we only look at the `InlineAsmType`, + // which means that pointers and integers are treated as identical (modulo + // size). + if let Some((in_expr, Some(in_asm_ty))) = tied_input { + if in_asm_ty != asm_ty { + let msg = "incompatible types for asm inout argument"; + let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); + + let in_expr_ty = (self.get_operand_ty)(in_expr); + err.span_label(in_expr.span, &format!("type `{in_expr_ty}`")); + err.span_label(expr.span, &format!("type `{ty}`")); + err.note( + "asm inout arguments must have the same type, \ + unless they are both pointers or integers of the same size", + ); + err.emit(); + } + + // All of the later checks have already been done on the input, so + // let's not emit errors and warnings twice. + return Some(asm_ty); + } + + // Check the type against the list of types supported by the selected + // register class. + let asm_arch = self.tcx.sess.asm_arch.unwrap(); + let reg_class = reg.reg_class(); + let supported_tys = reg_class.supported_types(asm_arch); + let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else { + let msg = &format!("type `{ty}` cannot be used with this register class"); + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + let supported_tys: Vec<_> = + supported_tys.iter().map(|(t, _)| t.to_string()).collect(); + err.note(&format!( + "register class `{}` supports these types: {}", + reg_class.name(), + supported_tys.join(", "), + )); + if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) { + err.help(&format!( + "consider using the `{}` register class instead", + suggest.name() + )); + } + err.emit(); + return Some(asm_ty); + }; + + // Check whether the selected type requires a target feature. Note that + // this is different from the feature check we did earlier. While the + // previous check checked that this register class is usable at all + // with the currently enabled features, some types may only be usable + // with a register class when a certain feature is enabled. We check + // this here since it depends on the results of typeck. + // + // Also note that this check isn't run when the operand type is never + // (!). In that case we still need the earlier check to verify that the + // register class is usable at all. + if let Some(feature) = feature { + if !target_features.contains(&feature) { + let msg = &format!("`{}` target feature is not enabled", feature); + let mut err = self.tcx.sess.struct_span_err(expr.span, msg); + err.note(&format!( + "this is required to use type `{}` with register class `{}`", + ty, + reg_class.name(), + )); + err.emit(); + return Some(asm_ty); + } + } + + // Check whether a modifier is suggested for using this type. + if let Some((suggested_modifier, suggested_result)) = + reg_class.suggest_modifier(asm_arch, asm_ty) + { + // Search for any use of this operand without a modifier and emit + // the suggestion for them. + let mut spans = vec![]; + for piece in template { + if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece + { + if operand_idx == idx && modifier.is_none() { + spans.push(span); + } + } + } + if !spans.is_empty() { + let (default_modifier, default_result) = + reg_class.default_modifier(asm_arch).unwrap(); + self.tcx.struct_span_lint_hir( + lint::builtin::ASM_SUB_REGISTER, + expr.hir_id, + spans, + "formatting may not be suitable for sub-register argument", + |lint| { + lint.span_label(expr.span, "for this argument"); + lint.help(&format!( + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", + )); + lint.help(&format!( + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", + )); + lint + }, + ); + } + } + + Some(asm_ty) + } + + pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) { + let hir = self.tcx.hir(); + let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id(); + let target_features = self.tcx.asm_target_features(enclosing_def_id); + let Some(asm_arch) = self.tcx.sess.asm_arch else { + self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm"); + return; + }; + for (idx, (op, op_sp)) in asm.operands.iter().enumerate() { + // Validate register classes against currently enabled target + // features. We check that at least one type is available for + // the enabled features. + // + // We ignore target feature requirements for clobbers: if the + // feature is disabled then the compiler doesn't care what we + // do with the registers. + // + // Note that this is only possible for explicit register + // operands, which cannot be used in the asm string. + if let Some(reg) = op.reg() { + // Some explicit registers cannot be used depending on the + // target. Reject those here. + if let InlineAsmRegOrRegClass::Reg(reg) = reg { + if let InlineAsmReg::Err = reg { + // `validate` will panic on `Err`, as an error must + // already have been reported. + continue; + } + if let Err(msg) = reg.validate( + asm_arch, + self.tcx.sess.relocation_model(), + &target_features, + &self.tcx.sess.target, + op.is_clobber(), + ) { + let msg = format!("cannot use register `{}`: {}", reg.name(), msg); + self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); + continue; + } + } + + if !op.is_clobber() { + let mut missing_required_features = vec![]; + let reg_class = reg.reg_class(); + if let InlineAsmRegClass::Err = reg_class { + continue; + } + for &(_, feature) in reg_class.supported_types(asm_arch) { + match feature { + Some(feature) => { + if target_features.contains(&feature) { + missing_required_features.clear(); + break; + } else { + missing_required_features.push(feature); + } + } + None => { + missing_required_features.clear(); + break; + } + } + } + + // We are sorting primitive strs here and can use unstable sort here + missing_required_features.sort_unstable(); + missing_required_features.dedup(); + match &missing_required_features[..] { + [] => {} + [feature] => { + let msg = format!( + "register class `{}` requires the `{}` target feature", + reg_class.name(), + feature + ); + self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); + // register isn't enabled, don't do more checks + continue; + } + features => { + let msg = format!( + "register class `{}` requires at least one of the following target features: {}", + reg_class.name(), + features + .iter() + .map(|f| f.as_str()) + .intersperse(", ") + .collect::<String>(), + ); + self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); + // register isn't enabled, don't do more checks + continue; + } + } + } + } + + match *op { + hir::InlineAsmOperand::In { reg, ref expr } => { + self.check_asm_operand_type( + idx, + reg, + expr, + asm.template, + true, + None, + &target_features, + ); + } + hir::InlineAsmOperand::Out { reg, late: _, ref expr } => { + if let Some(expr) = expr { + self.check_asm_operand_type( + idx, + reg, + expr, + asm.template, + false, + None, + &target_features, + ); + } + } + hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => { + self.check_asm_operand_type( + idx, + reg, + expr, + asm.template, + false, + None, + &target_features, + ); + } + hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => { + let in_ty = self.check_asm_operand_type( + idx, + reg, + in_expr, + asm.template, + true, + None, + &target_features, + ); + if let Some(out_expr) = out_expr { + self.check_asm_operand_type( + idx, + reg, + out_expr, + asm.template, + false, + Some((in_expr, in_ty)), + &target_features, + ); + } + } + // No special checking is needed for these: + // - Typeck has checked that Const operands are integers. + // - AST lowering guarantees that SymStatic points to a static. + hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {} + // Check that sym actually points to a function. Later passes + // depend on this. + hir::InlineAsmOperand::SymFn { anon_const } => { + let ty = self.tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); + match ty.kind() { + ty::Never | ty::Error(_) => {} + ty::FnDef(..) => {} + _ => { + let mut err = + self.tcx.sess.struct_span_err(*op_sp, "invalid `sym` operand"); + err.span_label( + self.tcx.hir().span(anon_const.body.hir_id), + &format!("is {} `{}`", ty.kind().article(), ty), + ); + err.help("`sym` operands must refer to either a function or a static"); + err.emit(); + } + }; + } + } + } + } +} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs new file mode 100644 index 000000000..2e7b10257 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -0,0 +1,515 @@ +/*! + +# typeck: check phase + +Within the check phase of type check, we check each item one at a time +(bodies of function expressions are checked as part of the containing +function). Inference is used to supply types wherever they are unknown. + +By far the most complex case is checking the body of a function. This +can be broken down into several distinct phases: + +- gather: creates type variables to represent the type of each local + variable and pattern binding. + +- main: the main pass does the lion's share of the work: it + determines the types of all expressions, resolves + methods, checks for most invalid conditions, and so forth. In + some cases, where a type is unknown, it may create a type or region + variable and use that as the type of an expression. + + In the process of checking, various constraints will be placed on + these type variables through the subtyping relationships requested + through the `demand` module. The `infer` module is in charge + of resolving those constraints. + +- regionck: after main is complete, the regionck pass goes over all + types looking for regions and making sure that they did not escape + into places where they are not in scope. This may also influence the + final assignments of the various region variables if there is some + flexibility. + +- writeback: writes the final types within a function body, replacing + type variables with their final inferred types. These final types + are written into the `tcx.node_types` table, which should *never* contain + any reference to a type variable. + +## Intermediate types + +While type checking a function, the intermediate types for the +expressions, blocks, and so forth contained within the function are +stored in `fcx.node_types` and `fcx.node_substs`. These types +may contain unresolved type variables. After type checking is +complete, the functions in the writeback module are used to take the +types from this table, resolve them, and then write them into their +permanent home in the type context `tcx`. + +This means that during inferencing you should use `fcx.write_ty()` +and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of +nodes within the function. + +The types of top-level items, which never contain unbound type +variables, are stored directly into the `tcx` typeck_results. + +N.B., a type variable is not the same thing as a type parameter. A +type variable is an instance of a type parameter. That is, +given a generic function `fn foo<T>(t: T)`, while checking the +function `foo`, the type `ty_param(0)` refers to the type `T`, which +is treated in abstract. However, when `foo()` is called, `T` will be +substituted for a fresh type variable `N`. This variable will +eventually be resolved to some concrete type (which might itself be +a type parameter). + +*/ + +mod check; +mod compare_method; +pub mod dropck; +pub mod intrinsic; +pub mod intrinsicck; +mod region; +pub mod wfcheck; + +pub use check::check_abi; + +use check::check_mod_item_types; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_index::bit_set::BitSet; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; +use rustc_session::parse::feature_err; +use rustc_span::source_map::DUMMY_SP; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::{self, BytePos, Span, Symbol}; +use rustc_target::abi::VariantIdx; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; +use std::num::NonZeroU32; + +use crate::require_c_abi_if_c_variadic; +use crate::util::common::indenter; + +use self::compare_method::collect_trait_impl_trait_tys; +use self::region::region_scope_tree; + +pub fn provide(providers: &mut Providers) { + wfcheck::provide(providers); + *providers = Providers { + adt_destructor, + check_mod_item_types, + region_scope_tree, + collect_trait_impl_trait_tys, + compare_assoc_const_impl_item_with_trait_item: compare_method::raw_compare_const_impl, + ..*providers + }; +} + +fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> { + tcx.calculate_dtor(def_id, dropck::check_drop_impl) +} + +/// Given a `DefId` for an opaque type in return position, find its parent item's return +/// expressions. +fn get_owner_return_paths<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_id = tcx.hir().get_parent_item(hir_id).def_id; + tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| { + let body = tcx.hir().body(body_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + (parent_id, visitor) + }) +} + +/// Forbid defining intrinsics in Rust code, +/// as they must always be defined by the compiler. +// FIXME: Move this to a more appropriate place. +pub fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { + if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { + tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); + } +} + +fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { + // Only restricted on wasm target for now + if !tcx.sess.target.is_like_wasm { + return; + } + + // If `#[link_section]` is missing, then nothing to verify + let attrs = tcx.codegen_fn_attrs(id); + if attrs.link_section.is_none() { + return; + } + + // For the wasm32 target statics with `#[link_section]` are placed into custom + // sections of the final output file, but this isn't link custom sections of + // other executable formats. Namely we can only embed a list of bytes, + // nothing with provenance (pointers to anything else). If any provenance + // show up, reject it here. + // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is + // 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 + { + let msg = "statics with a custom `#[link_section]` must be a \ + simple list of bytes on the wasm target with no \ + extra levels of indirection such as references"; + tcx.sess.span_err(tcx.def_span(id), msg); + } +} + +fn report_forbidden_specialization( + tcx: TyCtxt<'_>, + impl_item: &hir::ImplItemRef, + parent_impl: DefId, +) { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0520, + "`{}` specializes an item from a parent `impl`, but \ + that item is not marked `default`", + impl_item.ident + ); + err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident)); + + match tcx.span_of_impl(parent_impl) { + Ok(span) => { + err.span_label(span, "parent `impl` is here"); + err.note(&format!( + "to specialize, `{}` in the parent `impl` must be marked `default`", + impl_item.ident + )); + } + Err(cname) => { + err.note(&format!("parent implementation is in crate `{cname}`")); + } + } + + err.emit(); +} + +fn missing_items_err( + tcx: TyCtxt<'_>, + impl_span: Span, + missing_items: &[&ty::AssocItem], + full_impl_span: Span, +) { + let missing_items_msg = missing_items + .iter() + .map(|trait_item| trait_item.name.to_string()) + .collect::<Vec<_>>() + .join("`, `"); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{missing_items_msg}`", + ); + err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation")); + + // `Span` before impl block closing brace. + let hi = full_impl_span.hi() - BytePos(1); + // Point at the place right before the closing brace of the relevant `impl` to suggest + // adding the associated item at the end of its body. + let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); + // Obtain the level of indentation ending in `sugg_sp`. + let padding = + tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); + + for trait_item in missing_items { + let snippet = suggestion_signature(trait_item, tcx); + let code = format!("{}{}\n{}", padding, snippet, padding); + let msg = format!("implement the missing item: `{snippet}`"); + let appl = Applicability::HasPlaceholders; + if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { + err.span_label(span, format!("`{}` from trait", trait_item.name)); + err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); + } else { + err.span_suggestion_hidden(sugg_sp, &msg, code, appl); + } + } + err.emit(); +} + +fn missing_items_must_implement_one_of_err( + tcx: TyCtxt<'_>, + impl_span: Span, + missing_items: &[Ident], + annotation_span: Option<Span>, +) { + let missing_items_msg = + missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `"); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing one of: `{missing_items_msg}`", + ); + err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation")); + + if let Some(annotation_span) = annotation_span { + err.span_note(annotation_span, "required because of this annotation"); + } + + err.emit(); +} + +fn default_body_is_unstable( + tcx: TyCtxt<'_>, + impl_span: Span, + item_did: DefId, + feature: Symbol, + reason: Option<Symbol>, + issue: Option<NonZeroU32>, +) { + let missing_item_name = &tcx.associated_item(item_did).name; + let use_of_unstable_library_feature_note = match reason { + Some(r) => format!("use of unstable library feature '{feature}': {r}"), + None => format!("use of unstable library feature '{feature}'"), + }; + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{missing_item_name}`", + ); + err.note(format!("default implementation of `{missing_item_name}` is unstable")); + err.note(use_of_unstable_library_feature_note); + rustc_session::parse::add_feature_diagnostics_for_issue( + &mut err, + &tcx.sess.parse_sess, + feature, + rustc_feature::GateIssue::Library(issue), + ); + err.emit(); +} + +/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. +fn bounds_from_generic_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: ty::GenericPredicates<'tcx>, +) -> (String, String) { + let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default(); + let mut projections = vec![]; + for (predicate, _) in predicates.predicates { + debug!("predicate {:?}", predicate); + let bound_predicate = predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::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() { + // Type params are `Sized` by default, do not add that restriction to the list + // if it is a positive requirement. + entry.push(trait_predicate.def_id()); + } + } + ty::PredicateKind::Projection(projection_pred) => { + projections.push(bound_predicate.rebind(projection_pred)); + } + _ => {} + } + } + let generics = if types.is_empty() { + "".to_string() + } else { + format!( + "<{}>", + types + .keys() + .filter_map(|t| match t.kind() { + ty::Param(_) => Some(t.to_string()), + // Avoid suggesting the following: + // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {} + _ => None, + }) + .collect::<Vec<_>>() + .join(", ") + ) + }; + let mut where_clauses = vec![]; + for (ty, bounds) in types { + where_clauses + .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound)))); + } + for projection in &projections { + let p = projection.skip_binder(); + // FIXME: this is not currently supported syntax, we should be looking at the `types` and + // insert the associated types where they correspond, but for now let's be "lazy" and + // propose this instead of the following valid resugaring: + // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>` + where_clauses.push(format!( + "{} = {}", + tcx.def_path_str(p.projection_ty.item_def_id), + p.term, + )); + } + let where_clauses = if where_clauses.is_empty() { + String::new() + } else { + format!(" where {}", where_clauses.join(", ")) + }; + (generics, where_clauses) +} + +/// Return placeholder code for the given function. +fn fn_sig_suggestion<'tcx>( + tcx: TyCtxt<'tcx>, + sig: ty::FnSig<'tcx>, + ident: Ident, + predicates: ty::GenericPredicates<'tcx>, + assoc: &ty::AssocItem, +) -> String { + let args = sig + .inputs() + .iter() + .enumerate() + .map(|(i, ty)| { + Some(match ty.kind() { + ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), + ty::Ref(reg, ref_ty, mutability) if i == 0 => { + let reg = format!("{reg} "); + let reg = match ®[..] { + "'_ " | " " => "", + reg => reg, + }; + if assoc.fn_has_self_parameter { + match ref_ty.kind() { + ty::Param(param) if param.name == kw::SelfUpper => { + format!("&{}{}self", reg, mutability.prefix_str()) + } + + _ => format!("self: {ty}"), + } + } else { + format!("_: {ty}") + } + } + _ => { + if assoc.fn_has_self_parameter && i == 0 { + format!("self: {ty}") + } else { + format!("_: {ty}") + } + } + }) + }) + .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) + .flatten() + .collect::<Vec<String>>() + .join(", "); + let output = sig.output(); + let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() }; + + let unsafety = sig.unsafety.prefix_str(); + let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); + + // FIXME: this is not entirely correct, as the lifetimes from borrowed params will + // not be present in the `fn` definition, not will we account for renamed + // lifetimes between the `impl` and the `trait`, but this should be good enough to + // fill in a significant portion of the missing code, and other subsequent + // suggestions can help the user fix the code. + format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}") +} + +pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { + Some(match ty.kind() { + ty::Bool => "true", + ty::Char => "'a'", + ty::Int(_) | ty::Uint(_) => "42", + ty::Float(_) => "3.14159", + ty::Error(_) | ty::Never => return None, + _ => "value", + }) +} + +/// Return placeholder code for the given associated item. +/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a +/// structured suggestion. +fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { + match assoc.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + fn_sig_suggestion( + tcx, + tcx.fn_sig(assoc.def_id).skip_binder(), + assoc.ident(tcx), + tcx.predicates_of(assoc.def_id), + assoc, + ) + } + ty::AssocKind::Type => format!("type {} = Type;", assoc.name), + ty::AssocKind::Const => { + let ty = tcx.type_of(assoc.def_id); + let val = ty_kind_suggestion(ty).unwrap_or("value"); + format!("const {}: {} = {};", assoc.name, ty, val) + } + } +} + +/// Emit an error when encountering two or more variants in a transparent enum. +fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) { + let variant_spans: Vec<_> = adt + .variants() + .iter() + .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) + .collect(); + let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),); + let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}"); + err.span_label(sp, &msg); + if let [start @ .., end] = &*variant_spans { + for variant_span in start { + err.span_label(*variant_span, ""); + } + err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did))); + } + err.emit(); +} + +/// Emit an error when encountering two or more non-zero-sized fields in a transparent +/// enum. +fn bad_non_zero_sized_fields<'tcx>( + tcx: TyCtxt<'tcx>, + adt: ty::AdtDef<'tcx>, + field_count: usize, + field_spans: impl Iterator<Item = Span>, + sp: Span, +) { + let msg = format!("needs at most one non-zero-sized field, but has {field_count}"); + let mut err = struct_span_err!( + tcx.sess, + sp, + E0690, + "{}transparent {} {}", + if adt.is_enum() { "the variant of a " } else { "" }, + adt.descr(), + msg, + ); + err.span_label(sp, &msg); + for sp in field_spans { + err.span_label(sp, "this field is non-zero-sized"); + } + err.emit(); +} + +// FIXME: Consider moving this method to a more fitting place. +pub fn potentially_plural_count(count: usize, word: &str) -> String { + format!("{} {}{}", count, word, pluralize!(count)) +} diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 0081e9049..ff32329e4 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { + hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => { + // Let-else has a special lexical structure for variables. + // First we take a checkpoint of the current scope context here. + let mut prev_cx = visitor.cx; + + visitor.enter_scope(Scope { + id: blk.hir_id.local_id, + data: ScopeData::Remainder(FirstStatementIndex::new(i)), + }); + visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement); + // We need to back out temporarily to the last enclosing scope + // for the `else` block, so that even the temporaries receiving + // extended lifetime will be dropped inside this block. + // We are visiting the `else` block in this order so that + // the sequence of visits agree with the order in the default + // `hir::intravisit` visitor. + mem::swap(&mut prev_cx, &mut visitor.cx); + visitor.terminating_scopes.insert(els.hir_id.local_id); + visitor.visit_block(els); + // From now on, we continue normally. + visitor.cx = prev_cx; + } hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => { // Each declaration introduces a subscope for bindings // introduced by the declaration; this subscope covers a @@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement) } - hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } - visitor.visit_stmt(statement) } walk_list!(visitor, visit_expr, &blk.expr); } @@ -229,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h ) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. - terminating(r.hir_id.local_id); - } + // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries + // should live beyond the immediate expression + if !matches!(r.kind, hir::ExprKind::Let(_)) { + terminating(r.hir_id.local_id); + } + } hir::ExprKind::If(_, ref then, Some(ref otherwise)) => { terminating(then.hir_id.local_id); terminating(otherwise.hir_id.local_id); @@ -460,7 +487,6 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, - els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -547,9 +573,6 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { visitor.visit_pat(pat); } - if let Some(els) = els { - visitor.visit_block(els); - } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -587,8 +610,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingAnnotation::Ref, ..) - | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true, + PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true, PatKind::Struct(_, ref field_pats, _) => { field_pats.iter().any(|fp| is_binding_pat(&fp.pat)) @@ -607,10 +629,7 @@ fn resolve_local<'tcx>( PatKind::Box(ref subpat) => is_binding_pat(&subpat), PatKind::Ref(_, _) - | PatKind::Binding( - hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, - .., - ) + | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Path(_) | PatKind::Lit(_) @@ -770,7 +789,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value), None); + resolve_local(self, None, Some(&body.value)); } if body.generator_kind.is_some() { @@ -797,7 +816,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init, l.els) + resolve_local(self, Some(&l.pat), l.init) } } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index d0334cd0d..a23575004 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1,5 +1,5 @@ -use crate::check::regionck::OutlivesEnvironmentExt; 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_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; @@ -10,22 +10,21 @@ use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::Normalized; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, 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_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::query::normalize::AtExt; -use rustc_trait_selection::traits::query::NoSolution; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; @@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) { let cause = traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)); + // 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( cause, - self.param_env, + param_env, ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), )); } @@ -86,31 +87,35 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( body_def_id: LocalDefId, f: F, ) where - F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>, + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>), { let param_env = tcx.param_env(body_def_id); let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id); - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); - let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); - if !tcx.features().trivial_bounds { - wfcx.check_false_global_bounds() - } - let wf_tys = f(&mut wfcx); - let errors = wfcx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; - } + let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, body_id); - infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); - }) + let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + + if !tcx.features().trivial_bounds { + wfcx.check_false_global_bounds() + } + f(&mut wfcx); + let errors = wfcx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return; + } + + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); } -fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { let node = tcx.hir().expect_owner(def_id); match node { hir::OwnerNode::Crate(_) => {} @@ -142,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// the types first. #[instrument(skip(tcx), level = "debug")] fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); @@ -169,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // for `T` hir::ItemKind::Impl(ref impl_) => { let is_auto = tcx - .impl_trait_ref(item.def_id) + .impl_trait_ref(def_id) .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id)); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); @@ -205,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } } hir::ItemKind::Fn(ref sig, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl); + check_item_fn(tcx, def_id, item.ident, item.span, sig.decl); } hir::ItemKind::Static(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + check_item_type(tcx, def_id, ty.span, false); } hir::ItemKind::Const(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + 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)]); @@ -241,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); match item.kind { hir::ForeignItemKind::Fn(decl, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, decl) + check_item_fn(tcx, def_id, item.ident, item.span, decl) } - hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true), + hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true), hir::ForeignItemKind::Type => (), } } fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { - let def_id = trait_item.def_id; + let def_id = trait_item.owner_id.def_id; let (method_sig, span) = match trait_item.kind { hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span), @@ -266,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { _ => (None, trait_item.span), }; check_object_unsafe_self_trait_by_name(tcx, trait_item); - check_associated_item(tcx, trait_item.def_id, span, method_sig); + check_associated_item(tcx, def_id, span, method_sig); let encl_trait_def_id = tcx.local_parent(def_id); let encl_trait = tcx.hir().expect_item(encl_trait_def_id); - let encl_trait_def_id = encl_trait.def_id.to_def_id(); + let encl_trait_def_id = encl_trait.owner_id.to_def_id(); let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { Some("fn") } else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() { @@ -343,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe loop { let mut should_continue = false; for gat_item in associated_items { - let gat_def_id = gat_item.id.def_id; + let gat_def_id = gat_item.id.owner_id; let gat_item = tcx.associated_item(gat_def_id); // If this item is not an assoc ty, or has no substs, then it's not a GAT if gat_item.kind != ty::AssocKind::Type { @@ -360,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe // constrains the GAT with individually. let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None; for item in associated_items { - let item_def_id = item.id.def_id; + let item_def_id = item.id.owner_id; // Skip our own GAT, since it does not constrain itself at all. if item_def_id == gat_def_id { continue; @@ -383,11 +388,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe tcx, param_env, item_hir_id, - sig.output(), + sig.inputs_and_output, // We also assume that all of the function signature's parameter types // are well formed. &sig.inputs().iter().copied().collect(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -410,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe .copied() .collect::<Vec<_>>(), &FxHashSet::default(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -450,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe } for (gat_def_id, required_bounds) in required_bounds_by_item { - let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); + let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id); debug!(?required_bounds); let param_env = tcx.param_env(gat_def_id); let gat_hir = gat_item_hir.hir_id(); @@ -658,7 +663,7 @@ fn ty_known_to_outlive<'tcx>( resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| { let origin = infer::RelateParamBound(DUMMY_SP, ty, None); let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env); - outlives.type_must_outlive(origin, ty, region); + outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation); }) } @@ -676,7 +681,12 @@ fn region_known_to_outlive<'tcx>( use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; let origin = infer::RelateRegionParamBound(DUMMY_SP); // `region_a: region_b` -> `region_b <= region_a` - infcx.push_sub_region_constraint(origin, region_b, region_a); + infcx.push_sub_region_constraint( + origin, + region_b, + region_a, + ConstraintCategory::BoringNoLocation, + ); }) } @@ -688,26 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>( id: hir::HirId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxHashSet<Ty<'tcx>>, - add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'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 // region constraints get added and solved there and we need to test each // call individually. - tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id); - let region_bound_pairs = outlives_environment.region_bound_pairs(); + let infcx = tcx.infer_ctxt().build(); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + ); + let region_bound_pairs = outlives_environment.region_bound_pairs(); - add_constraints(&infcx, region_bound_pairs); + add_constraints(&infcx, region_bound_pairs); - let errors = infcx.resolve_regions(&outlives_environment); + infcx.process_registered_region_obligations( + outlives_environment.region_bound_pairs(), + param_env, + ); + let errors = infcx.resolve_regions(&outlives_environment); - debug!(?errors, "errors"); + debug!(?errors, "errors"); - // If we were able to prove that the type outlives the region without - // an error, it must be because of the implied or explicit bounds... - errors.is_empty() - }) + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() } /// TypeVisitor that looks for uses of GATs like @@ -761,7 +777,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { - [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()), + [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, _ => false, @@ -772,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { /// When this is done, suggest using `Self` instead. fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) { let (trait_name, trait_def_id) = - match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) { + match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.def_id), + hir::ItemKind::Trait(..) => (item.ident, item.owner_id), _ => return, }, _ => return, @@ -782,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem let mut trait_should_be_self = vec![]; match &item.kind { hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty)) - if could_be_self(trait_def_id, ty) => + if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span) } hir::TraitItemKind::Fn(sig, _) => { for ty in sig.decl.inputs { - if could_be_self(trait_def_id, ty) { + if could_be_self(trait_def_id.def_id, ty) { trait_should_be_self.push(ty.span); } } match sig.decl.output { - hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => { + hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span); } _ => {} @@ -822,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { - let def_id = impl_item.def_id; - let (method_sig, span) = match impl_item.kind { hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span), // Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`. - hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span), + hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span), _ => (None, impl_item.span), }; - check_associated_item(tcx, def_id, span, method_sig); + check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig); } fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { @@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } } -#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))] +#[instrument(level = "debug", skip(tcx, span, sig_if_method))] fn check_associated_item( tcx: TyCtxt<'_>, item_id: LocalDefId, @@ -976,15 +990,9 @@ fn check_associated_item( enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { let item = tcx.associated_item(item_id); - let (mut implied_bounds, self_ty) = match item.container { - ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param), - ty::ImplContainer => { - let def_id = item.container_id(tcx); - ( - impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span), - tcx.type_of(def_id), - ) - } + let self_ty = match item.container { + ty::TraitContainer => tcx.types.self_param, + ty::ImplContainer => tcx.type_of(item.container_id(tcx)), }; match item.kind { @@ -1002,7 +1010,6 @@ fn check_associated_item( sig, hir_sig.decl, item.def_id.expect_local(), - &mut implied_bounds, ); check_method_receiver(wfcx, hir_sig, item, self_ty); } @@ -1017,8 +1024,6 @@ fn check_associated_item( } } } - - implied_bounds }) } @@ -1040,9 +1045,11 @@ fn check_type_defn<'tcx, F>( ) where F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + let _ = tcx.representability(item.owner_id.def_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.def_id).repr().packed(); + let packed = tcx.adt_def(item.owner_id).repr().packed(); for variant in &variants { // All field types must be well-formed. @@ -1066,7 +1073,7 @@ fn check_type_defn<'tcx, F>( // Just treat unresolved type expression as if it needs drop. true } else { - ty.needs_drop(tcx, tcx.param_env(item.def_id)) + ty.needs_drop(tcx, tcx.param_env(item.owner_id)) } } }; @@ -1098,8 +1105,6 @@ fn check_type_defn<'tcx, F>( // Explicit `enum` discriminant values must const-evaluate successfully. if let Some(discr_def_id) = variant.explicit_discr { - let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id()); - let cause = traits::ObligationCause::new( tcx.def_span(discr_def_id), wfcx.body_id, @@ -1108,31 +1113,28 @@ fn check_type_defn<'tcx, F>( wfcx.register_obligation(traits::Obligation::new( cause, wfcx.param_env, - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new( - ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), - discr_substs, - ))) + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable( + ty::Const::from_anon_const(tcx, discr_def_id), + )) .to_predicate(tcx), )); } } - check_where_clauses(wfcx, item.span, item.def_id); - - // No implied bounds in a struct definition. - FxHashSet::default() + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } #[instrument(skip(tcx, item))] fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - debug!(?item.def_id); + debug!(?item.owner_id); - let trait_def = tcx.trait_def(item.def_id); + let def_id = item.owner_id.def_id; + let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) { - for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) { + for associated_def_id in &*tcx.associated_item_def_ids(def_id) { struct_span_err!( tcx.sess, tcx.def_span(*associated_def_id), @@ -1143,10 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } } - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - check_where_clauses(wfcx, item.span, item.def_id); - - FxHashSet::default() + enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + check_where_clauses(wfcx, item.span, def_id) }); // Only check traits, don't check trait aliases @@ -1186,9 +1186,7 @@ fn check_item_fn( ) { enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { let sig = tcx.fn_sig(def_id); - let mut implied_bounds = FxHashSet::default(); - check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds); - implied_bounds + check_fn_or_method(wfcx, ident.span, sig, decl, def_id); }) } @@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo tcx.require_lang_item(LangItem::Sync, Some(ty_span)), ); } - - // No implied bounds in a const, etc. - FxHashSet::default() }); } -#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] +#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, @@ -1245,13 +1240,13 @@ fn check_impl<'tcx>( ast_trait_ref: &Option<hir::TraitRef<'_>>, constness: hir::Constness, ) { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { match *ast_trait_ref { Some(ref ast_trait_ref) => { // `#[rustc_reservation_impl]` impls are not real impls and // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). - let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap(); + let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap(); let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); let trait_pred = ty::TraitPredicate { trait_ref, @@ -1273,19 +1268,21 @@ fn check_impl<'tcx>( wfcx.register_obligations(obligations); } None => { - let self_ty = tcx.type_of(item.def_id); - let self_ty = wfcx.normalize(item.span, None, self_ty); + let self_ty = tcx.type_of(item.owner_id); + let self_ty = wfcx.normalize( + item.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), + self_ty, + ); wfcx.register_wf_obligation( ast_self_ty.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty.into(), ); } } - check_where_clauses(wfcx, item.span, item.def_id); - - impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span) + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } @@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { - wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into()); + wfcx.register_wf_obligation( + tcx.def_span(param.def_id), + Some(WellFormedLoc::Ty(param.def_id.expect_local())), + ty.into(), + ); } } } @@ -1426,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id let substituted_pred = predicates.rebind(pred).subst(tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. - if substituted_pred.has_param_types_or_consts() - || param_count.params.len() > 1 - || has_region + if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region { None } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) { @@ -1465,21 +1464,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| { - traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp) + traits::wf::predicate_obligations( + infcx, + wfcx.param_env.without_const(), + wfcx.body_id, + p, + sp, + ) }); let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); wfcx.register_obligations(obligations); } -#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))] +#[instrument(level = "debug", skip(wfcx, span, hir_decl))] fn check_fn_or_method<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, sig: ty::PolyFnSig<'tcx>, hir_decl: &hir::FnDecl<'_>, def_id: LocalDefId, - implied_bounds: &mut FxHashSet<Ty<'tcx>>, ) { let tcx = wfcx.tcx(); let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); @@ -1521,23 +1525,66 @@ fn check_fn_or_method<'tcx>( ); } - implied_bounds.extend(sig.inputs()); - - wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into()); + wfcx.register_wf_obligation( + hir_decl.output.span(), + Some(WellFormedLoc::Param { + function: def_id, + param_idx: sig.inputs().len().try_into().unwrap(), + }), + sig.output().into(), + ); - // FIXME(#27579) return types should not be implied bounds - implied_bounds.insert(sig.output()); + check_where_clauses(wfcx, span, def_id); - debug!(?implied_bounds); + check_return_position_impl_trait_in_trait_bounds( + tcx, + wfcx, + def_id, + sig.output(), + hir_decl.output.span(), + ); +} - check_where_clauses(wfcx, span, def_id); +/// Basically `check_associated_type_bounds`, but separated for now and should be +/// deduplicated when RPITITs get lowered into real associated items. +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, +) { + if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) + && assoc_item.container == ty::AssocItemContainer::TraitContainer + { + for arg in fn_output.walk() { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(proj) = ty.kind() + && 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 bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id); + let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let normalized_bound = wfcx.normalize(span, None, bound); + traits::wf::predicate_obligations( + wfcx.infcx, + wfcx.param_env, + wfcx.body_id, + normalized_bound, + bound_span, + ) + }); + wfcx.register_obligations(wf_obligations); + } + } + } } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \ `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \ of the previous types except `Self`)"; -#[tracing::instrument(level = "debug", skip(wfcx))] +#[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_sig: &hir::FnSig<'_>, @@ -1629,7 +1676,7 @@ 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) { - infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); + infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); } return true; } @@ -1661,7 +1708,10 @@ fn receiver_is_valid<'tcx>( if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty) { - infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit(); + infcx + .err_ctxt() + .report_mismatched_types(&cause, self_ty, potential_self_ty, err) + .emit(); } break; @@ -1728,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>( item: &hir::Item<'tcx>, hir_generics: &hir::Generics<'_>, ) { - let ty = tcx.type_of(item.def_id); + let ty = tcx.type_of(item.owner_id); if tcx.has_error_field(ty) { return; } - let ty_predicates = tcx.predicates_of(item.def_id); + let ty_predicates = tcx.predicates_of(item.owner_id); assert_eq!(ty_predicates.parent, None); - let variances = tcx.variances_of(item.def_id); + let variances = tcx.variances_of(item.owner_id); let mut constrained_parameters: FxHashSet<_> = variances .iter() @@ -1748,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>( // Lazily calculated because it is only needed in case of an error. let explicitly_bounded_params = LazyCell::new(|| { - let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id()); + let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id()); hir_generics .predicates .iter() @@ -1817,6 +1867,7 @@ fn report_bivariance( impl<'tcx> WfCheckingCtxt<'_, 'tcx> { /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that /// aren't true. + #[instrument(level = "debug", skip(self))] fn check_false_global_bounds(&mut self) { let tcx = self.ocx.infcx.tcx; let mut span = self.span; @@ -1868,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { let items = tcx.hir_module_items(module); - items.par_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)); } /////////////////////////////////////////////////////////////////////////// @@ -1924,40 +1975,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { } } -pub fn impl_implied_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: LocalDefId, - span: Span, -) -> FxHashSet<Ty<'tcx>> { - // We completely ignore any obligations caused by normalizing the types - // we assume to be well formed. Considering that the user of the implied - // bounds will also normalize them, we leave it to them to emit errors - // which should result in better causes and spans. - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id)); - match tcx.impl_trait_ref(impl_def_id) { - Some(trait_ref) => { - // Trait impl: take implied bounds from all types that - // appear in the trait reference. - match infcx.at(&cause, param_env).normalize(trait_ref) { - Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(), - Err(NoSolution) => FxHashSet::default(), - } - } - - None => { - // Inherent impl: take implied bounds from the `self` type. - let self_ty = tcx.type_of(impl_def_id); - match infcx.at(&cause, param_env).normalize(self_ty) { - Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]), - Err(NoSolution) => FxHashSet::default(), - } - } - } - }) -} - 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 new file mode 100644 index 000000000..d0c317334 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -0,0 +1,192 @@ +use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::unord::UnordSet; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint; +use rustc_span::{Span, Symbol}; + +pub fn check_crate(tcx: TyCtxt<'_>) { + let mut used_trait_imports: UnordSet<LocalDefId> = Default::default(); + + for item_def_id in tcx.hir().body_owners() { + let imports = tcx.used_trait_imports(item_def_id); + debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); + used_trait_imports.extend(imports.items().copied()); + } + + for &id in tcx.maybe_unused_trait_imports(()) { + debug_assert_eq!(tcx.def_kind(id), DefKind::Use); + if tcx.visibility(id).is_public() { + continue; + } + if used_trait_imports.contains(&id) { + continue; + } + let item = tcx.hir().expect_item(id); + if item.span.is_dummy() { + continue; + } + let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() }; + let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { + format!("unused import: `{}`", snippet) + } else { + "unused import".to_owned() + }; + tcx.struct_span_lint_hir( + lint::builtin::UNUSED_IMPORTS, + item.hir_id(), + path.span, + msg, + |lint| lint, + ); + } + + unused_crates_lint(tcx); +} + +fn unused_crates_lint(tcx: TyCtxt<'_>) { + let lint = lint::builtin::UNUSED_EXTERN_CRATES; + + // Collect first the crates that are completely unused. These we + // can always suggest removing (no matter which edition we are + // in). + let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx + .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) + && !tcx.has_global_allocator(cnum) + && !tcx.has_panic_handler(cnum) + }) + }) + .cloned() + .collect(); + + // Collect all the extern crates (in a reliable order). + let mut crates_to_lint = vec![]; + + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) { + let item = tcx.hir().item(id); + if let hir::ItemKind::ExternCrate(orig_name) = item.kind { + crates_to_lint.push(ExternCrateToLint { + def_id: item.owner_id.to_def_id(), + span: item.span, + orig_name, + warn_if_unused: !item.ident.as_str().starts_with('_'), + }); + } + } + } + + let extern_prelude = &tcx.resolutions(()).extern_prelude; + + for extern_crate in &crates_to_lint { + let def_id = extern_crate.def_id.expect_local(); + let item = tcx.hir().expect_item(def_id); + + // If the crate is fully unused, we suggest removing it altogether. + // We do this in any edition. + if extern_crate.warn_if_unused { + if let Some(&span) = unused_extern_crates.get(&def_id) { + // Removal suggestion span needs to include attributes (Issue #54400) + let id = tcx.hir().local_def_id_to_hir_id(def_id); + let span_with_attrs = tcx + .hir() + .attrs(id) + .iter() + .map(|attr| attr.span) + .fold(span, |acc, attr_span| acc.to(attr_span)); + + tcx.emit_spanned_lint(lint, id, span, UnusedExternCrate { span: span_with_attrs }); + continue; + } + } + + // If we are not in Rust 2018 edition, then we don't make any further + // suggestions. + if !tcx.sess.rust_2018() { + continue; + } + + // If the extern crate isn't in the extern prelude, + // there is no way it can be written as a `use`. + let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); + if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { + continue; + } + + // If the extern crate is renamed, then we cannot suggest replacing it with a use as this + // would not insert the new name into the prelude, where other imports in the crate may be + // expecting it. + if extern_crate.orig_name.is_some() { + continue; + } + + let id = tcx.hir().local_def_id_to_hir_id(def_id); + // If the extern crate has any attributes, they may have funky + // semantics we can't faithfully represent using `use` (most + // notably `#[macro_use]`). Ignore it. + if !tcx.hir().attrs(id).is_empty() { + continue; + } + + let base_replacement = match extern_crate.orig_name { + Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), + None => format!("use {};", item.ident.name), + }; + let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default(); + let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; + tcx.emit_spanned_lint( + lint, + id, + extern_crate.span, + ExternCrateNotIdiomatic { + span: extern_crate.span, + msg_code: add_vis("use".to_string()), + suggestion_code: add_vis(base_replacement), + }, + ); + } +} + +struct ExternCrateToLint { + /// `DefId` of the extern crate + def_id: DefId, + + /// span from the item + span: Span, + + /// if `Some`, then this is renamed (`extern crate orig_name as + /// crate_name`), and -- perhaps surprisingly -- this stores the + /// *original* name (`item.name` will contain the new name) + orig_name: Option<Symbol>, + + /// if `false`, the original name started with `_`, so we shouldn't lint + /// about it going unused (but we should still emit idiom lints). + warn_if_unused: bool, +} diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs new file mode 100644 index 000000000..b6c91d425 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -0,0 +1,572 @@ +//! Check properties that are required by built-in traits and set +//! up data structures required by type-checking/codegen. + +use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; +use rustc_errors::{struct_span_err, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::ItemKind; +use rustc_infer::infer; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable}; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; +use rustc_trait_selection::traits::predicate_for_trait_def; +use rustc_trait_selection::traits::{self, ObligationCause}; +use std::collections::BTreeMap; + +pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { + let lang_items = tcx.lang_items(); + Checker { tcx, trait_def_id } + .check(lang_items.drop_trait(), visit_implementation_of_drop) + .check(lang_items.copy_trait(), visit_implementation_of_copy) + .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) + .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); +} + +struct Checker<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, +} + +impl<'tcx> Checker<'tcx> { + fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self + where + F: FnMut(TyCtxt<'tcx>, LocalDefId), + { + if Some(self.trait_def_id) == trait_def_id { + for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { + f(self.tcx, impl_def_id); + } + } + self + } +} + +fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + // Destructors only work on local ADT types. + match tcx.type_of(impl_did).kind() { + ty::Adt(def, _) if def.did().is_local() => return, + ty::Error(_) => return, + _ => {} + } + + let sp = match tcx.hir().expect_item(impl_did).kind { + ItemKind::Impl(ref impl_) => impl_.self_ty.span, + _ => bug!("expected Drop impl item"), + }; + + tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); +} + +fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { + debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); + + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + + let self_type = tcx.type_of(impl_did); + debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); + + let param_env = tcx.param_env(impl_did); + assert!(!self_type.has_escaping_bound_vars()); + + debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); + + let span = match tcx.hir().expect_item(impl_did).kind { + ItemKind::Impl(hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. }) => return, + ItemKind::Impl(impl_) => impl_.self_ty.span, + _ => bug!("expected Copy impl item"), + }; + + let cause = traits::ObligationCause::misc(span, impl_hir_id); + match can_type_implement_copy(tcx, param_env, self_type, cause) { + Ok(()) => {} + Err(CopyImplementationError::InfrigingFields(fields)) => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0204, + "the trait `Copy` may not be implemented for this type" + ); + + // We'll try to suggest constraining type parameters to fulfill the requirements of + // their `Copy` implementation. + let mut errors: BTreeMap<_, Vec<_>> = Default::default(); + let mut bounds = vec![]; + + for (field, ty) in fields { + let field_span = tcx.def_span(field.did); + let field_ty_span = match tcx.hir().get_if_local(field.did) { + Some(hir::Node::Field(field_def)) => field_def.ty.span, + _ => field_span, + }; + err.span_label(field_span, "this field does not implement `Copy`"); + // Spin up a new FulfillmentContext, so we can get the _precise_ reason + // why this field does not implement Copy. This is useful because sometimes + // it is not immediately clear why Copy is not implemented for a field, since + // all we point at is the field itself. + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + for error in traits::fully_solve_bound( + &infcx, + traits::ObligationCause::dummy_with_span(field_ty_span), + param_env, + ty, + tcx.lang_items().copy_trait().unwrap(), + ) { + let error_predicate = error.obligation.predicate; + // Only note if it's not the root obligation, otherwise it's trivial and + // should be self-explanatory (i.e. a field literally doesn't implement Copy). + + // FIXME: This error could be more descriptive, especially if the error_predicate + // contains a foreign type or if it's a deeply nested type... + if error_predicate != error.root_obligation.predicate { + errors + .entry((ty.to_string(), error_predicate.to_string())) + .or_default() + .push(error.obligation.cause.span); + } + if let ty::PredicateKind::Trait(ty::TraitPredicate { + trait_ref, + polarity: ty::ImplPolarity::Positive, + .. + }) = error_predicate.kind().skip_binder() + { + let ty = trait_ref.self_ty(); + if let ty::Param(_) = ty.kind() { + bounds.push(( + format!("{ty}"), + trait_ref.print_only_trait_path().to_string(), + Some(trait_ref.def_id), + )); + } + } + } + } + for ((ty, error_predicate), spans) in errors { + let span: MultiSpan = spans.into(); + err.span_note( + span, + &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate), + ); + } + suggest_constraining_type_params( + tcx, + tcx.hir().get_generics(impl_did).expect("impls always have generics"), + &mut err, + bounds.iter().map(|(param, constraint, def_id)| { + (param.as_str(), constraint.as_str(), *def_id) + }), + ); + err.emit(); + } + Err(CopyImplementationError::NotAnAdt) => { + tcx.sess.emit_err(CopyImplOnNonAdt { span }); + } + Err(CopyImplementationError::HasDestructor) => { + tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); + } + } +} + +fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { + debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); + + // Just compute this for the side-effects, in particular reporting + // errors; other parts of the code may demand it for the info of + // course. + let span = tcx.def_span(impl_did); + tcx.at(span).coerce_unsized_info(impl_did); +} + +fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { + debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); + + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + let span = tcx.hir().span(impl_hir_id); + + let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); + + let source = tcx.type_of(impl_did); + assert!(!source.has_escaping_bound_vars()); + let target = { + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); + + trait_ref.substs.type_at(1) + }; + + debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); + + let param_env = tcx.param_env(impl_did); + + let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); + + let infcx = tcx.infer_ctxt().build(); + let cause = ObligationCause::misc(span, impl_hir_id); + + use rustc_type_ir::sty::TyKind::*; + match (source.kind(), target.kind()) { + (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) + if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {} + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did()); + let target_path = tcx.def_path_str(def_b.did()); + + create_err(&format!( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with the same \ + definition; expected `{}`, found `{}`", + source_path, target_path, + )) + .emit(); + + return; + } + + if def_a.repr().c() || def_a.repr().packed() { + create_err( + "structs implementing `DispatchFromDyn` may not have \ + `#[repr(packed)]` or `#[repr(C)]`", + ) + .emit(); + } + + let fields = &def_a.non_enum_variant().fields; + + let coerced_fields = fields + .iter() + .filter(|field| { + let ty_a = field.ty(tcx, substs_a); + let ty_b = field.ty(tcx, substs_b); + + if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { + if layout.is_zst() && layout.align.abi.bytes() == 1 { + // ignore ZST fields with alignment of 1 byte + return false; + } + } + + if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { + if ok.obligations.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for structs containing the field being coerced, \ + ZST fields with 1 byte alignment, and nothing else", + ) + .note(&format!( + "extra field `{}` of type `{}` is not allowed", + field.name, ty_a, + )) + .emit(); + + return false; + } + } + + return true; + }) + .collect::<Vec<_>>(); + + if coerced_fields.is_empty() { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with a single field \ + being coerced, none found", + ) + .emit(); + } else if coerced_fields.len() > 1 { + create_err("implementing the `DispatchFromDyn` trait requires multiple coercions") + .note( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures with a single field \ + being coerced", + ) + .note(&format!( + "currently, {} fields need coercions: {}", + coerced_fields.len(), + coerced_fields + .iter() + .map(|field| { + format!( + "`{}` (`{}` to `{}`)", + field.name, + field.ty(tcx, substs_a), + field.ty(tcx, substs_b), + ) + }) + .collect::<Vec<_>>() + .join(", ") + )) + .emit(); + } else { + let errors = traits::fully_solve_obligations( + &infcx, + coerced_fields.into_iter().map(|field| { + predicate_for_trait_def( + tcx, + param_env, + cause.clone(), + dispatch_from_dyn_trait, + 0, + field.ty(tcx, substs_a), + &[field.ty(tcx, substs_b).into()], + ) + }), + ); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + + // Finally, resolve all regions. + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); + } + } + _ => { + create_err( + "the trait `DispatchFromDyn` may only be implemented \ + for a coercion between structures", + ) + .emit(); + } + } +} + +pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { + debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); + + // this provider should only get invoked for local def-ids + let impl_did = impl_did.expect_local(); + let span = tcx.def_span(impl_did); + + let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); + + let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| { + tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string())); + }); + + let source = tcx.type_of(impl_did); + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); + assert_eq!(trait_ref.def_id, coerce_unsized_trait); + let target = trait_ref.substs.type_at(1); + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); + + let param_env = tcx.param_env(impl_did); + assert!(!source.has_escaping_bound_vars()); + + let err_info = CoerceUnsizedInfo { custom_kind: None }; + + debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); + + let infcx = tcx.infer_ctxt().build(); + let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); + let cause = ObligationCause::misc(span, impl_hir_id); + 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) { + infcx + .err_ctxt() + .report_mismatched_types( + &cause, + mk_ptr(mt_b.ty), + target, + ty::error::TypeError::Mutability, + ) + .emit(); + } + (mt_a.ty, mt_b.ty, unsize_trait, None) + }; + let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { + (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { + infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) + } + + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) + } + + (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)), + + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did()); + let target_path = tcx.def_path_str(def_b.did()); + struct_span_err!( + tcx.sess, + span, + E0377, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with the same \ + definition; expected `{}`, found `{}`", + source_path, + target_path + ) + .emit(); + return err_info; + } + + // Here we are considering a case of converting + // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, + // which acts like a pointer to `U`, but carries along some extra data of type `T`: + // + // struct Foo<T, U> { + // extra: T, + // ptr: *mut U, + // } + // + // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized + // to `Foo<T, [i32]>`. That impl would look like: + // + // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {} + // + // Here `U = [i32; 3]` and `V = [i32]`. At runtime, + // when this coercion occurs, we would be changing the + // field `ptr` from a thin pointer of type `*mut [i32; + // 3]` to a fat pointer of type `*mut [i32]` (with + // extra data `3`). **The purpose of this check is to + // make sure that we know how to do this conversion.** + // + // To check if this impl is legal, we would walk down + // the fields of `Foo` and consider their types with + // both substitutes. We are looking to find that + // exactly one (non-phantom) field has changed its + // type, which we will expect to be the pointer that + // is becoming fat (we could probably generalize this + // to multiple thin pointers of the same type becoming + // fat, but we don't). In this case: + // + // - `extra` has type `T` before and type `T` after + // - `ptr` has type `*mut U` before and type `*mut V` after + // + // Since just one field changed, we would then check + // that `*mut U: CoerceUnsized<*mut V>` is implemented + // (in other words, that we know how to do this + // conversion). This will work out because `U: + // Unsize<V>`, and we have a builtin rule that `*mut + // U` can be coerced to `*mut V` if `U: Unsize<V>`. + let fields = &def_a.non_enum_variant().fields; + let diff_fields = fields + .iter() + .enumerate() + .filter_map(|(i, f)| { + let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); + + if tcx.type_of(f.did).is_phantom_data() { + // Ignore PhantomData fields + return None; + } + + // Ignore fields that aren't changed; it may + // be that we could get away with subtyping or + // something more accepting, but we use + // equality because we want to be able to + // perform this check without computing + // variance where possible. (This is because + // we may have to evaluate constraint + // expressions in the course of execution.) + // See e.g., #41936. + if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) { + if ok.obligations.is_empty() { + return None; + } + } + + // Collect up all fields that were significantly changed + // i.e., those that contain T in coerce_unsized T -> U + Some((i, a, b)) + }) + .collect::<Vec<_>>(); + + if diff_fields.is_empty() { + struct_span_err!( + tcx.sess, + span, + E0374, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures with one field \ + being coerced, none found" + ) + .emit(); + return err_info; + } else if diff_fields.len() > 1 { + let item = tcx.hir().expect_item(impl_did); + let span = + if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = item.kind { + t.path.span + } else { + tcx.def_span(impl_did) + }; + + struct_span_err!( + tcx.sess, + span, + E0375, + "implementing the trait \ + `CoerceUnsized` requires multiple \ + coercions" + ) + .note( + "`CoerceUnsized` may only be implemented for \ + a coercion between structures with one field being coerced", + ) + .note(&format!( + "currently, {} fields need coercions: {}", + diff_fields.len(), + diff_fields + .iter() + .map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) }) + .collect::<Vec<_>>() + .join(", ") + )) + .span_label(span, "requires multiple coercions") + .emit(); + return err_info; + } + + let (i, a, b) = diff_fields[0]; + let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); + (a, b, coerce_unsized_trait, Some(kind)) + } + + _ => { + struct_span_err!( + tcx.sess, + span, + E0376, + "the trait `CoerceUnsized` may only be implemented \ + for a coercion between structures" + ) + .emit(); + return err_info; + } + }; + + // 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()]); + let errors = traits::fully_solve_obligation(&infcx, predicate); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + + // Finally, resolve all regions. + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); + + CoerceUnsizedInfo { custom_kind: kind } +} diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 52aad636f..2890c149b 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -58,7 +58,7 @@ const ADD_ATTR: &str = impl<'tcx> InherentCollect<'tcx> { fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) { - let impl_def_id = item.def_id; + let impl_def_id = item.owner_id; if let Some(def_id) = def_id.as_local() { // Add the implementation to the mapping from implementation to base // type def ID, if there is a base type for this implementation and @@ -89,7 +89,7 @@ impl<'tcx> InherentCollect<'tcx> { for impl_item in items { if !self .tcx - .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(impl_item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -105,7 +105,7 @@ impl<'tcx> InherentCollect<'tcx> { } if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) { - self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); + self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id); } else { bug!("unexpected self type: {:?}", self_ty); } @@ -135,7 +135,7 @@ impl<'tcx> InherentCollect<'tcx> { for item in items { if !self .tcx - .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -177,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> { } fn check_item(&mut self, id: hir::ItemId) { - if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { + if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl) { return; } @@ -186,7 +186,7 @@ impl<'tcx> InherentCollect<'tcx> { return; }; - let self_ty = self.tcx.type_of(item.def_id); + let self_ty = self.tcx.type_of(item.owner_id); match *self_ty.kind() { ty::Adt(def, _) => { self.check_def_id(item, self_ty, def.did()); @@ -220,7 +220,9 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Ref(..) | ty::Never | ty::FnPtr(_) - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), + | ty::Tuple(..) => { + self.check_primitive_impl(item.owner_id.def_id, self_ty, items, ty.span) + } ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { let mut err = struct_span_err!( self.tcx.sess, @@ -241,7 +243,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + bug!("unexpected impl self type of impl: {:?} {:?}", item.owner_id, self_ty); } ty::Error(_) => {} } diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 03e076bf5..972769eb1 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -58,6 +58,37 @@ impl<'tcx> InherentOverlapChecker<'tcx> { == item2.ident(self.tcx).normalize_to_macros_2_0() } + fn check_for_duplicate_items_in_impl(&self, impl_: DefId) { + let impl_items = self.tcx.associated_items(impl_); + + let mut seen_items = FxHashMap::default(); + for impl_item in impl_items.in_definition_order() { + let span = self.tcx.def_span(impl_item.def_id); + let ident = impl_item.ident(self.tcx); + + let norm_ident = ident.normalize_to_macros_2_0(); + match seen_items.entry(norm_ident) { + Entry::Occupied(entry) => { + let former = entry.get(); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0592, + "duplicate definitions with name `{}`", + ident, + ); + err.span_label(span, format!("duplicate definitions for `{}`", ident)); + err.span_label(*former, format!("other definition for `{}`", ident)); + + err.emit(); + } + Entry::Vacant(entry) => { + entry.insert(span); + } + } + } + } + fn check_for_common_items_in_impls( &self, impl1: DefId, @@ -117,29 +148,22 @@ impl<'tcx> InherentOverlapChecker<'tcx> { // inherent impls without warning. SkipLeakCheck::Yes, overlap_mode, - |overlap| { - self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); - false - }, - || true, - ); + ) + .map_or(true, |overlap| { + self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); + false + }); } fn check_item(&mut self, id: hir::ItemId) { - let def_kind = self.tcx.def_kind(id.def_id); + let def_kind = self.tcx.def_kind(id.owner_id); if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { return; } - let impls = self.tcx.inherent_impls(id.def_id); + let impls = self.tcx.inherent_impls(id.owner_id); - // If there is only one inherent impl block, - // there is nothing to overlap check it with - if impls.len() <= 1 { - return; - } - - let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id()); + let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id()); let impls_items = impls .iter() @@ -152,6 +176,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { const ALLOCATING_ALGO_THRESHOLD: usize = 500; if impls.len() < ALLOCATING_ALGO_THRESHOLD { for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + self.check_for_duplicate_items_in_impl(impl1_def_id); + for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { if self.impls_have_common_items(impl_items1, impl_items2) { self.check_for_overlapping_inherent_impls( @@ -290,6 +316,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { impl_blocks.sort_unstable(); for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; + self.check_for_duplicate_items_in_impl(impl1_def_id); + for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; if self.impls_have_common_items(impl_items1, impl_items2) { diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index ae9ebe590..ae9ebe590 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 1608550aa..bb45c3823 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -2,10 +2,9 @@ //! crate or pertains to a type defined in this crate. use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, DelayDm}; use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; @@ -43,7 +42,7 @@ fn do_orphan_check_impl<'tcx>( ) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; - let item = tcx.hir().item(hir::ItemId { def_id }); + let item = tcx.hir().expect_item(def_id); let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!("{:?} is not an impl: {:?}", def_id, item); }; @@ -102,7 +101,7 @@ fn do_orphan_check_impl<'tcx>( span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") } - match traits::orphan_check(tcx, item.def_id.to_def_id()) { + match traits::orphan_check(tcx, item.owner_id.to_def_id()) { Ok(()) => {} Err(err) => emit_orphan_check_error( tcx, @@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>( "only traits defined in the current crate {msg}" ); err.span_label(sp, "impl doesn't use only types from inside the current crate"); - for (ty, is_target_ty) in &tys { - let mut ty = *ty; - tcx.infer_ctxt().enter(|infcx| { - // Remove the lifetimes unnecessary for this error. - ty = infcx.freshen(ty); - }); + for &(mut ty, is_target_ty) in &tys { + ty = tcx.erase_regions(ty); ty = match ty.kind() { // Remove the type arguments from the output, as they are not relevant. // You can think of this as the reverse of `resolve_vars_if_possible`. @@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>( }; let msg = format!("{} is not defined in the current crate{}", ty, postfix); - if *is_target_ty { + if is_target_ty { // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` err.span_label(self_ty_span, &msg); } else { @@ -417,30 +412,31 @@ fn lint_auto_trait_impl<'tcx>( lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, tcx.hir().local_def_id_to_hir_id(impl_def_id), tcx.def_span(impl_def_id), - |err| { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); - let mut err = err.build(&format!( + DelayDm(|| { + format!( "cross-crate traits with a default impl, like `{}`, \ should not be specialized", tcx.def_path_str(trait_ref.def_id), - )); + ) + }), + |lint| { + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); match arg { ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); + lint.note(&format!("`{}` is mentioned multiple times", arg)); } ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); + lint.note(&format!("`{}` is not a generic parameter", arg)); } } - err.span_note( + lint.span_note( item_span, &format!( "try using the same sequence of generic parameters as the {} definition", self_descr, ), - ); - err.emit(); + ) }, ); } diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs new file mode 100644 index 000000000..a34815b45 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -0,0 +1,96 @@ +//! Unsafety checker: every impl either implements a trait defined in this +//! crate or pertains to a type defined in this crate. + +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::Unsafety; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::LocalDefId; + +pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { + debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl)); + let item = tcx.hir().expect_item(def_id); + let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() }; + + if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) { + let trait_def = tcx.trait_def(trait_ref.def_id); + let unsafe_attr = + impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); + match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0199, + "implementing the trait `{}` is not unsafe", + trait_ref.print_only_trait_path() + ) + .span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); + } + + (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0200, + "the trait `{}` requires an `unsafe impl` declaration", + trait_ref.print_only_trait_path() + ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit(); + } + + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0569, + "requires an `unsafe impl` declaration due to `#[{}]` attribute", + attr_name + ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit(); + } + + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + // Reported in AST validation + tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); + } + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, None, Unsafety::Normal, _) => { + // OK + } + } + } +} diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs new file mode 100644 index 000000000..346d2e2fc --- /dev/null +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -0,0 +1,2263 @@ +//! "Collection" is the process of determining the type and other external +//! details of each item in Rust. Collection is specifically concerned +//! with *inter-procedural* things -- for example, for a function +//! definition, collection will figure out the type and signature of the +//! function, but it will not visit the *body* of the function in any way, +//! nor examine type annotations on local variables (that's the job of +//! type *checking*). +//! +//! Collecting is ultimately defined by a bundle of queries that +//! inquire after various facts about the items in the crate (e.g., +//! `type_of`, `generics_of`, `predicates_of`, etc). See the `provide` function +//! for the full set. +//! +//! At present, however, we do run collection across all items in the +//! crate as a kind of pass. This should eventually be factored away. + +use crate::astconv::AstConv; +use crate::check::intrinsic::intrinsic_operation_unsafety; +use crate::errors; +use rustc_ast as ast; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; +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_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_session::lint; +use rustc_session::parse::feature_err; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::Span; +use rustc_target::spec::{abi, SanitizerSet}; +use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use std::iter; + +mod generics_of; +mod item_bounds; +mod lifetimes; +mod predicates_of; +mod type_of; + +/////////////////////////////////////////////////////////////////////////// +// Main entry point + +fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx }); +} + +pub fn provide(providers: &mut Providers) { + lifetimes::provide(providers); + *providers = Providers { + opt_const_param_of: type_of::opt_const_param_of, + type_of: type_of::type_of, + item_bounds: item_bounds::item_bounds, + explicit_item_bounds: item_bounds::explicit_item_bounds, + generics_of: generics_of::generics_of, + predicates_of: predicates_of::predicates_of, + predicates_defined_on, + explicit_predicates_of: predicates_of::explicit_predicates_of, + super_predicates_of: predicates_of::super_predicates_of, + super_predicates_that_define_assoc_type: + predicates_of::super_predicates_that_define_assoc_type, + trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds, + type_param_predicates: predicates_of::type_param_predicates, + trait_def, + adt_def, + fn_sig, + impl_trait_ref, + impl_polarity, + is_foreign_item, + generator_kind, + codegen_fn_attrs, + asm_target_features, + collect_mod_item_types, + should_inherit_track_caller, + ..*providers + }; +} + +/////////////////////////////////////////////////////////////////////////// + +/// Context specific to some particular item. This is what implements +/// [`AstConv`]. +/// +/// # `ItemCtxt` vs `FnCtxt` +/// +/// `ItemCtxt` is primarily used to type-check item signatures and lower them +/// from HIR to their [`ty::Ty`] representation, which is exposed using [`AstConv`]. +/// It's also used for the bodies of items like structs where the body (the fields) +/// are just signatures. +/// +/// This is in contrast to `FnCtxt`, which is used to type-check bodies of +/// functions, closures, and `const`s -- anywhere that expressions and statements show up. +/// +/// An important thing to note is that `ItemCtxt` does no inference -- it has no [`InferCtxt`] -- +/// while `FnCtxt` does do inference. +/// +/// [`InferCtxt`]: rustc_infer::infer::InferCtxt +/// +/// # Trait predicates +/// +/// `ItemCtxt` has information about the predicates that are defined +/// on the trait. Unfortunately, this predicate information is +/// available in various different forms at various points in the +/// process. So we can't just store a pointer to e.g., the AST or the +/// parsed ty form, we have to be more flexible. To this end, the +/// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy +/// `get_type_parameter_bounds` requests, drawing the information from +/// the AST (`hir::Generics`), recursively. +pub struct ItemCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + item_def_id: DefId, +} + +/////////////////////////////////////////////////////////////////////////// + +#[derive(Default)] +pub(crate) struct HirPlaceholderCollector(pub(crate) Vec<Span>); + +impl<'v> Visitor<'v> for HirPlaceholderCollector { + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + if let hir::TyKind::Infer = t.kind { + self.0.push(t.span); + } + intravisit::walk_ty(self, t) + } + fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) { + match generic_arg { + hir::GenericArg::Infer(inf) => { + self.0.push(inf.span); + intravisit::walk_inf(self, inf); + } + hir::GenericArg::Type(t) => self.visit_ty(t), + _ => {} + } + } + fn visit_array_length(&mut self, length: &'v hir::ArrayLen) { + if let &hir::ArrayLen::Infer(_, span) = length { + self.0.push(span); + } + intravisit::walk_array_len(self, length) + } +} + +struct CollectItemTypesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed +/// and suggest adding type parameters in the appropriate place, taking into consideration any and +/// all already existing generic type parameters to avoid suggesting a name that is already in use. +pub(crate) fn placeholder_type_error<'tcx>( + tcx: TyCtxt<'tcx>, + generics: Option<&hir::Generics<'_>>, + placeholder_types: Vec<Span>, + suggest: bool, + hir_ty: Option<&hir::Ty<'_>>, + kind: &'static str, +) { + if placeholder_types.is_empty() { + return; + } + + placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind) + .emit(); +} + +pub(crate) fn placeholder_type_error_diag<'tcx>( + tcx: TyCtxt<'tcx>, + generics: Option<&hir::Generics<'_>>, + placeholder_types: Vec<Span>, + additional_spans: Vec<Span>, + suggest: bool, + hir_ty: Option<&hir::Ty<'_>>, + kind: &'static str, +) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + if placeholder_types.is_empty() { + return bad_placeholder(tcx, additional_spans, kind); + } + + let params = generics.map(|g| g.params).unwrap_or_default(); + let type_name = params.next_type_param_name(None); + let mut sugg: Vec<_> = + placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); + + if let Some(generics) = generics { + if let Some(arg) = params.iter().find(|arg| { + matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })) + }) { + // Account for `_` already present in cases like `struct S<_>(_);` and suggest + // `struct S<T>(T);` instead of `struct S<_, T>(T);`. + sugg.push((arg.span, (*type_name).to_string())); + } else if let Some(span) = generics.span_for_param_suggestion() { + // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`. + sugg.push((span, format!(", {}", type_name))); + } else { + sugg.push((generics.span, format!("<{}>", type_name))); + } + } + + let mut err = + bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind); + + // Suggest, but only if it is not a function in const or static + if suggest { + let mut is_fn = false; + let mut is_const_or_static = false; + + if let Some(hir_ty) = hir_ty && let hir::TyKind::BareFn(_) = hir_ty.kind { + is_fn = true; + + // Check if parent is const or static + let parent_id = tcx.hir().get_parent_node(hir_ty.hir_id); + let parent_node = tcx.hir().get(parent_id); + + is_const_or_static = matches!( + parent_node, + Node::Item(&hir::Item { + kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..), + .. + }) | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Const(..), + .. + }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) + ); + } + + // if function is wrapped around a const or static, + // then don't show the suggestion + if !(is_fn && is_const_or_static) { + err.multipart_suggestion( + "use type parameters instead", + sugg, + Applicability::HasPlaceholders, + ); + } + } + + err +} + +fn reject_placeholder_type_signatures_in_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: &'tcx hir::Item<'tcx>, +) { + let (generics, suggest) = match &item.kind { + hir::ItemKind::Union(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl(hir::Impl { generics, .. }) + | hir::ItemKind::Struct(_, generics) => (generics, true), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) + | hir::ItemKind::TyAlias(_, generics) => (generics, false), + // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. + _ => return, + }; + + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_item(item); + + placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr()); +} + +impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + convert_item(self.tcx, item.item_id()); + reject_placeholder_type_signatures_in_item(self.tcx, item); + intravisit::walk_item(self, item); + } + + fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { + for param in generics.params { + 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); + } + 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); + 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); + } + } + } + } + intravisit::walk_generics(self, generics); + } + + 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); + // 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. + } + intravisit::walk_expr(self, expr); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + convert_trait_item(self.tcx, trait_item.trait_item_id()); + intravisit::walk_trait_item(self, trait_item); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + convert_impl_item(self.tcx, impl_item.impl_item_id()); + intravisit::walk_impl_item(self, impl_item); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Utility types and common code for the above passes. + +fn bad_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + mut spans: Vec<Span>, + kind: &'static str, +) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) }; + + spans.sort(); + let mut err = struct_span_err!( + tcx.sess, + spans.clone(), + E0121, + "the placeholder `_` is not allowed within types on item signatures for {}", + kind + ); + for span in spans { + err.span_label(span, "not allowed in type signatures"); + } + err +} + +impl<'tcx> ItemCtxt<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> { + ItemCtxt { tcx, item_def_id } + } + + pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_ty) + } + + pub fn hir_id(&self) -> hir::HirId { + self.tcx.hir().local_def_id_to_hir_id(self.item_def_id.expect_local()) + } + + pub fn node(&self) -> hir::Node<'tcx> { + self.tcx.hir().get(self.hir_id()) + } +} + +impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option<DefId> { + Some(self.item_def_id) + } + + fn get_type_parameter_bounds( + &self, + span: Span, + def_id: DefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx> { + self.tcx.at(span).type_param_predicates(( + self.item_def_id, + def_id.expect_local(), + assoc_name, + )) + } + + fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option<ty::Region<'tcx>> { + None + } + + fn allow_ty_infer(&self) -> bool { + false + } + + fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + self.tcx().ty_error_with_message(span, "bad placeholder type") + } + + fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { + let ty = self.tcx.fold_regions(ty, |r, _| match *r { + ty::ReErased => self.tcx.lifetimes.re_static, + _ => r, + }); + self.tcx().const_error_with_message(ty, span, "bad placeholder constant") + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { + let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( + self, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + self.tcx().mk_projection(item_def_id, item_substs) + } else { + // There are no late-bound regions; we can just ignore the binder. + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0212, + "cannot use the associated type of a trait \ + with uninferred generic parameters" + ); + + match self.node() { + hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => { + let item = self + .tcx + .hir() + .expect_item(self.tcx.hir().get_parent_item(self.hir_id()).def_id); + match &item.kind { + hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Union(_, generics) => { + let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); + let (lt_sp, sugg) = match generics.params { + [] => (generics.span, format!("<{}>", lt_name)), + [bound, ..] => { + (bound.span.shrink_to_lo(), format!("{}, ", lt_name)) + } + }; + let suggestions = vec![ + (lt_sp, sugg), + ( + span.with_hi(item_segment.ident.span.lo()), + format!( + "{}::", + // Replace the existing lifetimes with a new named lifetime. + self.tcx.replace_late_bound_regions_uncached( + poly_trait_ref, + |_| { + self.tcx.mk_region(ty::ReEarlyBound( + ty::EarlyBoundRegion { + def_id: item_def_id, + index: 0, + name: Symbol::intern(<_name), + }, + )) + } + ), + ), + ), + ]; + err.multipart_suggestion( + "use a fully qualified path with explicit lifetimes", + suggestions, + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..), + .. + }) => {} + hir::Node::Item(_) + | hir::Node::ForeignItem(_) + | hir::Node::TraitItem(_) + | hir::Node::ImplItem(_) => { + err.span_suggestion_verbose( + span.with_hi(item_segment.ident.span.lo()), + "use a fully qualified path with inferred lifetimes", + format!( + "{}::", + // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`. + self.tcx.anonymize_late_bound_regions(poly_trait_ref).skip_binder(), + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err.emit(); + self.tcx().ty_error() + } + } + + fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + // Types in item signatures are not normalized to avoid undue dependencies. + ty + } + + fn set_tainted_by_errors(&self) { + // There's no obvious place to track this, so just let it go. + } + + fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { + // There's no place to record types from signatures? + } +} + +/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. +fn get_new_lifetime_name<'tcx>( + tcx: TyCtxt<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + generics: &hir::Generics<'tcx>, +) -> String { + let existing_lifetimes = tcx + .collect_referenced_late_bound_regions(&poly_trait_ref) + .into_iter() + .filter_map(|lt| { + if let ty::BoundRegionKind::BrNamed(_, name) = lt { + Some(name.as_str().to_string()) + } else { + None + } + }) + .chain(generics.params.iter().filter_map(|param| { + if let hir::GenericParamKind::Lifetime { .. } = ¶m.kind { + Some(param.name.ident().as_str().to_string()) + } else { + None + } + })) + .collect::<FxHashSet<String>>(); + + let a_to_z_repeat_n = |n| { + (b'a'..=b'z').map(move |c| { + let mut s = '\''.to_string(); + s.extend(std::iter::repeat(char::from(c)).take(n)); + s + }) + }; + + // If all single char lifetime names are present, we wrap around and double the chars. + (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() +} + +fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { + let it = tcx.hir().item(item_id); + debug!("convert: item {} with id {}", it.ident, it.hir_id()); + let def_id = item_id.owner_id.def_id; + + match it.kind { + // These don't define types. + hir::ItemKind::ExternCrate(_) + | hir::ItemKind::Use(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Mod(_) + | hir::ItemKind::GlobalAsm(_) => {} + hir::ItemKind::ForeignMod { items, .. } => { + for item in items { + let item = tcx.hir().foreign_item(item.id); + tcx.ensure().generics_of(item.owner_id); + 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::Static(..) => { + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_foreign_item(item); + placeholder_type_error( + tcx, + None, + visitor.0, + false, + None, + "static variable", + ); + } + _ => (), + } + } + } + hir::ItemKind::Enum(ref enum_definition, _) => { + 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); + } + hir::ItemKind::Impl { .. } => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().impl_trait_ref(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::Trait(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().trait_def(def_id); + tcx.at(it.span).super_predicates_of(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::TraitAlias(..) => { + tcx.ensure().generics_of(def_id); + tcx.at(it.span).super_predicates_of(def_id); + tcx.ensure().predicates_of(def_id); + } + hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + 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); + } + + if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { + convert_variant_ctor(tcx, ctor_hir_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. + hir::ItemKind::OpaqueTy(..) => { + tcx.ensure().generics_of(def_id); + tcx.ensure().predicates_of(def_id); + tcx.ensure().explicit_item_bounds(def_id); + } + hir::ItemKind::TyAlias(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Const(..) + | hir::ItemKind::Fn(..) => { + 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()); + } + } + _ => (), + } + } + } +} + +fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { + let trait_item = tcx.hir().trait_item(trait_item_id); + let def_id = trait_item_id.owner_id; + tcx.ensure().generics_of(def_id); + + match trait_item.kind { + hir::TraitItemKind::Fn(..) => { + tcx.ensure().type_of(def_id); + tcx.ensure().fn_sig(def_id); + } + + hir::TraitItemKind::Const(.., Some(_)) => { + tcx.ensure().type_of(def_id); + } + + hir::TraitItemKind::Const(hir_ty, _) => { + tcx.ensure().type_of(def_id); + // Account for `const C: _;`. + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_trait_item(trait_item); + if !tcx.sess.diagnostic().has_stashed_diagnostic(hir_ty.span, StashKey::ItemNoType) { + placeholder_type_error(tcx, None, visitor.0, false, None, "constant"); + } + } + + hir::TraitItemKind::Type(_, Some(_)) => { + tcx.ensure().item_bounds(def_id); + tcx.ensure().type_of(def_id); + // Account for `type T = _;`. + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_trait_item(trait_item); + placeholder_type_error(tcx, None, visitor.0, false, None, "associated type"); + } + + hir::TraitItemKind::Type(_, None) => { + tcx.ensure().item_bounds(def_id); + // #74612: Visit and try to find bad placeholders + // even if there is no concrete type. + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_trait_item(trait_item); + + placeholder_type_error(tcx, None, visitor.0, false, None, "associated type"); + } + }; + + tcx.ensure().predicates_of(def_id); +} + +fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { + let def_id = impl_item_id.owner_id; + tcx.ensure().generics_of(def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().predicates_of(def_id); + let impl_item = tcx.hir().impl_item(impl_item_id); + match impl_item.kind { + hir::ImplItemKind::Fn(..) => { + tcx.ensure().fn_sig(def_id); + } + hir::ImplItemKind::Type(_) => { + // Account for `type T = _;` + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_impl_item(impl_item); + + placeholder_type_error(tcx, None, visitor.0, false, None, "associated type"); + } + hir::ImplItemKind::Const(..) => {} + } +} + +fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) { + let def_id = tcx.hir().local_def_id(ctor_id); + 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<'_>]) { + 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 { + 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()) + } 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()), + ) + .note(&format!( + "explicitly set `{} = {}` if that is desired outcome", + variant.ident, wrapped_discr + )) + .emit(); + None + } + .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); + } + + // 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); + } + } +} + +fn convert_variant( + tcx: TyCtxt<'_>, + variant_did: Option<LocalDefId>, + ctor_did: Option<LocalDefId>, + ident: Ident, + discr: ty::VariantDiscr, + def: &hir::VariantData<'_>, + adt_kind: ty::AdtKind, + parent_did: LocalDefId, +) -> ty::VariantDef { + let mut seen_fields: FxHashMap<Ident, Span> = Default::default(); + let fields = def + .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 { + field_name: f.ident, + span: f.span, + prev_span, + }); + } else { + 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) } + }) + .collect(); + let recovered = match def { + hir::VariantData::Struct(_, r) => *r, + _ => false, + }; + ty::VariantDef::new( + ident.name, + variant_did.map(LocalDefId::to_def_id), + ctor_did.map(LocalDefId::to_def_id), + discr, + fields, + CtorKind::from_hir(def), + adt_kind, + parent_did.to_def_id(), + recovered, + adt_kind == AdtKind::Struct && tcx.has_attr(parent_did.to_def_id(), sym::non_exhaustive) + || variant_did.map_or(false, |variant_did| { + tcx.has_attr(variant_did.to_def_id(), sym::non_exhaustive) + }), + ) +} + +fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { + use rustc_hir::*; + + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let Node::Item(item) = tcx.hir().get(hir_id) else { + bug!(); + }; + + let repr = ReprOptions::new(tcx, def_id.to_def_id()); + let (kind, variants) = match item.kind { + ItemKind::Enum(ref def, _) => { + let mut distance_from_explicit = 0; + let variants = def + .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()) + } else { + ty::VariantDiscr::Relative(distance_from_explicit) + }; + distance_from_explicit += 1; + + convert_variant( + tcx, + variant_did, + ctor_did, + v.ident, + discr, + &v.data, + AdtKind::Enum, + def_id, + ) + }) + .collect(); + + (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)); + + let variants = std::iter::once(convert_variant( + tcx, + variant_did, + ctor_did, + item.ident, + ty::VariantDiscr::Relative(0), + def, + AdtKind::Union, + def_id, + )) + .collect(); + + (AdtKind::Union, variants) + } + _ => bug!(), + }; + tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr) +} + +fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { + let item = tcx.hir().expect_item(def_id.expect_local()); + + let (is_auto, unsafety, items) = match item.kind { + hir::ItemKind::Trait(is_auto, unsafety, .., items) => { + (is_auto == hir::IsAuto::Yes, unsafety, items) + } + hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal, &[][..]), + _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), + }; + + let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar); + if paren_sugar && !tcx.features().unboxed_closures { + tcx.sess + .struct_span_err( + item.span, + "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \ + which traits can use parenthetical notation", + ) + .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it") + .emit(); + } + + let is_marker = tcx.has_attr(def_id, sym::marker); + let skip_array_during_method_dispatch = + tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch); + let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { + ty::trait_def::TraitSpecializationKind::Marker + } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; + let must_implement_one_of = tcx + .get_attr(def_id, sym::rustc_must_implement_one_of) + // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]` + // and that they are all identifiers + .and_then(|attr| match attr.meta_item_list() { + Some(items) if items.len() < 2 => { + tcx.sess + .struct_span_err( + attr.span, + "the `#[rustc_must_implement_one_of]` attribute must be \ + used with at least 2 args", + ) + .emit(); + + None + } + Some(items) => items + .into_iter() + .map(|item| item.ident().ok_or(item.span())) + .collect::<Result<Box<[_]>, _>>() + .map_err(|span| { + tcx.sess + .struct_span_err(span, "must be a name of an associated function") + .emit(); + }) + .ok() + .zip(Some(attr.span)), + // Error is reported by `rustc_attr!` + None => None, + }) + // Check that all arguments of `#[rustc_must_implement_one_of]` reference + // functions in the trait with default implementations + .and_then(|(list, attr_span)| { + let errors = list.iter().filter_map(|ident| { + let item = items.iter().find(|item| item.ident == *ident); + + match item { + Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => { + if !tcx.impl_defaultness(item.id.owner_id).has_value() { + tcx.sess + .struct_span_err( + item.span, + "This function doesn't have a default implementation", + ) + .span_note(attr_span, "required by this annotation") + .emit(); + + return Some(()); + } + + return None; + } + Some(item) => { + tcx.sess + .struct_span_err(item.span, "Not a function") + .span_note(attr_span, "required by this annotation") + .note( + "All `#[rustc_must_implement_one_of]` arguments \ + must be associated function names", + ) + .emit(); + } + None => { + tcx.sess + .struct_span_err(ident.span, "Function not found in this trait") + .emit(); + } + } + + Some(()) + }); + + (errors.count() == 0).then_some(list) + }) + // Check for duplicates + .and_then(|list| { + let mut set: FxHashMap<Symbol, Span> = FxHashMap::default(); + let mut no_dups = true; + + for ident in &*list { + if let Some(dup) = set.insert(ident.name, ident.span) { + tcx.sess + .struct_span_err(vec![dup, ident.span], "Functions names are duplicated") + .note( + "All `#[rustc_must_implement_one_of]` arguments \ + must be unique", + ) + .emit(); + + no_dups = false; + } + } + + no_dups.then_some(list) + }); + + ty::TraitDef::new( + def_id, + unsafety, + paren_sugar, + is_auto, + is_marker, + skip_array_during_method_dispatch, + spec_kind, + must_implement_one_of, + ) +} + +fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { + generic_args.iter().any(|arg| match arg { + hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty), + hir::GenericArg::Infer(_) => true, + _ => false, + }) +} + +/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to +/// use inference to provide suggestions for the appropriate type if possible. +fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { + debug!(?ty); + use hir::TyKind::*; + match &ty.kind { + Infer => true, + Slice(ty) => is_suggestable_infer_ty(ty), + Array(ty, length) => { + is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _)) + } + Tup(tys) => tys.iter().any(is_suggestable_infer_ty), + Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), + OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args), + Path(hir::QPath::TypeRelative(ty, segment)) => { + is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args) + } + Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { + ty_opt.map_or(false, is_suggestable_infer_ty) + || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args)) + } + _ => false, + } +} + +pub fn get_infer_ret_ty<'hir>(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> { + if let hir::FnRetTy::Return(ty) = output { + if is_suggestable_infer_ty(ty) { + return Some(&*ty); + } + } + None +} + +#[instrument(level = "debug", skip(tcx))] +fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { + use rustc_hir::Node::*; + use rustc_hir::*; + + let def_id = def_id.expect_local(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let icx = ItemCtxt::new(tcx, def_id.to_def_id()); + + match tcx.hir().get(hir_id) { + TraitItem(hir::TraitItem { + kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), + generics, + .. + }) + | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { + infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + } + + ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { + // Do not try to infer the return type for a impl method coming from a trait + if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = + tcx.hir().get(tcx.hir().get_parent_node(hir_id)) + && i.of_trait.is_some() + { + <dyn AstConv<'_>>::ty_of_fn( + &icx, + hir_id, + sig.header.unsafety, + sig.header.abi, + sig.decl, + Some(generics), + None, + ) + } else { + infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + } + } + + TraitItem(hir::TraitItem { + kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), + generics, + .. + }) => <dyn AstConv<'_>>::ty_of_fn( + &icx, + hir_id, + header.unsafety, + header.abi, + decl, + Some(generics), + None, + ), + + ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { + let abi = tcx.hir().get_foreign_abi(hir_id); + 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() => { + 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))); + ty::Binder::dummy(tcx.mk_fn_sig( + inputs, + ty, + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + )) + } + + Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + // Closure signatures are not like other function + // signatures and cannot be accessed through `fn_sig`. For + // example, a closure signature excludes the `self` + // argument. In any case they are embedded within the + // closure type as part of the `ClosureSubsts`. + // + // To get the signature of a closure, you should use the + // `sig` method on the `ClosureSubsts`: + // + // substs.as_closure().sig(def_id, tcx) + bug!( + "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`", + ); + } + + x => { + bug!("unexpected sort of node in fn_sig(): {:?}", x); + } + } +} + +fn infer_return_ty_for_fn_sig<'tcx>( + tcx: TyCtxt<'tcx>, + sig: &hir::FnSig<'_>, + generics: &hir::Generics<'_>, + def_id: LocalDefId, + icx: &ItemCtxt<'tcx>, +) -> ty::PolyFnSig<'tcx> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + match get_infer_ret_ty(&sig.decl.output) { + Some(ty) => { + let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; + // Typeck doesn't expect erased regions to be returned from `type_of`. + let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r { + ty::ReErased => tcx.lifetimes.re_static, + _ => r, + }); + let fn_sig = ty::Binder::dummy(fn_sig); + + let mut visitor = HirPlaceholderCollector::default(); + visitor.visit_ty(ty); + let mut diag = bad_placeholder(tcx, visitor.0, "return type"); + let ret_ty = fn_sig.skip_binder().output(); + if ret_ty.is_suggestable(tcx, false) { + diag.span_suggestion( + ty.span, + "replace with the correct return type", + ret_ty, + Applicability::MachineApplicable, + ); + } else if matches!(ret_ty.kind(), ty::FnDef(..)) { + let fn_sig = ret_ty.fn_sig(tcx); + if fn_sig + .skip_binder() + .inputs_and_output + .iter() + .all(|t| t.is_suggestable(tcx, false)) + { + diag.span_suggestion( + ty.span, + "replace with the correct return type", + fn_sig, + Applicability::MachineApplicable, + ); + } + } else if ret_ty.is_closure() { + // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds + // to prevent the user from getting a papercut while trying to use the unique closure + // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); + } + diag.emit(); + + fn_sig + } + None => <dyn AstConv<'_>>::ty_of_fn( + icx, + hir_id, + sig.header.unsafety, + sig.header.abi, + sig.decl, + Some(generics), + None, + ), + } +} + +fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> { + let icx = ItemCtxt::new(tcx, def_id); + let item = tcx.hir().expect_item(def_id.expect_local()); + match item.kind { + hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { + let selfty = tcx.type_of(def_id); + <dyn AstConv<'_>>::instantiate_mono_trait_ref( + &icx, + ast_trait_ref, + selfty, + check_impl_constness(tcx, impl_.constness, ast_trait_ref), + ) + }), + _ => bug!(), + } +} + +fn check_impl_constness( + tcx: TyCtxt<'_>, + constness: hir::Constness, + ast_trait_ref: &hir::TraitRef<'_>, +) -> ty::BoundConstness { + match constness { + hir::Constness::Const => { + if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) { + let trait_name = tcx.item_name(trait_def_id).to_string(); + tcx.sess.emit_err(errors::ConstImplForNonConstTrait { + trait_ref_span: ast_trait_ref.path.span, + trait_name, + local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()), + marking: (), + adding: (), + }); + ty::BoundConstness::NotConst + } else { + ty::BoundConstness::ConstIfConst + } + }, + hir::Constness::NotConst => ty::BoundConstness::NotConst, + } +} + +fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { + let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); + let item = tcx.hir().expect_item(def_id.expect_local()); + match &item.kind { + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Negative(span), + of_trait, + .. + }) => { + if is_rustc_reservation { + let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span)); + tcx.sess.span_err(span, "reservation impls can't be negative"); + } + ty::ImplPolarity::Negative + } + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Positive, + of_trait: None, + .. + }) => { + if is_rustc_reservation { + tcx.sess.span_err(item.span, "reservation impls can't be inherent"); + } + ty::ImplPolarity::Positive + } + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Positive, + of_trait: Some(_), + .. + }) => { + if is_rustc_reservation { + ty::ImplPolarity::Reservation + } else { + ty::ImplPolarity::Positive + } + } + item => bug!("impl_polarity: {:?} not an impl", item), + } +} + +/// Returns the early-bound lifetimes declared in this generics +/// listing. For anything other than fns/methods, this is just all +/// the lifetimes that are declared. For fns or methods, we have to +/// screen out those that do not appear in any where-clauses etc using +/// `resolve_lifetime::early_bound_lifetimes`. +fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>( + tcx: TyCtxt<'tcx>, + generics: &'a hir::Generics<'a>, +) -> impl Iterator<Item = &'a hir::GenericParam<'a>> + Captures<'tcx> { + generics.params.iter().filter(move |param| match param.kind { + GenericParamKind::Lifetime { .. } => !tcx.is_late_bound(param.hir_id), + _ => false, + }) +} + +/// Returns a list of type predicates for the definition with ID `def_id`, including inferred +/// lifetime constraints. This includes all predicates returned by `explicit_predicates_of`, plus +/// inferred constraints concerning which regions outlive other regions. +#[instrument(level = "debug", skip(tcx))] +fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + let mut result = tcx.explicit_predicates_of(def_id); + debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,); + let inferred_outlives = tcx.inferred_outlives_of(def_id); + if !inferred_outlives.is_empty() { + debug!( + "predicates_defined_on: inferred_outlives_of({:?}) = {:?}", + def_id, inferred_outlives, + ); + if result.predicates.is_empty() { + result.predicates = inferred_outlives; + } else { + result.predicates = tcx + .arena + .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); + } + } + + debug!("predicates_defined_on({:?}) = {:?}", def_id, result); + result +} + +fn compute_sig_of_foreign_fn_decl<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + decl: &'tcx hir::FnDecl<'tcx>, + abi: abi::Abi, +) -> ty::PolyFnSig<'tcx> { + let unsafety = if abi == abi::Abi::RustIntrinsic { + intrinsic_operation_unsafety(tcx, def_id) + } else { + hir::Unsafety::Unsafe + }; + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let fty = <dyn AstConv<'_>>::ty_of_fn( + &ItemCtxt::new(tcx, def_id), + hir_id, + unsafety, + abi, + decl, + None, + None, + ); + + // Feature gate SIMD types in FFI, since I am not sure that the + // ABIs are handled at all correctly. -huonw + if abi != abi::Abi::RustIntrinsic + && abi != abi::Abi::PlatformIntrinsic + && !tcx.features().simd_ffi + { + let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { + if ty.is_simd() { + let snip = tcx + .sess + .source_map() + .span_to_snippet(ast_ty.span) + .map_or_else(|_| String::new(), |s| format!(" `{}`", s)); + tcx.sess + .struct_span_err( + ast_ty.span, + &format!( + "use of SIMD type{} in FFI is highly experimental and \ + may result in invalid code", + snip + ), + ) + .help("add `#![feature(simd_ffi)]` to the crate attributes to enable") + .emit(); + } + }; + for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) { + check(input, *ty) + } + if let hir::FnRetTy::Return(ref ty) = decl.output { + check(ty, fty.output().skip_binder()) + } + } + + fty +} + +fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + match tcx.hir().get_if_local(def_id) { + Some(Node::ForeignItem(..)) => true, + Some(_) => false, + _ => bug!("is_foreign_item applied to non-local def-id {:?}", def_id), + } +} + +fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> { + match tcx.hir().get_if_local(def_id) { + Some(Node::Expr(&rustc_hir::Expr { + kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }), + .. + })) => tcx.hir().body(body).generator_kind(), + Some(_) => None, + _ => bug!("generator_kind applied to non-local def-id {:?}", def_id), + } +} + +fn from_target_feature( + tcx: TyCtxt<'_>, + attr: &ast::Attribute, + supported_target_features: &FxHashMap<String, Option<Symbol>>, + target_features: &mut Vec<Symbol>, +) { + let Some(list) = attr.meta_item_list() else { return }; + let bad_item = |span| { + let msg = "malformed `target_feature` attribute input"; + let code = "enable = \"..\""; + tcx.sess + .struct_span_err(span, msg) + .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) + .emit(); + }; + let rust_features = tcx.features(); + for item in list { + // Only `enable = ...` is accepted in the meta-item list. + if !item.has_name(sym::enable) { + bad_item(item.span()); + continue; + } + + // Must be of the form `enable = "..."` (a string). + let Some(value) = item.value_str() else { + bad_item(item.span()); + continue; + }; + + // We allow comma separation to enable multiple features. + target_features.extend(value.as_str().split(',').filter_map(|feature| { + let Some(feature_gate) = supported_target_features.get(feature) else { + let msg = + format!("the feature named `{}` is not valid for this target", feature); + let mut err = tcx.sess.struct_span_err(item.span(), &msg); + err.span_label( + item.span(), + format!("`{}` is not valid for this target", feature), + ); + if let Some(stripped) = feature.strip_prefix('+') { + let valid = supported_target_features.contains_key(stripped); + if valid { + err.help("consider removing the leading `+` in the feature name"); + } + } + err.emit(); + return None; + }; + + // Only allow features whose feature gates have been enabled. + let allowed = match feature_gate.as_ref().copied() { + Some(sym::arm_target_feature) => rust_features.arm_target_feature, + Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, + Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, + Some(sym::mips_target_feature) => rust_features.mips_target_feature, + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, + Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, + Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, + Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, + Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, + Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, + Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, + Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, + Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, + Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, + Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, + Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, + Some(name) => bug!("unknown target feature gate {}", name), + None => true, + }; + if !allowed { + feature_err( + &tcx.sess.parse_sess, + feature_gate.unwrap(), + item.span(), + &format!("the target feature `{}` is currently unstable", feature), + ) + .emit(); + } + Some(Symbol::intern(feature)) + })); + } +} + +fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { + use rustc_middle::mir::mono::Linkage::*; + + // Use the names from src/llvm/docs/LangRef.rst here. Most types are only + // applicable to variable declarations and may not really make sense for + // Rust code in the first place but allow them anyway and trust that the + // user knows what they're doing. Who knows, unanticipated use cases may pop + // up in the future. + // + // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported + // and don't have to be, LLVM treats them as no-ops. + match name { + "appending" => Appending, + "available_externally" => AvailableExternally, + "common" => Common, + "extern_weak" => ExternalWeak, + "external" => External, + "internal" => Internal, + "linkonce" => LinkOnceAny, + "linkonce_odr" => LinkOnceODR, + "private" => Private, + "weak" => WeakAny, + "weak_odr" => WeakODR, + _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"), + } +} + +fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { + if cfg!(debug_assertions) { + let def_kind = tcx.def_kind(did); + assert!( + def_kind.has_codegen_attrs(), + "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", + ); + } + + let did = did.expect_local(); + let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did)); + let mut codegen_fn_attrs = CodegenFnAttrs::new(); + if tcx.should_inherit_track_caller(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } + + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); + + let mut inline_span = None; + let mut link_ordinal_span = None; + let mut no_sanitize_span = None; + for attr in attrs.iter() { + if attr.has_name(sym::cold) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; + } else if attr.has_name(sym::rustc_allocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; + } else if attr.has_name(sym::ffi_returns_twice) { + if tcx.is_foreign_item(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE; + } else { + // `#[ffi_returns_twice]` is only allowed `extern fn`s. + struct_span_err!( + tcx.sess, + attr.span, + E0724, + "`#[ffi_returns_twice]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::ffi_pure) { + if tcx.is_foreign_item(did) { + if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]` + struct_span_err!( + tcx.sess, + attr.span, + E0757, + "`#[ffi_const]` function cannot be `#[ffi_pure]`" + ) + .emit(); + } else { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE; + } + } else { + // `#[ffi_pure]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0755, + "`#[ffi_pure]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::ffi_const) { + if tcx.is_foreign_item(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST; + } else { + // `#[ffi_const]` is only allowed on foreign functions + struct_span_err!( + tcx.sess, + attr.span, + E0756, + "`#[ffi_const]` may only be used on foreign functions" + ) + .emit(); + } + } else if attr.has_name(sym::rustc_nounwind) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } else if attr.has_name(sym::rustc_reallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; + } else if attr.has_name(sym::rustc_deallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR; + } else if attr.has_name(sym::rustc_allocator_zeroed) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED; + } else if attr.has_name(sym::naked) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; + } else if attr.has_name(sym::no_mangle) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } else if attr.has_name(sym::no_coverage) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE; + } else if attr.has_name(sym::rustc_std_internal_symbol) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } else if attr.has_name(sym::used) { + let inner = attr.meta_item_list(); + match inner.as_deref() { + Some([item]) if item.has_name(sym::linker) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(linker)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; + } + Some([item]) if item.has_name(sym::compiler) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(compiler)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + } + Some(_) => { + tcx.sess.emit_err(errors::ExpectedUsedSymbol { span: attr.span }); + } + None => { + // Unfortunately, unconditionally using `llvm.used` causes + // issues in handling `.init_array` with the gold linker, + // but using `llvm.compiler.used` caused a nontrival amount + // of unintentional ecosystem breakage -- particularly on + // Mach-O targets. + // + // As a result, we emit `llvm.compiler.used` only on ELF + // targets. This is somewhat ad-hoc, but actually follows + // our pre-LLVM 13 behavior (prior to the ecosystem + // breakage), and seems to match `clang`'s behavior as well + // (both before and after LLVM 13), possibly because they + // have similar compatibility concerns to us. See + // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 + // and following comments for some discussion of this, as + // well as the comments in `rustc_codegen_llvm` where these + // flags are handled. + // + // Anyway, to be clear: this is still up in the air + // somewhat, and is subject to change in the future (which + // is a good thing, because this would ideally be a bit + // more firmed up). + let is_like_elf = !(tcx.sess.target.is_like_osx + || tcx.sess.target.is_like_windows + || tcx.sess.target.is_like_wasm); + codegen_fn_attrs.flags |= if is_like_elf { + CodegenFnAttrFlags::USED + } else { + CodegenFnAttrFlags::USED_LINKER + }; + } + } + } else if attr.has_name(sym::cmse_nonsecure_entry) { + if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { + struct_span_err!( + tcx.sess, + attr.span, + E0776, + "`#[cmse_nonsecure_entry]` requires C ABI" + ) + .emit(); + } + if !tcx.sess.target.llvm_target.contains("thumbv8m") { + struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension") + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY; + } else if attr.has_name(sym::thread_local) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; + } else if attr.has_name(sym::track_caller) { + if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust { + struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") + .emit(); + } + if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { + feature_err( + &tcx.sess.parse_sess, + sym::closure_track_caller, + attr.span, + "`#[track_caller]` on closures is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } else if attr.has_name(sym::export_name) { + if let Some(s) = attr.value_str() { + if s.as_str().contains('\0') { + // `#[export_name = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + struct_span_err!( + tcx.sess, + attr.span, + E0648, + "`export_name` may not contain null characters" + ) + .emit(); + } + codegen_fn_attrs.export_name = Some(s); + } + } else if attr.has_name(sym::target_feature) { + if !tcx.is_closure(did.to_def_id()) + && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal + { + if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { + // The `#[target_feature]` attribute is allowed on + // WebAssembly targets on all functions, including safe + // ones. Other targets require that `#[target_feature]` is + // only applied to unsafe functions (pending the + // `target_feature_11` feature) because on most targets + // execution of instructions that are not supported is + // considered undefined behavior. For WebAssembly which is a + // 100% safe target at execution time it's not possible to + // execute undefined instructions, and even if a future + // feature was added in some form for this it would be a + // deterministic trap. There is no undefined behavior when + // executing WebAssembly so `#[target_feature]` is allowed + // on safe functions (but again, only for WebAssembly) + // + // Note that this is also allowed if `actually_rustdoc` so + // if a target is documenting some wasm-specific code then + // it's not spuriously denied. + } else if !tcx.features().target_feature_11 { + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::target_feature_11, + attr.span, + "`#[target_feature(..)]` can only be applied to `unsafe` functions", + ); + err.span_label(tcx.def_span(did), "not an `unsafe` function"); + err.emit(); + } else { + check_target_feature_trait_unsafe(tcx, did, attr.span); + } + } + from_target_feature( + tcx, + attr, + supported_target_features, + &mut codegen_fn_attrs.target_features, + ); + } 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())); + } + } else if attr.has_name(sym::link_section) { + if let Some(val) = attr.value_str() { + if val.as_str().bytes().any(|b| b == 0) { + let msg = format!( + "illegal null byte in link_section \ + value: `{}`", + &val + ); + tcx.sess.span_err(attr.span, &msg); + } else { + codegen_fn_attrs.link_section = Some(val); + } + } + } else if attr.has_name(sym::link_name) { + codegen_fn_attrs.link_name = attr.value_str(); + } else if attr.has_name(sym::link_ordinal) { + link_ordinal_span = Some(attr.span); + if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { + codegen_fn_attrs.link_ordinal = ordinal; + } + } else if attr.has_name(sym::no_sanitize) { + no_sanitize_span = Some(attr.span); + if let Some(list) = attr.meta_item_list() { + for item in list.iter() { + if item.has_name(sym::address) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; + } else if item.has_name(sym::cfi) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; + } else if item.has_name(sym::memory) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; + } else if item.has_name(sym::memtag) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; + } else if item.has_name(sym::shadow_call_stack) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; + } else if item.has_name(sym::thread) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; + } else if item.has_name(sym::hwaddress) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS; + } else { + tcx.sess + .struct_span_err(item.span(), "invalid argument for `no_sanitize`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") + .emit(); + } + } + } + } else if attr.has_name(sym::instruction_set) { + codegen_fn_attrs.instruction_set = match attr.meta_kind() { + Some(MetaItemKind::List(ref items)) => match items.as_slice() { + [NestedMetaItem::MetaItem(set)] => { + let segments = + set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>(); + match segments.as_slice() { + [sym::arm, sym::a32] | [sym::arm, sym::t32] => { + if !tcx.sess.target.has_thumb_interworking { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "target does not support `#[instruction_set]`" + ) + .emit(); + None + } else if segments[1] == sym::a32 { + Some(InstructionSetAttr::ArmA32) + } else if segments[1] == sym::t32 { + Some(InstructionSetAttr::ArmT32) + } else { + unreachable!() + } + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "invalid instruction set specified", + ) + .emit(); + None + } + } + } + [] => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "`#[instruction_set]` requires an argument" + ) + .emit(); + None + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "cannot specify more than one instruction set" + ) + .emit(); + None + } + }, + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "must specify an instruction set" + ) + .emit(); + None + } + }; + } else if attr.has_name(sym::repr) { + codegen_fn_attrs.alignment = match attr.meta_item_list() { + Some(items) => match items.as_slice() { + [item] => match item.name_value_literal() { + Some((sym::align, literal)) => { + let alignment = rustc_attr::parse_alignment(&literal.kind); + + match alignment { + Ok(align) => Some(align), + Err(msg) => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0589, + "invalid `repr(align)` attribute: {}", + msg + ) + .emit(); + + None + } + } + } + _ => None, + }, + [] => None, + _ => None, + }, + None => None, + }; + } + } + + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { + if !attr.has_name(sym::inline) { + return ia; + } + match attr.meta_kind() { + Some(MetaItemKind::Word) => InlineAttr::Hint, + Some(MetaItemKind::List(ref items)) => { + inline_span = Some(attr.span); + if items.len() != 1 { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0534, + "expected one argument" + ) + .emit(); + InlineAttr::None + } else if list_contains_name(&items, sym::always) { + InlineAttr::Always + } else if list_contains_name(&items, sym::never) { + InlineAttr::Never + } else { + struct_span_err!( + tcx.sess.diagnostic(), + items[0].span(), + E0535, + "invalid argument" + ) + .help("valid inline arguments are `always` and `never`") + .emit(); + + InlineAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { + if !attr.has_name(sym::optimize) { + return ia; + } + let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit(); + match attr.meta_kind() { + Some(MetaItemKind::Word) => { + err(attr.span, "expected one argument"); + ia + } + Some(MetaItemKind::List(ref items)) => { + inline_span = Some(attr.span); + if items.len() != 1 { + err(attr.span, "expected one argument"); + OptimizeAttr::None + } else if list_contains_name(&items, sym::size) { + OptimizeAttr::Size + } else if list_contains_name(&items, sym::speed) { + OptimizeAttr::Speed + } else { + err(items[0].span(), "invalid argument"); + OptimizeAttr::None + } + } + Some(MetaItemKind::NameValue(_)) => ia, + None => ia, + } + }); + + // #73631: closures inherit `#[target_feature]` annotations + if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) { + let owner_id = tcx.parent(did.to_def_id()); + if tcx.def_kind(owner_id).has_codegen_attrs() { + codegen_fn_attrs + .target_features + .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()); + } + } + + // If a function uses #[target_feature] it can't be inlined into general + // purpose functions as they wouldn't have the right target features + // enabled. For that reason we also forbid #[inline(always)] as it can't be + // respected. + if !codegen_fn_attrs.target_features.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let Some(span) = inline_span { + tcx.sess.span_err( + span, + "cannot use `#[inline(always)]` with \ + `#[target_feature]`", + ); + } + } + } + + if !codegen_fn_attrs.no_sanitize.is_empty() { + if codegen_fn_attrs.inline == InlineAttr::Always { + if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(did); + tcx.struct_span_lint_hir( + lint::builtin::INLINE_NO_SANITIZE, + hir_id, + no_sanitize_span, + "`no_sanitize` will have no effect after inlining", + |lint| lint.span_note(inline_span, "inlining requested here"), + ) + } + } + } + + // 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()) { + 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); + } + check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); + + // Internal symbols to the standard library all have no_mangle semantics in + // that they have defined symbol names present in the function name. This + // also applies to weak symbols where they all have known symbol names. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } + + // Any linkage to LLVM intrinsics for now forcibly marks them all as never + // unwinds since LLVM sometimes can't handle codegen which `invoke`s + // intrinsic functions. + if let Some(name) = &codegen_fn_attrs.link_name { + if name.as_str().starts_with("llvm.") { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } + } + + codegen_fn_attrs +} + +/// Computes the set of target features used in a function for the purposes of +/// inline assembly. +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> { + let mut target_features = tcx.sess.unstable_target_features.clone(); + if tcx.def_kind(did).has_codegen_attrs() { + let attrs = tcx.codegen_fn_attrs(did); + target_features.extend(&attrs.target_features); + match attrs.instruction_set { + None => {} + Some(InstructionSetAttr::ArmA32) => { + target_features.remove(&sym::thumb_mode); + } + Some(InstructionSetAttr::ArmT32) => { + target_features.insert(sym::thumb_mode); + } + } + } + + tcx.arena.alloc(target_features) +} + +/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller +/// applied to the method prototype. +fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(impl_item) = tcx.opt_associated_item(def_id) + && let ty::AssocItemContainer::ImplContainer = impl_item.container + && let Some(trait_item) = impl_item.trait_item_def_id + { + return tcx + .codegen_fn_attrs(trait_item) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); + } + + false +} + +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { + use rustc_ast::{Lit, LitIntType, LitKind}; + if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" { + feature_err( + &tcx.sess.parse_sess, + sym::raw_dylib, + attr.span, + "`#[link_ordinal]` is unstable on x86", + ) + .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 sole_meta_list = match meta_item_list { + Some([item]) => item.literal(), + Some(_) => { + tcx.sess + .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`") + .note("the attribute requires exactly one argument") + .emit(); + return None; + } + _ => None, + }; + if let Some(Lit { 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 + // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. + // + // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this: + // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies + // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library + // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import + // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet + // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment + // about LINK.EXE failing.) + if *ordinal <= u16::MAX as u128 { + Some(*ordinal as u16) + } else { + let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); + tcx.sess + .struct_span_err(attr.span, &msg) + .note("the value may not exceed `u16::MAX`") + .emit(); + None + } + } else { + tcx.sess + .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`") + .note("an unsuffixed integer value, e.g., `1`, is expected") + .emit(); + None + } +} + +fn check_link_name_xor_ordinal( + tcx: TyCtxt<'_>, + codegen_fn_attrs: &CodegenFnAttrs, + inline_span: Option<Span>, +) { + if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { + return; + } + let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; + if let Some(span) = inline_span { + tcx.sess.span_err(span, msg); + } else { + tcx.sess.err(msg); + } +} + +/// Checks the function annotated with `#[target_feature]` is not a safe +/// trait method implementation, reporting an error if it is. +fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let node = tcx.hir().get(hir_id); + if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_item = tcx.hir().expect_item(parent_id.def_id); + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { + tcx.sess + .struct_span_err( + attr_span, + "`#[target_feature(..)]` cannot be applied to safe trait method", + ) + .span_label(attr_span, "cannot be applied to safe trait method") + .span_label(tcx.def_span(id), "not an `unsafe` function") + .emit(); + } + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs new file mode 100644 index 000000000..c7777a946 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -0,0 +1,481 @@ +use crate::middle::resolve_lifetime as rl; +use hir::{ + intravisit::{self, Visitor}, + GenericParamKind, HirId, Node, +}; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::Span; + +pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { + use rustc_hir::*; + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + + let node = tcx.hir().get(hir_id); + let parent_def_id = match node { + Node::ImplItem(_) + | Node::TraitItem(_) + | Node::Variant(_) + | Node::Ctor(..) + | Node::Field(_) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + Some(parent_id.to_def_id()) + } + // FIXME(#43408) always enable this once `lazy_normalization` is + // stable enough and does not need a feature gate anymore. + Node::AnonConst(_) => { + let parent_def_id = tcx.hir().get_parent_item(hir_id); + + let mut in_param_ty = false; + for (_parent, node) in tcx.hir().parent_iter(hir_id) { + if let Some(generics) = node.generics() { + let mut visitor = AnonConstInParamTyDetector { + in_param_ty: false, + found_anon_const_in_param_ty: false, + ct: hir_id, + }; + + visitor.visit_generics(generics); + in_param_ty = visitor.found_anon_const_in_param_ty; + break; + } + } + + if in_param_ty { + // We do not allow generic parameters in anon consts if we are inside + // 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 the def_id we are calling generics_of on is an anon ct default i.e: + // + // struct Foo<const N: usize = { .. }>; + // ^^^ ^ ^^^^^^ def id of this anon const + // ^ ^ param_id + // ^ parent_def_id + // + // then we only want to return generics for params to the left of `N`. If we don't do that we + // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`. + // + // This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as + // we substitute the defaults with the partially built substs when we build the substs. Subst'ing + // the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building. + // + // We fix this by having this function return the parent's generics ourselves and truncating the + // generics to only include non-forward declared params (with the exception of the `Self` ty) + // + // For the above code example that means we want `substs: []` + // For the following struct def we want `substs: [N#0]` when generics_of is called on + // the def id of the `{ N + 1 }` anon const + // struct Foo<const N: usize, const M: usize = { N + 1 }>; + // + // 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]; + // 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 = + params.iter().map(|param| (param.def_id, param.index)).collect(); + + return ty::Generics { + // we set the parent of these generics to be our parent's parent so that we + // dont end up with substs: [N, M, N] for the const default on a struct like this: + // struct Foo<const N: usize, const M: usize = { ... }>; + parent: generics.parent, + parent_count: generics.parent_count, + params, + param_def_id_to_index, + has_self: generics.has_self, + has_late_bound_regions: generics.has_late_bound_regions, + }; + } + + // HACK(eddyb) this provides the correct generics when + // `feature(generic_const_expressions)` is enabled, so that const expressions + // used with const generics, e.g. `Foo<{N+1}>`, can work at all. + // + // Note that we do not supply the parent generics when using + // `min_const_generics`. + Some(parent_def_id.to_def_id()) + } else { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + match parent_node { + // HACK(eddyb) this provides the correct generics for repeat + // expressions' count (i.e. `N` in `[x; N]`), and explicit + // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), + // as they shouldn't be able to cause query cycle errors. + Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + if constant.hir_id() == hir_id => + { + Some(parent_def_id.to_def_id()) + } + Node::Variant(Variant { disr_expr: Some(ref constant), .. }) + if constant.hir_id == hir_id => + { + Some(parent_def_id.to_def_id()) + } + Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { + Some(tcx.typeck_root_def_id(def_id)) + } + // Exclude `GlobalAsm` here which cannot have generics. + Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) + if asm.operands.iter().any(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } + | hir::InlineAsmOperand::SymFn { anon_const } => { + anon_const.hir_id == hir_id + } + _ => false, + }) => + { + Some(parent_def_id.to_def_id()) + } + _ => None, + } + } + } + Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + Some(tcx.typeck_root_def_id(def_id)) + } + Node::Item(item) => match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + in_trait, + .. + }) => { + if in_trait { + assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn)) + } else { + assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn)) + } + Some(fn_def_id.to_def_id()) + } + ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { + let parent_id = tcx.hir().get_parent_item(hir_id); + assert_ne!(parent_id, hir::CRATE_OWNER_ID); + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); + // Opaque types are always nested within another item, and + // inherit the generics of the item. + Some(parent_id.to_def_id()) + } + _ => None, + }, + _ => None, + }; + + enum Defaults { + Allowed, + // See #36887 + FutureCompatDisallowed, + Deny, + } + + let no_generics = hir::Generics::empty(); + let ast_generics = node.generics().unwrap_or(&no_generics); + let (opt_self, allow_defaults) = match node { + Node::Item(item) => { + match item.kind { + ItemKind::Trait(..) | ItemKind::TraitAlias(..) => { + // Add in the self type parameter. + // + // Something of a hack: use the node id for the trait, also as + // the node id for the Self type parameter. + let opt_self = Some(ty::GenericParamDef { + index: 0, + name: kw::SelfUpper, + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { + has_default: false, + synthetic: false, + }, + }); + + (opt_self, Defaults::Allowed) + } + ItemKind::TyAlias(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::OpaqueTy(..) + | ItemKind::Union(..) => (None, Defaults::Allowed), + _ => (None, Defaults::FutureCompatDisallowed), + } + } + + // GATs + Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => { + (None, Defaults::Deny) + } + Node::ImplItem(item) if matches!(item.kind, ImplItemKind::Type(..)) => { + (None, Defaults::Deny) + } + + _ => (None, Defaults::FutureCompatDisallowed), + }; + + let has_self = opt_self.is_some(); + let mut parent_has_self = false; + let mut own_start = has_self as u32; + let parent_count = parent_def_id.map_or(0, |def_id| { + let generics = tcx.generics_of(def_id); + assert!(!has_self); + parent_has_self = generics.has_self; + own_start = generics.count() as u32; + generics.parent_count + generics.params.len() + }); + + let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize); + + if let Some(opt_self) = opt_self { + params.push(opt_self); + } + + let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, ast_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(), + pure_wrt_drop: param.pure_wrt_drop, + kind: ty::GenericParamDefKind::Lifetime, + })); + + // Now create the real type and const parameters. + let type_start = own_start - has_self as u32 + params.len() as u32; + let mut i = 0; + let mut next_index = || { + let prev = i; + i += 1; + prev as u32 + type_start + }; + + const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \ + `struct`, `enum`, `type`, or `trait` definitions"; + + params.extend(ast_generics.params.iter().filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => None, + GenericParamKind::Type { ref default, synthetic, .. } => { + if default.is_some() { + match allow_defaults { + Defaults::Allowed => {} + Defaults::FutureCompatDisallowed + if tcx.features().default_type_parameter_fallback => {} + Defaults::FutureCompatDisallowed => { + tcx.struct_span_lint_hir( + lint::builtin::INVALID_TYPE_PARAM_DEFAULT, + param.hir_id, + param.span, + TYPE_DEFAULT_NOT_ALLOWED, + |lint| lint, + ); + } + Defaults::Deny => { + tcx.sess.span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED); + } + } + } + + let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }; + + Some(ty::GenericParamDef { + index: next_index(), + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind, + }) + } + GenericParamKind::Const { default, .. } => { + if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() { + tcx.sess.span_err( + param.span, + "defaults for const parameters are only allowed in \ + `struct`, `enum`, `type`, or `trait` definitions", + ); + } + + Some(ty::GenericParamDef { + index: next_index(), + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind: ty::GenericParamDefKind::Const { has_default: default.is_some() }, + }) + } + })); + + // provide junk type parameter defs - the only place that + // cares about anything but the length is instantiation, + // and we don't do that for closures. + if let Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }), + .. + }) = node + { + let dummy_args = if gen.is_some() { + &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..] + } else { + &["<closure_kind>", "<closure_signature>", "<upvars>"][..] + }; + + params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef { + index: next_index(), + name: Symbol::intern(arg), + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, + })); + } + + // provide junk type parameter defs for const blocks. + if let Node::AnonConst(_) = node { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { + params.push(ty::GenericParamDef { + index: next_index(), + name: Symbol::intern("<const_ty>"), + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, + }); + } + } + + let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); + + ty::Generics { + parent: parent_def_id, + parent_count, + params, + param_def_id_to_index, + has_self: has_self || parent_has_self, + has_late_bound_regions: has_late_bound_regions(tcx, node), + } +} + +fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> { + struct LateBoundRegionsDetector<'tcx> { + tcx: TyCtxt<'tcx>, + outer_index: ty::DebruijnIndex, + has_late_bound_regions: Option<Span>, + } + + impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + if self.has_late_bound_regions.is_some() { + return; + } + match ty.kind { + hir::TyKind::BareFn(..) => { + self.outer_index.shift_in(1); + intravisit::walk_ty(self, ty); + self.outer_index.shift_out(1); + } + _ => intravisit::walk_ty(self, ty), + } + } + + fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) { + if self.has_late_bound_regions.is_some() { + return; + } + self.outer_index.shift_in(1); + intravisit::walk_poly_trait_ref(self, tr); + self.outer_index.shift_out(1); + } + + fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { + if self.has_late_bound_regions.is_some() { + return; + } + + match self.tcx.named_region(lt.hir_id) { + 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); + } + } + } + } + + fn has_late_bound_regions<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx hir::Generics<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + ) -> Option<Span> { + let mut visitor = LateBoundRegionsDetector { + tcx, + outer_index: ty::INNERMOST, + has_late_bound_regions: None, + }; + for param in generics.params { + if let GenericParamKind::Lifetime { .. } = param.kind { + if tcx.is_late_bound(param.hir_id) { + return Some(param.span); + } + } + } + visitor.visit_fn_decl(decl); + visitor.has_late_bound_regions + } + + match node { + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Fn(ref sig, _) => { + has_late_bound_regions(tcx, &item.generics, sig.decl) + } + _ => None, + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Fn(ref sig, _) => { + has_late_bound_regions(tcx, &item.generics, sig.decl) + } + _ => None, + }, + Node::ForeignItem(item) => match item.kind { + hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => { + has_late_bound_regions(tcx, generics, fn_decl) + } + _ => None, + }, + Node::Item(item) => match item.kind { + hir::ItemKind::Fn(ref sig, .., ref generics, _) => { + has_late_bound_regions(tcx, generics, sig.decl) + } + _ => None, + }, + _ => None, + } +} + +struct AnonConstInParamTyDetector { + in_param_ty: bool, + found_anon_const_in_param_ty: bool, + ct: HirId, +} + +impl<'v> Visitor<'v> for AnonConstInParamTyDetector { + fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { + if let GenericParamKind::Const { ty, default: _ } = p.kind { + let prev = self.in_param_ty; + self.in_param_ty = true; + self.visit_ty(ty); + self.in_param_ty = prev; + } + } + + fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { + if self.in_param_ty && self.ct == c.hir_id { + self.found_anon_const_in_param_ty = true; + } else { + intravisit::walk_anon_const(self, c) + } + } +} diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0d2b75d33..0d34a8bfe 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -53,20 +53,28 @@ fn associated_type_bounds<'tcx>( /// impl trait it isn't possible to write a suitable predicate on the /// containing function and for type-alias impl trait we don't have a backwards /// compatibility issue. +#[instrument(level = "trace", skip(tcx), ret)] fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: DefId, ast_bounds: &'tcx [hir::GenericBound<'tcx>], span: Span, + in_trait: bool, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { ty::print::with_no_queries!({ - let item_ty = - tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id)); + let substs = InternalSubsts::identity_for_item(tcx, opaque_def_id); + let item_ty = if in_trait { + tcx.mk_projection(opaque_def_id, substs) + } else { + tcx.mk_opaque(opaque_def_id, substs) + }; let icx = ItemCtxt::new(tcx, opaque_def_id); let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds); // Opaque types are implicitly sized unless a `?Sized` bound is found <dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span); + debug!(?bounds); + tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty)) }) } @@ -83,10 +91,10 @@ pub(super) fn explicit_item_bounds( .. }) => associated_type_bounds(tcx, def_id, bounds, *span), hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }), + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }), span, .. - }) => opaque_type_bounds(tcx, def_id, bounds, *span), + }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait), _ => bug!("item_bounds called on {:?}", def_id), } } diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs new file mode 100644 index 000000000..3f263a6de --- /dev/null +++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs @@ -0,0 +1,1888 @@ +//! Resolution of early vs late bound lifetimes. +//! +//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this +//! information, typechecking needs to transform the lifetime parameters into bound lifetimes. +//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit +//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file +//! is also responsible for assigning their semantics to implicit lifetimes in trait objects. + +use rustc_ast::walk_list; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +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_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 late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); + + fn id(&self) -> Option<DefId>; + + fn shifted(self, amount: u32) -> Region; +} + +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 late(idx: u32, hir_map: Map<'_>, 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, + ); + (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) + } + + fn id(&self) -> Option<DefId> { + match *self { + Region::Static => None, + + Region::EarlyBound(id) | Region::LateBound(_, _, id) | Region::Free(_, id) => Some(id), + } + } + + fn shifted(self, amount: u32) -> Region { + match self { + Region::LateBound(debruijn, idx, id) => { + Region::LateBound(debruijn.shifted_in(amount), idx, id) + } + _ => self, + } + } +} + +/// Maps the id of each lifetime reference to the lifetime decl +/// that it corresponds to. +/// +/// FIXME. This struct gets converted to a `ResolveLifetimes` for +/// actual use. It has the same data, but indexed by `LocalDefId`. This +/// is silly. +#[derive(Debug, Default)] +struct NamedRegionMap { + // maps from every use of a named (not anonymous) lifetime to a + // `Region` describing how that region is bound + defs: HirIdMap<Region>, + + // Maps relevant hir items to the bound vars on them. These include: + // - function defs + // - function pointers + // - closures + // - trait refs + // - bound types (like `T` in `for<'a> T<'a>: Foo`) + late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>, +} + +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)] +enum Scope<'a> { + /// Declares lifetimes, and each can be early-bound or late-bound. + /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and + /// it should be shifted by the number of `Binder`s in between the + /// declaration `Binder` and the location it's referenced from. + Binder { + /// We use an IndexMap here because we want these lifetimes in order + /// for diagnostics. + lifetimes: FxIndexMap<LocalDefId, Region>, + + scope_type: BinderScopeType, + + /// The late bound vars for a given item are stored by `HirId` to be + /// queried later. However, if we enter an elision scope, we have to + /// later append the elided bound vars to the list and need to know what + /// to append to. + hir_id: hir::HirId, + + s: ScopeRef<'a>, + + /// If this binder comes from a where clause, specify how it was created. + /// This is used to diagnose inaccessible lifetimes in APIT: + /// ```ignore (illustrative) + /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {} + /// ``` + where_bound_origin: Option<hir::PredicateOrigin>, + }, + + /// Lifetimes introduced by a fn are scoped to the call-site for that fn, + /// if this is a fn body, otherwise the original definitions are used. + /// Unspecified lifetimes are inferred, unless an elision scope is nested, + /// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`. + Body { + id: hir::BodyId, + s: ScopeRef<'a>, + }, + + /// A scope which either determines unspecified lifetimes or errors + /// on them (e.g., due to ambiguity). + Elision { + s: ScopeRef<'a>, + }, + + /// Use a specific lifetime (if `Some`) or leave it unset (to be + /// inferred in a function body or potentially error outside one), + /// for the default choice of lifetime in a trait object type. + ObjectLifetimeDefault { + lifetime: Option<Region>, + s: ScopeRef<'a>, + }, + + /// When we have nested trait refs, we concatenate late bound vars for inner + /// trait refs from outer ones. But we also need to include any HRTB + /// lifetimes encountered when identifying the trait that an associated type + /// is declared on. + Supertrait { + lifetimes: Vec<ty::BoundVariableKind>, + s: ScopeRef<'a>, + }, + + TraitRefBoundary { + s: ScopeRef<'a>, + }, + + Root, +} + +#[derive(Copy, Clone, Debug)] +enum BinderScopeType { + /// Any non-concatenating binder scopes. + Normal, + /// Within a syntactic trait ref, there may be multiple poly trait refs that + /// are nested (under the `associated_type_bounds` feature). The binders of + /// the inner poly trait refs are extended from the outer poly trait refs + /// and don't increase the late bound depth. If you had + /// `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope + /// would be `Concatenating`. This also used in trait refs in where clauses + /// where we have two binders `for<> T: for<> Foo` (I've intentionally left + /// out any lifetimes because they aren't needed to show the two scopes). + /// The inner `for<>` has a scope of `Concatenating`. + Concatenating, +} + +// A helper struct for debugging scopes without printing parent scopes +struct TruncatedScopeDebug<'a>(&'a Scope<'a>); + +impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Scope::Binder { lifetimes, scope_type, hir_id, where_bound_origin, s: _ } => f + .debug_struct("Binder") + .field("lifetimes", lifetimes) + .field("scope_type", scope_type) + .field("hir_id", hir_id) + .field("where_bound_origin", where_bound_origin) + .field("s", &"..") + .finish(), + Scope::Body { id, s: _ } => { + f.debug_struct("Body").field("id", id).field("s", &"..").finish() + } + Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), + Scope::ObjectLifetimeDefault { lifetime, s: _ } => f + .debug_struct("ObjectLifetimeDefault") + .field("lifetime", lifetime) + .field("s", &"..") + .finish(), + Scope::Supertrait { lifetimes, s: _ } => f + .debug_struct("Supertrait") + .field("lifetimes", lifetimes) + .field("s", &"..") + .finish(), + Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Scope::Root => f.debug_struct("Root").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), + is_late_bound_map, + object_lifetime_default, + late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, 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); + 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, + }; + visitor.visit_item(item); + + named_region_map +} + +fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes { + let mut rl = ResolveLifetimes::default(); + + for (hir_id, v) in named_region_map.defs { + let map = rl.defs.entry(hir_id.owner).or_default(); + map.insert(hir_id.local_id, v); + } + for (hir_id, v) in named_region_map.late_bound_vars { + let map = rl.late_bound_vars.entry(hir_id.owner).or_default(); + map.insert(hir_id.local_id, v); + } + + debug!(?rl.defs); + debug!(?rl.late_bound_vars); + 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) => { + let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); + ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) + } + _ => bug!("{:?} is not a late region", region), + } +} + +impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { + /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. + fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) { + let mut scope = self.scope; + let mut supertrait_lifetimes = vec![]; + loop { + match scope { + Scope::Body { .. } | Scope::Root => { + break (vec![], BinderScopeType::Normal); + } + + Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + scope = s; + } + + Scope::Supertrait { s, lifetimes } => { + supertrait_lifetimes = lifetimes.clone(); + scope = s; + } + + Scope::TraitRefBoundary { .. } => { + // We should only see super trait lifetimes if there is a `Binder` above + assert!(supertrait_lifetimes.is_empty()); + break (vec![], BinderScopeType::Normal); + } + + Scope::Binder { hir_id, .. } => { + // Nested poly trait refs have the binders concatenated + let mut full_binders = + self.map.late_bound_vars.entry(*hir_id).or_default().clone(); + full_binders.extend(supertrait_lifetimes.into_iter()); + break (full_binders, BinderScopeType::Concatenating); + } + } + } + } +} +impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + 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| { + this.visit_body(body); + }); + } + + fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Closure(hir::Closure { + binder, bound_generic_params, fn_decl, .. + }) = e.kind + { + if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { + fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> { + struct V(Option<Span>); + + impl<'v> Visitor<'v> for V { + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + match t.kind { + _ if self.0.is_some() => (), + hir::TyKind::Infer => { + self.0 = Some(t.span); + } + _ => intravisit::walk_ty(self, t), + } + } + } + + let mut v = V(None); + v.visit_ty(ty); + v.0 + } + + let infer_in_rt_sp = match fn_decl.output { + hir::FnRetTy::DefaultReturn(sp) => Some(sp), + hir::FnRetTy::Return(ty) => span_of_infer(ty), + }; + + let infer_spans = fn_decl + .inputs + .into_iter() + .filter_map(span_of_infer) + .chain(infer_in_rt_sp) + .collect::<Vec<_>>(); + + if !infer_spans.is_empty() { + self.tcx.sess + .struct_span_err( + infer_spans, + "implicit types in closure signatures are forbidden when `for<...>` is present", + ) + .span_label(for_sp, "`for<...>` is here") + .emit(); + } + } + + let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = + bound_generic_params + .iter() + .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 r = late_region_as_bound_region(self.tcx, &pair.1); + (pair, r) + }) + .unzip(); + + self.record_late_bound_vars(e.hir_id, binders); + let scope = Scope::Binder { + hir_id: e.hir_id, + lifetimes, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + + self.with(scope, |this| { + // a closure has no bounds, so everything + // contained within is scoped within its binder. + intravisit::walk_expr(this, e) + }); + } else { + intravisit::walk_expr(self, e) + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + match &item.kind { + hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => { + if let Some(of_trait) = of_trait { + self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default()); + } + } + _ => {} + } + match item.kind { + hir::ItemKind::Fn(_, ref generics, _) => { + self.visit_early_late(item.hir_id(), generics, |this| { + intravisit::walk_item(this, item); + }); + } + + hir::ItemKind::ExternCrate(_) + | hir::ItemKind::Use(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::GlobalAsm(..) => { + // These sorts of items have no lifetime parameters at all. + intravisit::walk_item(self, item); + } + hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { + // No lifetime parameters, but implied 'static. + self.with(Scope::Elision { s: self.scope }, |this| { + intravisit::walk_item(this, item) + }); + } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { + // Opaque types are visited when we visit the + // `TyKind::OpaqueDef`, so that they have the lifetimes from + // their parent opaque_ty in scope. + // + // The core idea here is that since OpaqueTys are generated with the impl Trait as + // 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; + } + hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"), + _ => {} + } + } + } + hir::ItemKind::TyAlias(_, ref generics) + | hir::ItemKind::Enum(_, ref generics) + | hir::ItemKind::Struct(_, ref generics) + | hir::ItemKind::Union(_, ref generics) + | hir::ItemKind::Trait(_, _, ref generics, ..) + | hir::ItemKind::TraitAlias(ref generics, ..) + | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { + // These kinds of items have only early-bound lifetime parameters. + let lifetimes = generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + Some(Region::early(self.tcx.hir(), param)) + } + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, + }) + .collect(); + self.record_late_bound_vars(item.hir_id(), vec![]); + let scope = Scope::Binder { + hir_id: item.hir_id(), + lifetimes, + scope_type: BinderScopeType::Normal, + s: ROOT_SCOPE, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + intravisit::walk_item(this, item); + }); + }); + } + } + } + + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { + match item.kind { + hir::ForeignItemKind::Fn(_, _, ref generics) => { + self.visit_early_late(item.hir_id(), generics, |this| { + intravisit::walk_foreign_item(this, item); + }) + } + hir::ForeignItemKind::Static(..) => { + intravisit::walk_foreign_item(self, item); + } + hir::ForeignItemKind::Type => { + intravisit::walk_foreign_item(self, item); + } + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + match ty.kind { + hir::TyKind::BareFn(ref c) => { + let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c + .generic_params + .iter() + .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 r = late_region_as_bound_region(self.tcx, &pair.1); + (pair, r) + }) + .unzip(); + self.record_late_bound_vars(ty.hir_id, binders); + 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| { + // a bare fn has no bounds, so everything + // contained within is scoped within its binder. + intravisit::walk_ty(this, ty); + }); + } + hir::TyKind::TraitObject(bounds, ref lifetime, _) => { + debug!(?bounds, ?lifetime, "TraitObject"); + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |this| { + for bound in bounds { + this.visit_poly_trait_ref(bound); + } + }); + match lifetime.name { + LifetimeName::ImplicitObjectLifetimeDefault => { + // If the user does not write *anything*, we + // use the object lifetime defaulting + // rules. So e.g., `Box<dyn Debug>` becomes + // `Box<dyn Debug + 'static>`. + self.resolve_object_lifetime_default(lifetime) + } + LifetimeName::Infer => { + // If the user writes `'_`, we use the *ordinary* elision + // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be + // resolved the same as the `'_` in `&'_ Foo`. + // + // cc #48468 + } + LifetimeName::Param(..) | LifetimeName::Static => { + // If the user wrote an explicit name, use that. + self.visit_lifetime(lifetime); + } + LifetimeName::Error => {} + } + } + hir::TyKind::Rptr(ref lifetime_ref, ref mt) => { + self.visit_lifetime(lifetime_ref); + let scope = Scope::ObjectLifetimeDefault { + lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(), + s: self.scope, + }; + self.with(scope, |this| this.visit_ty(&mt.ty)); + } + hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => { + // Resolve the lifetimes in the bounds to the lifetime defs in the generics. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `type MyAnonTy<'b> = impl MyTrait<'b>;` + // ^ ^ 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 { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias, + .. + }) => { + intravisit::walk_ty(self, ty); + + // Elided lifetimes are not allowed in non-return + // position impl Trait + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |this| { + let scope = Scope::Elision { s: this.scope }; + this.with(scope, |this| { + intravisit::walk_item(this, opaque_ty); + }) + }); + + return; + } + 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), + }; + + // Resolve the lifetimes that are applied to the opaque type. + // These are resolved in the current scope. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `fn foo<'a>() -> MyAnonTy<'a> { ... }` + // ^ ^this gets resolved in the current scope + for lifetime in lifetimes { + let hir::GenericArg::Lifetime(lifetime) = lifetime else { + continue + }; + self.visit_lifetime(lifetime); + + // Check for predicates like `impl for<'a> Trait<impl OtherTrait<'a>>` + // and ban them. Type variables instantiated inside binders aren't + // well-supported at the moment, so this doesn't work. + // In the future, this should be fixed and this error should be removed. + let def = self.map.defs.get(&lifetime.hir_id).cloned(); + let Some(Region::LateBound(_, _, def_id)) = def else { + continue + }; + let Some(def_id) = def_id.as_local() else { + continue + }; + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + // 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(); + } + 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(); + } + 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), + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + use self::hir::TraitItemKind::*; + match trait_item.kind { + Fn(_, _) => { + self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| { + intravisit::walk_trait_item(this, trait_item) + }); + } + Type(bounds, ref ty) => { + let generics = &trait_item.generics; + let lifetimes = generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + Some(Region::early(self.tcx.hir(), param)) + } + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, + }) + .collect(); + self.record_late_bound_vars(trait_item.hir_id(), vec![]); + let scope = Scope::Binder { + hir_id: trait_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| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + if let Some(ty) = ty { + this.visit_ty(ty); + } + }) + }); + } + Const(_, _) => { + // Only methods and types support generics. + assert!(trait_item.generics.params.is_empty()); + intravisit::walk_trait_item(self, trait_item); + } + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + use self::hir::ImplItemKind::*; + match impl_item.kind { + Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| { + intravisit::walk_impl_item(this, impl_item) + }), + Type(ref ty) => { + let generics = &impl_item.generics; + let lifetimes: FxIndexMap<LocalDefId, Region> = generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + Some(Region::early(self.tcx.hir(), param)) + } + GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None, + }) + .collect(); + self.record_late_bound_vars(impl_item.hir_id(), vec![]); + let scope = Scope::Binder { + hir_id: impl_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| { + this.visit_generics(generics); + this.visit_ty(ty); + }) + }); + } + Const(_, _) => { + // Only methods and types support generics. + assert!(impl_item.generics.params.is_empty()); + intravisit::walk_impl_item(self, impl_item); + } + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { + match lifetime_ref.name { + hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), + 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`. + hir::LifetimeName::Error => {} + // Those will be resolved by typechecking. + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} + } + } + + fn visit_path(&mut self, path: &'tcx 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 { + self.visit_segment_args(path.res, depth, args); + } + } + } + + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _: Span, + _: hir::HirId, + ) { + let output = match fd.output { + hir::FnRetTy::DefaultReturn(_) => None, + hir::FnRetTy::Return(ref ty) => Some(&**ty), + }; + self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); + intravisit::walk_fn_kind(self, fk); + self.visit_nested_body(body_id) + } + + fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |this| { + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => {} + GenericParamKind::Type { ref default, .. } => { + if let Some(ref ty) = default { + this.visit_ty(&ty); + } + } + GenericParamKind::Const { ref ty, default } => { + this.visit_ty(&ty); + if let Some(default) = default { + this.visit_body(this.tcx.hir().body(default.body)); + } + } + } + } + for predicate in generics.predicates { + match predicate { + &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + hir_id, + ref bounded_ty, + bounds, + ref bound_generic_params, + origin, + .. + }) => { + let lifetimes: FxIndexMap<LocalDefId, Region> = + bound_generic_params + .iter() + .filter(|param| { + matches!(param.kind, GenericParamKind::Lifetime { .. }) + }) + .enumerate() + .map(|(late_bound_idx, param)| { + Region::late(late_bound_idx as u32, this.tcx.hir(), param) + }) + .collect(); + let binders: Vec<_> = + lifetimes + .iter() + .map(|(_, region)| { + late_region_as_bound_region(this.tcx, region) + }) + .collect(); + this.record_late_bound_vars(hir_id, binders.clone()); + // Even if there are no lifetimes defined here, we still wrap it in a binder + // scope. If there happens to be a nested poly trait ref (an error), that + // will be `Concatenating` anyways, so we don't have to worry about the depth + // being wrong. + let scope = Scope::Binder { + hir_id, + lifetimes, + s: this.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: Some(origin), + }; + this.with(scope, |this| { + this.visit_ty(&bounded_ty); + walk_list!(this, visit_param_bound, bounds); + }) + } + &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + ref lifetime, + bounds, + .. + }) => { + this.visit_lifetime(lifetime); + walk_list!(this, visit_param_bound, bounds); + + if lifetime.name != hir::LifetimeName::Static { + for bound in bounds { + let hir::GenericBound::Outlives(ref lt) = bound else { + continue; + }; + if lt.name != hir::LifetimeName::Static { + continue; + } + this.insert_lifetime(lt, Region::Static); + this.tcx + .sess + .struct_span_warn( + lifetime.span, + &format!( + "unnecessary lifetime parameter `{}`", + lifetime.name.ident(), + ), + ) + .help(&format!( + "you can use the `'static` lifetime directly, in place of `{}`", + lifetime.name.ident(), + )) + .emit(); + } + } + } + &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + ref lhs_ty, + ref rhs_ty, + .. + }) => { + this.visit_ty(lhs_ty); + this.visit_ty(rhs_ty); + } + } + } + }) + } + + fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) { + match bound { + hir::GenericBound::LangItemTrait(_, _, hir_id, _) => { + // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go + // through the regular poly trait ref code, so we don't get another + // chance to introduce a binder. For now, I'm keeping the existing logic + // of "if there isn't a Binder scope above us, add one", but I + // imagine there's a better way to go about this. + let (binders, scope_type) = self.poly_trait_ref_binder_info(); + + self.record_late_bound_vars(*hir_id, binders); + let scope = Scope::Binder { + hir_id: *hir_id, + lifetimes: FxIndexMap::default(), + s: self.scope, + scope_type, + where_bound_origin: None, + }; + self.with(scope, |this| { + intravisit::walk_param_bound(this, bound); + }); + } + _ => intravisit::walk_param_bound(self, bound), + } + } + + fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) { + debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); + + let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); + + let initial_bound_vars = binders.len() as u32; + let mut lifetimes: FxIndexMap<LocalDefId, Region> = FxIndexMap::default(); + let binders_iter = trait_ref + .bound_generic_params + .iter() + .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 r = late_region_as_bound_region(self.tcx, &pair.1); + lifetimes.insert(pair.0, pair.1); + r + }); + binders.extend(binders_iter); + + debug!(?binders); + self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders); + + // Always introduce a scope here, even if this is in a where clause and + // we introduced the binders around the bounded Ty. In that case, we + // just reuse the concatenation functionality also present in nested trait + // refs. + let scope = Scope::Binder { + hir_id: trait_ref.trait_ref.hir_ref_id, + lifetimes, + s: self.scope, + scope_type, + where_bound_origin: None, + }; + self.with(scope, |this| { + walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); + this.visit_trait_ref(&trait_ref.trait_ref); + }); + } +} + +fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> ObjectLifetimeDefault { + debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam); + let param_def_id = param_def_id.expect_local(); + let parent_def_id = tcx.local_parent(param_def_id); + let generics = tcx.hir().get_generics(parent_def_id).unwrap(); + let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); + let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); + + // Scan the bounds and where-clauses on parameters to extract bounds + // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` + // for each type parameter. + match param.kind { + GenericParamKind::Type { .. } => { + let mut set = Set1::Empty; + + // Look for `type: ...` where clauses. + for bound in generics.bounds_for_param(param_def_id) { + // Ignore `for<'a> type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if !bound.bound_generic_params.is_empty() { + continue; + } + + for bound in bound.bounds { + if let hir::GenericBound::Outlives(ref lifetime) = *bound { + set.insert(lifetime.name.normalize_to_macros_2_0()); + } + } + } + + match set { + Set1::Empty => ObjectLifetimeDefault::Empty, + Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeName::Param(param_def_id, _)) => { + ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + } + _ => ObjectLifetimeDefault::Ambiguous, + } + } + _ => { + bug!("object_lifetime_default_raw must only be called on a type parameter") + } + } +} + +impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { + fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F) + where + 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 span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); + { + let _enter = span.enter(); + f(&mut this); + } + } + + fn record_late_bound_vars(&mut self, hir_id: hir::HirId, binder: Vec<ty::BoundVariableKind>) { + if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) { + bug!( + "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", + self.map.late_bound_vars[&hir_id] + ) + } + } + + /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. + /// + /// Handles visiting fns and methods. These are a bit complicated because we must distinguish + /// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear + /// within type bounds; those are early bound lifetimes, and the rest are late bound. + /// + /// For example: + /// + /// fn foo<'a,'b,'c,T:Trait<'b>>(...) + /// + /// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound + /// lifetimes may be interspersed together. + /// + /// If early bound lifetimes are present, we separate them into their own list (and likewise + /// for late bound). They will be numbered sequentially, starting from the lowest index that is + /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late + /// bound lifetimes are resolved by name and associated with a binder ID (`binder_id`), so the + /// ordering is not important there. + fn visit_early_late<F>( + &mut self, + hir_id: hir::HirId, + generics: &'tcx hir::Generics<'tcx>, + walk: F, + ) where + F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>), + { + let mut named_late_bound_vars = 0; + let lifetimes: FxIndexMap<LocalDefId, Region> = generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + 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)) + } else { + Some(Region::early(self.tcx.hir(), param)) + } + } + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, + }) + .collect(); + + let binders: Vec<_> = generics + .params + .iter() + .filter(|param| { + matches!(param.kind, GenericParamKind::Lifetime { .. }) + && self.tcx.is_late_bound(param.hir_id) + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); + late_region_as_bound_region(self.tcx, &pair.1) + }) + .collect(); + self.record_late_bound_vars(hir_id, binders); + let scope = Scope::Binder { + hir_id, + lifetimes, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, walk); + } + + #[instrument(level = "debug", skip(self))] + fn resolve_lifetime_ref( + &mut self, + region_def_id: LocalDefId, + lifetime_ref: &'tcx hir::Lifetime, + ) { + // Walk up the scope chain, tracking the number of fn scopes + // that we pass through, until we find a lifetime with the + // given name or we run out of scopes. + // search. + let mut late_depth = 0; + let mut scope = self.scope; + let mut outermost_body = None; + let result = loop { + match *scope { + Scope::Body { id, s } => { + outermost_body = Some(id); + scope = s; + } + + Scope::Root => { + break None; + } + + Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => { + if let Some(&def) = lifetimes.get(®ion_def_id) { + break Some(def.shifted(late_depth)); + } + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } + // 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::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, + "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 => { } + } + + diag.emit(); + return; + } + scope = s; + } + + Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + } + }; + + if let Some(mut def) = result { + if let Region::EarlyBound(..) = def { + // Do not free early-bound regions, only late-bound ones. + } 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 { + kind: hir::TraitItemKind::Fn(..), .. + }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { + let scope = self.tcx.hir().local_def_id(fn_id); + def = Region::Free(scope.to_def_id(), def.id().unwrap()); + } + _ => {} + } + } + + self.insert_lifetime(lifetime_ref, def); + return; + } + + // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT. + // AST-based resolution does not care for impl-trait desugaring, which are the + // responibility of lowering. This may create a mismatch between the resolution + // AST found (`region_def_id`) which points to HRTB, and what HIR allows. + // ``` + // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {} + // ``` + // + // In such case, walk back the binders to diagnose it properly. + let mut scope = self.scope; + loop { + match *scope { + Scope::Binder { + where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), .. + } => { + let mut err = self.tcx.sess.struct_span_err( + lifetime_ref.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::Binder { s, .. } + | Scope::Body { s, .. } + | Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + } + } + + self.tcx.sess.delay_span_bug( + lifetime_ref.span, + &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,), + ); + } + + #[instrument(level = "debug", skip(self))] + fn visit_segment_args( + &mut self, + res: Res, + depth: usize, + generic_args: &'tcx hir::GenericArgs<'tcx>, + ) { + if generic_args.parenthesized { + self.visit_fn_like_elision( + generic_args.inputs(), + Some(generic_args.bindings[0].ty()), + false, + ); + return; + } + + for arg in generic_args.args { + if let hir::GenericArg::Lifetime(lt) = arg { + self.visit_lifetime(lt); + } + } + + // Figure out if this is a type/trait segment, + // which requires object lifetime defaults. + let type_def_id = match res { + Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)), + Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)), + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait, + def_id, + ) if depth == 0 => Some(def_id), + _ => None, + }; + + debug!(?type_def_id); + + // Compute a vector of defaults, one for each type parameter, + // per the rules given in RFCs 599 and 1156. Example: + // + // ```rust + // struct Foo<'a, T: 'a, U> { } + // ``` + // + // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default + // `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound) + // and `dyn Baz` to `dyn Baz + 'static` (because there is no + // such bound). + // + // Therefore, we would compute `object_lifetime_defaults` to a + // vector like `['x, 'static]`. Note that the vector only + // includes type parameters. + let object_lifetime_defaults = type_def_id.map_or_else(Vec::new, |def_id| { + let in_body = { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root => break false, + + Scope::Body { .. } => break true, + + Scope::Binder { s, .. } + | Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + } + } + }; + + let map = &self.map; + let generics = self.tcx.generics_of(def_id); + + // `type_def_id` points to an item, so there is nothing to inherit generics from. + debug_assert_eq!(generics.parent_count, 0); + + let set_to_region = |set: ObjectLifetimeDefault| match set { + ObjectLifetimeDefault::Empty => { + if in_body { + None + } else { + Some(Region::Static) + } + } + ObjectLifetimeDefault::Static => Some(Region::Static), + ObjectLifetimeDefault::Param(param_def_id) => { + // This index can be used with `generic_args` since `parent_count == 0`. + let index = generics.param_def_id_to_index[¶m_def_id] as usize; + generic_args.args.get(index).and_then(|arg| match arg { + GenericArg::Lifetime(lt) => map.defs.get(<.hir_id).copied(), + _ => None, + }) + } + ObjectLifetimeDefault::Ambiguous => None, + }; + generics + .params + .iter() + .filter_map(|param| { + match self.tcx.def_kind(param.def_id) { + // Generic consts don't impose any constraints. + // + // We still store a dummy value here to allow generic parameters + // in an arbitrary order. + DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty), + DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)), + // We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter + // works. Ignore it because it can't have a meaningful lifetime default. + DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None, + dk => bug!("unexpected def_kind {:?}", dk), + } + }) + .map(set_to_region) + .collect() + }); + + debug!(?object_lifetime_defaults); + + let mut i = 0; + for arg in generic_args.args { + match arg { + GenericArg::Lifetime(_) => {} + GenericArg::Type(ty) => { + if let Some(<) = object_lifetime_defaults.get(i) { + let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope }; + self.with(scope, |this| this.visit_ty(ty)); + } else { + self.visit_ty(ty); + } + i += 1; + } + GenericArg::Const(ct) => { + self.visit_anon_const(&ct.value); + i += 1; + } + GenericArg::Infer(inf) => { + self.visit_id(inf.hir_id); + i += 1; + } + } + } + + // Hack: when resolving the type `XX` in binding like `dyn + // Foo<'b, Item = XX>`, the current object-lifetime default + // would be to examine the trait `Foo` to check whether it has + // a lifetime bound declared on `Item`. e.g., if `Foo` is + // declared like so, then the default object lifetime bound in + // `XX` should be `'b`: + // + // ```rust + // trait Foo<'a> { + // type Item: 'a; + // } + // ``` + // + // but if we just have `type Item;`, then it would be + // `'static`. However, we don't get all of this logic correct. + // + // Instead, we do something hacky: if there are no lifetime parameters + // to the trait, then we simply use a default object lifetime + // bound of `'static`, because there is no other possibility. On the other hand, + // if there ARE lifetime parameters, then we require the user to give an + // explicit bound for now. + // + // This is intended to leave room for us to implement the + // correct behavior in the future. + let has_lifetime_parameter = + generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); + + // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or + // in the trait ref `YY<...>` in `Item: YY<...>`. + for binding in generic_args.bindings { + let scope = Scope::ObjectLifetimeDefault { + lifetime: if has_lifetime_parameter { None } else { Some(Region::Static) }, + s: self.scope, + }; + if let Some(type_def_id) = type_def_id { + let lifetimes = LifetimeContext::supertrait_hrtb_lifetimes( + self.tcx, + type_def_id, + binding.ident, + ); + self.with(scope, |this| { + let scope = Scope::Supertrait { + lifetimes: lifetimes.unwrap_or_default(), + s: this.scope, + }; + this.with(scope, |this| this.visit_assoc_type_binding(binding)); + }); + } else { + self.with(scope, |this| this.visit_assoc_type_binding(binding)); + } + } + } + + /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the + /// associated type name and starting trait. + /// For example, imagine we have + /// ```ignore (illustrative) + /// trait Foo<'a, 'b> { + /// type As; + /// } + /// trait Bar<'b>: for<'a> Foo<'a, 'b> {} + /// trait Bar: for<'b> Bar<'b> {} + /// ``` + /// In this case, if we wanted to the supertrait HRTB lifetimes for `As` on + /// the starting trait `Bar`, we would return `Some(['b, 'a])`. + fn supertrait_hrtb_lifetimes( + tcx: TyCtxt<'tcx>, + def_id: DefId, + assoc_name: Ident, + ) -> Option<Vec<ty::BoundVariableKind>> { + let trait_defines_associated_type_named = |trait_def_id: DefId| { + tcx.associated_items(trait_def_id) + .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id) + .is_some() + }; + + use smallvec::{smallvec, SmallVec}; + let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> = + smallvec![(def_id, smallvec![])]; + let mut visited: FxHashSet<DefId> = FxHashSet::default(); + loop { + let Some((def_id, bound_vars)) = stack.pop() else { + break None; + }; + // See issue #83753. If someone writes an associated type on a non-trait, just treat it as + // there being no supertrait HRTBs. + match tcx.def_kind(def_id) { + DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {} + _ => break None, + } + + if trait_defines_associated_type_named(def_id) { + break Some(bound_vars.into_iter().collect()); + } + let predicates = + tcx.super_predicates_that_define_assoc_type((def_id, Some(assoc_name))); + let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::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(); + all_bound_vars.extend(pred_bound_vars.iter()); + let super_def_id = data.trait_ref.def_id; + Some((super_def_id, all_bound_vars)) + } + _ => None, + } + }); + + let obligations = obligations.filter(|o| visited.insert(o.0)); + stack.extend(obligations); + } + } + + #[instrument(level = "debug", skip(self))] + fn visit_fn_like_elision( + &mut self, + inputs: &'tcx [hir::Ty<'tcx>], + output: Option<&'tcx hir::Ty<'tcx>>, + in_closure: bool, + ) { + self.with(Scope::Elision { s: self.scope }, |this| { + for input in inputs { + this.visit_ty(input); + } + if !in_closure && let Some(output) = output { + this.visit_ty(output); + } + }); + if in_closure && let Some(output) = output { + self.visit_ty(output); + } + } + + fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { + debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref); + let mut late_depth = 0; + let mut scope = self.scope; + let lifetime = loop { + match *scope { + Scope::Binder { s, scope_type, .. } => { + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } + scope = s; + } + + Scope::Root | Scope::Elision { .. } => break Region::Static, + + Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, + + Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l, + + Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + } + }; + self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth)); + } + + #[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) + ); + self.map.defs.insert(lifetime_ref.hir_id, def); + } + + /// Sometimes we resolve a lifetime, but later find that it is an + /// error (esp. around impl trait). In that case, we remove the + /// entry into `map.defs` so as not to confuse later code. + fn uninsert_lifetime_on_error(&mut self, lifetime_ref: &'tcx hir::Lifetime, bad_def: Region) { + let old_value = self.map.defs.remove(&lifetime_ref.hir_id); + assert_eq!(old_value, Some(bad_def)); + } +} + +/// Detects late-bound lifetimes and inserts them into +/// `late_bound`. +/// +/// A region declared on a fn is **late-bound** if: +/// - it is constrained by an argument type; +/// - it does not appear in a where-clause. +/// +/// "Constrained" basically means that it appears in any type but +/// not amongst the inputs to a projection. In other words, `<&'a +/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`. +fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?; + let generics = tcx.hir().get_generics(def_id)?; + + let mut late_bound = FxIndexSet::default(); + + let mut constrained_by_input = ConstrainedCollector::default(); + for arg_ty in decl.inputs { + constrained_by_input.visit_ty(arg_ty); + } + + let mut appears_in_output = AllCollector::default(); + intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output); + + debug!(?constrained_by_input.regions); + + // Walk the lifetimes that appear in where clauses. + // + // Subtle point: because we disallow nested bindings, we can just + // ignore binders here and scrape up all names we see. + let mut appears_in_where_clause = AllCollector::default(); + appears_in_where_clause.visit_generics(generics); + debug!(?appears_in_where_clause.regions); + + // Late bound regions are those that: + // - appear in the inputs + // - do not appear in the where-clauses + // - are not implicitly captured by `impl Trait` + for param in generics.params { + match param.kind { + hir::GenericParamKind::Lifetime { .. } => { /* fall through */ } + + // Neither types nor consts are late-bound. + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => continue, + } + + let param_def_id = tcx.hir().local_def_id(param.hir_id); + + // appears in the where clauses? early-bound. + if appears_in_where_clause.regions.contains(¶m_def_id) { + continue; + } + + // does not appear in the inputs, but appears in the return type? early-bound. + if !constrained_by_input.regions.contains(¶m_def_id) + && appears_in_output.regions.contains(¶m_def_id) + { + continue; + } + + debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id); + + let inserted = late_bound.insert(param_def_id); + assert!(inserted, "visited lifetime {:?} twice", param.hir_id); + } + + debug!(?late_bound); + return Some(tcx.arena.alloc(late_bound)); + + #[derive(Default)] + struct ConstrainedCollector { + regions: FxHashSet<LocalDefId>, + } + + impl<'v> Visitor<'v> for ConstrainedCollector { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + match ty.kind { + hir::TyKind::Path( + hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..), + ) => { + // ignore lifetimes appearing in associated type + // projections, as they are not *constrained* + // (defined above) + } + + 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 + // valid to have them elsewhere, but even if it + // is, those would be potentially inputs to + // projections + if let Some(last_segment) = path.segments.last() { + self.visit_path_segment(last_segment); + } + } + + _ => { + intravisit::walk_ty(self, ty); + } + } + } + + fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { + if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name { + self.regions.insert(def_id); + } + } + } + + #[derive(Default)] + struct AllCollector { + regions: FxHashSet<LocalDefId>, + } + + 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 { + 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 new file mode 100644 index 000000000..2e84e1d01 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -0,0 +1,707 @@ +use crate::astconv::AstConv; +use crate::bounds::Bounds; +use crate::collect::ItemCtxt; +use crate::constrained_generic_params as cgp; +use hir::{HirId, Node}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, DUMMY_SP}; + +#[derive(Debug)] +struct OnlySelfBounds(bool); + +/// Returns a list of all type predicates (explicit and implicit) for the definition with +/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus +/// `Self: Trait` predicates for traits. +pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + let mut result = tcx.predicates_defined_on(def_id); + + if tcx.is_trait(def_id) { + // For traits, add `Self: Trait` predicate. This is + // not part of the predicates that a user writes, but it + // is something that one must prove in order to invoke a + // method or project an associated type. + // + // In the chalk setup, this predicate is not part of the + // "predicates" for a trait item. But it is useful in + // rustc because if you directly (e.g.) invoke a trait + // method like `Trait::method(...)`, you must naturally + // prove that the trait applies to the types that were + // used, and adding the predicate into this list ensures + // that this is done. + // + // We use a DUMMY_SP here as a way to signal trait bounds that come + // from the trait itself that *shouldn't* be shown as the source of + // an obligation and instead be skipped. Otherwise we'd use + // `tcx.def_span(def_id);` + + let constness = if tcx.has_attr(def_id, sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; + + let span = rustc_span::DUMMY_SP; + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( + ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx), + span, + )))); + } + debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); + result +} + +/// Returns a list of user-specified type predicates for the definition with ID `def_id`. +/// N.B., this does not include any implied/inferred constraints. +#[instrument(level = "trace", skip(tcx), ret)] +fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + use rustc_hir::*; + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let node = tcx.hir().get(hir_id); + + let mut is_trait = None; + let mut is_default_impl_trait = None; + + let icx = ItemCtxt::new(tcx, def_id); + + const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty(); + + // We use an `IndexSet` to preserves order of insertion. + // Preserving the order of insertion is important here so as not to break UI tests. + let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); + + let ast_generics = match node { + Node::TraitItem(item) => item.generics, + + 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 + } + 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, + } + } + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Static(..) => NO_GENERICS, + ForeignItemKind::Fn(_, _, ref generics) => *generics, + ForeignItemKind::Type => NO_GENERICS, + }, + + _ => NO_GENERICS, + }; + + let generics = tcx.generics_of(def_id); + let parent_count = generics.parent_count as u32; + let has_own_self = generics.has_self && parent_count == 0; + + // Below we'll consider the bounds on the type parameters (including `Self`) + // and the explicit where-clauses, but to get the full set of predicates + // on a trait we need to add in the supertrait bounds and bounds found on + // associated types. + if let Some(_trait_ref) = is_trait { + predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); + } + + // In default impls, we can assume that the self type implements + // the trait. So in: + // + // default impl Foo for Bar { .. } + // + // we add a default where clause `Foo: Bar`. We do a similar thing for traits + // (see below). Recall that a default impl is not itself an impl, but rather a + // set of defaults that can be incorporated into another impl. + if let Some(trait_ref) = is_default_impl_trait { + predicates.insert((trait_ref.without_const().to_predicate(tcx), tcx.def_span(def_id))); + } + + // Collect the region predicates that were declared inline as + // well. In the case of parameters declared on a fn or method, we + // have to be careful to only iterate over early-bound regions. + let mut index = parent_count + + has_own_self as u32 + + super::early_bound_lifetimes_from_generics(tcx, ast_generics).count() as u32; + + trace!(?predicates); + trace!(?ast_generics); + + // Collect the predicates that were written inline by the user on each + // type parameter (e.g., `<T: Foo>`). + for param in ast_generics.params { + match param.kind { + // We already dealt with early bound lifetimes above. + GenericParamKind::Lifetime { .. } => (), + GenericParamKind::Type { .. } => { + let name = param.name.ident().name; + let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); + index += 1; + + let mut bounds = Bounds::default(); + // Params are implicitly sized unless a `?Sized` bound is found + <dyn AstConv<'_>>::add_implicitly_sized( + &icx, + &mut bounds, + &[], + Some((param.hir_id, ast_generics.predicates)), + param.span, + ); + trace!(?bounds); + predicates.extend(bounds.predicates(tcx, param_ty)); + trace!(?predicates); + } + GenericParamKind::Const { .. } => { + // Bounds on const parameters are currently not possible. + index += 1; + } + } + } + + trace!(?predicates); + // Add in the bounds that appear in the where-clause. + for predicate in ast_generics.predicates { + match predicate { + hir::WherePredicate::BoundPredicate(bound_pred) => { + let ty = icx.to_ty(bound_pred.bounded_ty); + let bound_vars = icx.tcx.late_bound_vars(bound_pred.hir_id); + + // Keep the type around in a dummy predicate, in case of no bounds. + // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` + // is still checked for WF. + if bound_pred.bounds.is_empty() { + if let ty::Param(_) = ty.kind() { + // This is a `where T:`, which can be in the HIR from the + // transformation that moves `?Sized` to `T`'s declaration. + // We can skip the predicate because type parameters are + // trivially WF, but also we *should*, to avoid exposing + // users who never wrote `where Type:,` themselves, to + // compiler/tooling bugs from not handling WF predicates. + } else { + let span = bound_pred.bounded_ty.span; + let predicate = ty::Binder::bind_with_vars( + ty::PredicateKind::WellFormed(ty.into()), + bound_vars, + ); + predicates.insert((predicate.to_predicate(tcx), span)); + } + } + + let mut bounds = Bounds::default(); + <dyn AstConv<'_>>::add_bounds( + &icx, + ty, + bound_pred.bounds.iter(), + &mut bounds, + bound_vars, + ); + predicates.extend(bounds.predicates(tcx, ty)); + } + + hir::WherePredicate::RegionPredicate(region_pred) => { + let r1 = <dyn AstConv<'_>>::ast_region_to_region(&icx, ®ion_pred.lifetime, None); + 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) + } + _ => bug!(), + }; + let pred = ty::Binder::dummy(ty::PredicateKind::RegionOutlives( + ty::OutlivesPredicate(r1, r2), + )) + .to_predicate(icx.tcx); + + (pred, span) + })) + } + + hir::WherePredicate::EqPredicate(..) => { + // FIXME(#20041) + } + } + } + + if tcx.features().generic_const_exprs { + predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); + } + + let mut predicates: Vec<_> = predicates.into_iter().collect(); + + // Subtle: before we store the predicates into the tcx, we + // sort them so that predicates like `T: Foo<Item=U>` come + // before uses of `U`. This avoids false ambiguity errors + // in trait checking. See `setup_constraining_predicates` + // for details. + if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node { + let self_ty = tcx.type_of(def_id); + let trait_ref = tcx.impl_trait_ref(def_id); + cgp::setup_constraining_predicates( + tcx, + &mut predicates, + trait_ref, + &mut cgp::parameters_for_impl(self_ty, trait_ref), + ); + } + + ty::GenericPredicates { + parent: generics.parent, + predicates: tcx.arena.alloc_from_iter(predicates), + } +} + +fn const_evaluatable_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> { + struct ConstCollector<'tcx> { + tcx: TyCtxt<'tcx>, + preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>, + } + + 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); + if let ty::ConstKind::Unevaluated(_) = ct.kind() { + let span = self.tcx.hir().span(c.hir_id); + self.preds.insert(( + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)) + .to_predicate(self.tcx), + span, + )); + } + } + + fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::AnonConst) { + // Do not look into const param defaults, + // these get checked when they are actually instantiated. + // + // We do not want the following to error: + // + // struct Foo<const N: usize, const M: usize = { N + 1 }>; + // struct Bar<const N: usize>(Foo<N, 3>); + } + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let node = tcx.hir().get(hir_id); + + let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() }; + if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(ref impl_) = item.kind { + if let Some(of_trait) = &impl_.of_trait { + debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); + collector.visit_trait_ref(of_trait); + } + + debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); + collector.visit_ty(impl_.self_ty); + } + + if let Some(generics) = node.generics() { + debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); + collector.visit_generics(generics); + } + + if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { + debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); + collector.visit_fn_decl(fn_sig.decl); + } + debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); + + collector.preds +} + +pub(super) fn trait_explicit_predicates_and_bounds( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::GenericPredicates<'_> { + assert_eq!(tcx.def_kind(def_id), DefKind::Trait); + gather_explicit_predicates_of(tcx, def_id.to_def_id()) +} + +pub(super) fn explicit_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> ty::GenericPredicates<'tcx> { + let def_kind = tcx.def_kind(def_id); + if let DefKind::Trait = def_kind { + // Remove bounds on associated types from the predicates, they will be + // returned by `explicit_item_bounds`. + let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local()); + let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id); + + let is_assoc_item_ty = |ty: Ty<'tcx>| { + // For a predicate from a where clause to become a bound on an + // associated type: + // * It must use the identity substs of the item. + // * Since any generic parameters on the item are not in scope, + // this means that the item is not a GAT, and its identity + // substs are the same as the trait's. + // * It must be an associated type for this trait (*not* a + // supertrait). + if let ty::Projection(projection) = ty.kind() { + projection.substs == trait_identity_substs + && tcx.associated_item(projection.item_def_id).container_id(tcx) == def_id + } else { + false + } + }; + + let predicates: Vec<_> = predicates_and_bounds + .predicates + .iter() + .copied() + .filter(|(pred, _)| match pred.kind().skip_binder() { + ty::PredicateKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()), + ty::PredicateKind::Projection(proj) => { + !is_assoc_item_ty(proj.projection_ty.self_ty()) + } + ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), + _ => true, + }) + .collect(); + if predicates.len() == predicates_and_bounds.predicates.len() { + predicates_and_bounds + } else { + ty::GenericPredicates { + parent: predicates_and_bounds.parent, + predicates: tcx.arena.alloc_slice(&predicates), + } + } + } 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() { + // 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) + // + // struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait; + // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling + // ^^^ explicit_predicates_of on + // parent item we dont have set as the + // 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 + return tcx.explicit_predicates_of(item_def_id); + } + } + gather_explicit_predicates_of(tcx, def_id) + } +} + +/// Ensures that the super-predicates of the trait with a `DefId` +/// of `trait_def_id` are converted and stored. This also ensures that +/// the transitive super-predicates are converted. +pub(super) fn super_predicates_of( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> ty::GenericPredicates<'_> { + tcx.super_predicates_that_define_assoc_type((trait_def_id, None)) +} + +/// Ensures that the super-predicates of the trait with a `DefId` +/// of `trait_def_id` are converted and stored. This also ensures that +/// the transitive super-predicates are converted. +pub(super) fn super_predicates_that_define_assoc_type( + tcx: TyCtxt<'_>, + (trait_def_id, assoc_name): (DefId, Option<Ident>), +) -> ty::GenericPredicates<'_> { + if trait_def_id.is_local() { + debug!("local trait"); + let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local()); + + let Node::Item(item) = tcx.hir().get(trait_hir_id) else { + bug!("trait_node_id {} is not an item", trait_hir_id); + }; + + let (generics, bounds) = match item.kind { + hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), + hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), + _ => span_bug!(item.span, "super_predicates invoked on non-trait"), + }; + + let icx = ItemCtxt::new(tcx, trait_def_id); + + // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. + let self_param_ty = tcx.types.self_param; + let superbounds1 = if let Some(assoc_name) = assoc_name { + <dyn AstConv<'_>>::compute_bounds_that_match_assoc_type( + &icx, + self_param_ty, + bounds, + assoc_name, + ) + } else { + <dyn AstConv<'_>>::compute_bounds(&icx, self_param_ty, bounds) + }; + + let superbounds1 = superbounds1.predicates(tcx, self_param_ty); + + // Convert any explicit superbounds in the where-clause, + // e.g., `trait Foo where Self: Bar`. + // In the case of trait aliases, however, we include all bounds in the where-clause, + // so e.g., `trait Foo = where u32: PartialEq<Self>` would include `u32: PartialEq<Self>` + // as one of its "superpredicates". + let is_trait_alias = tcx.is_trait_alias(trait_def_id); + let superbounds2 = icx.type_parameter_bounds_in_generics( + generics, + item.hir_id(), + self_param_ty, + OnlySelfBounds(!is_trait_alias), + assoc_name, + ); + + // Combine the two lists to form the complete set of superbounds: + let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); + debug!(?superbounds); + + // Now require that immediate supertraits are converted, + // which will, in turn, reach indirect supertraits. + if assoc_name.is_none() { + // Now require that immediate supertraits are converted, + // 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() { + tcx.at(span).super_predicates_of(bound.def_id()); + } + } + } + + ty::GenericPredicates { parent: None, predicates: superbounds } + } else { + // if `assoc_name` is None, then the query should've been redirected to an + // external provider + assert!(assoc_name.is_some()); + tcx.super_predicates_of(trait_def_id) + } +} + +/// Returns the predicates defined on `item_def_id` of the form +/// `X: Foo` where `X` is the type parameter `def_id`. +#[instrument(level = "trace", skip(tcx))] +pub(super) fn type_param_predicates( + tcx: TyCtxt<'_>, + (item_def_id, def_id, assoc_name): (DefId, LocalDefId, Ident), +) -> ty::GenericPredicates<'_> { + use rustc_hir::*; + + // In the AST, bounds can derive from two places. Either + // written inline like `<T: Foo>` or in a where-clause like + // `where T: Foo`. + + let param_id = tcx.hir().local_def_id_to_hir_id(def_id); + let param_owner = tcx.hir().ty_param_owner(def_id); + let generics = tcx.generics_of(param_owner); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id)); + + // Don't look for bounds where the type parameter isn't in scope. + let parent = if item_def_id == param_owner.to_def_id() { + None + } else { + tcx.generics_of(item_def_id).parent + }; + + let mut result = parent + .map(|parent| { + let icx = ItemCtxt::new(tcx, parent); + icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id(), assoc_name) + }) + .unwrap_or_default(); + let mut extend = None; + + let item_hir_id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + let ast_generics = match tcx.hir().get(item_hir_id) { + Node::TraitItem(item) => &item.generics, + + Node::ImplItem(item) => &item.generics, + + Node::Item(item) => { + match item.kind { + ItemKind::Fn(.., ref generics, _) + | ItemKind::Impl(hir::Impl { ref generics, .. }) + | ItemKind::TyAlias(_, ref generics) + | ItemKind::OpaqueTy(OpaqueTy { + ref generics, + origin: hir::OpaqueTyOrigin::TyAlias, + .. + }) + | ItemKind::Enum(_, ref generics) + | ItemKind::Struct(_, ref generics) + | ItemKind::Union(_, ref generics) => generics, + ItemKind::Trait(_, _, ref generics, ..) => { + // Implied `Self: Trait` and supertrait bounds. + if param_id == item_hir_id { + let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id); + extend = + Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); + } + generics + } + _ => return result, + } + } + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(_, _, ref generics) => generics, + _ => return result, + }, + + _ => return result, + }; + + let icx = ItemCtxt::new(tcx, item_def_id); + let extra_predicates = extend.into_iter().chain( + icx.type_parameter_bounds_in_generics( + ast_generics, + param_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), + _ => false, + }), + ); + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates)); + result +} + +impl<'tcx> ItemCtxt<'tcx> { + /// Finds bounds from `hir::Generics`. This requires scanning through the + /// AST. We do this to avoid having to convert *all* the bounds, which + /// would create artificial cycles. Instead, we can only convert the + /// bounds for a type parameter `X` if `X::Foo` is used. + #[instrument(level = "trace", skip(self, ast_generics))] + fn type_parameter_bounds_in_generics( + &self, + ast_generics: &'tcx hir::Generics<'tcx>, + param_id: hir::HirId, + 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() + .filter_map(|wp| match *wp { + hir::WherePredicate::BoundPredicate(ref bp) => Some(bp), + _ => None, + }) + .flat_map(|bp| { + let bt = if bp.is_param_bound(param_def_id) { + Some(ty) + } else if !only_self_bounds.0 { + Some(self.to_ty(bp.bounded_ty)) + } else { + None + }; + let bvars = self.tcx.late_bound_vars(bp.hir_id); + + bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b, bvars))).filter( + |(_, b, _)| match assoc_name { + Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name), + None => true, + }, + ) + }) + .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)) + .collect() + } + + #[instrument(level = "trace", skip(self))] + fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { + match b { + hir::GenericBound::Trait(poly_trait_ref, _) => { + let trait_ref = &poly_trait_ref.trait_ref; + if let Some(trait_did) = trait_ref.trait_def_id() { + self.tcx.trait_may_define_assoc_type(trait_did, assoc_name) + } else { + false + } + } + _ => false, + } + } +} + +/// Converts a specific `GenericBound` from the AST into a set of +/// predicates that apply to the self type. A vector is returned +/// because this can be anywhere from zero predicates (`T: ?Sized` adds no +/// predicates) to one (`T: Foo`) to many (`T: Bar<X = i32>` adds `T: Bar` +/// and `<T as Bar>::X == i32`). +fn predicates_from_bound<'tcx>( + astconv: &dyn AstConv<'tcx>, + param_ty: Ty<'tcx>, + bound: &'tcx hir::GenericBound<'tcx>, + bound_vars: &'tcx ty::List<ty::BoundVariableKind>, +) -> Vec<(ty::Predicate<'tcx>, Span)> { + let mut bounds = Bounds::default(); + astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars); + bounds.predicates(astconv.tcx(), param_ty).collect() +} diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 534ddfa95..c29a645eb 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,6 +1,5 @@ use rustc_errors::{Applicability, StashKey}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; @@ -19,7 +18,6 @@ use crate::errors::UnconstrainedOpaqueType; /// Computes the relevant generic parameter for a potential generic const argument. /// /// This should be called using the query `tcx.opt_const_param_of`. -#[instrument(level = "debug", skip(tcx))] pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { use hir::*; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); @@ -67,8 +65,8 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let ty = item_ctxt.ast_ty_to_ty(hir_ty); // Iterate through the generics of the projection to find the one that corresponds to - // the def_id that this query was called with. We filter to only const args here as a - // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't + // the def_id that this query was called with. We filter to only type and const args here + // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't // but it can't hurt to be safe ^^ if let ty::Projection(projection) = ty.kind() { let generics = tcx.generics_of(projection.item_def_id); @@ -79,7 +77,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.id() == hir_id) + .position(|arg| arg.hir_id() == hir_id) }) .unwrap_or_else(|| { bug!("no arg matching AnonConst in segment"); @@ -112,7 +110,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.id() == hir_id) + .position(|arg| arg.hir_id() == hir_id) }) .unwrap_or_else(|| { bug!("no arg matching AnonConst in segment"); @@ -166,7 +164,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< args.args .iter() .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.id() == hir_id) + .position(|arg| arg.hir_id() == hir_id) .map(|index| (index, seg)).or_else(|| args.bindings .iter() .filter_map(TypeBinding::opt_const) @@ -180,15 +178,12 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< return None; }; - // Try to use the segment resolution if it is valid, otherwise we - // default to the path resolution. - let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); - let generics = match tcx.res_generics_def_id(res) { + let generics = match tcx.res_generics_def_id(segment.res) { Some(def_id) => tcx.generics_of(def_id), None => { tcx.sess.delay_span_bug( tcx.def_span(def_id), - &format!("unexpected anon const res {:?} in path: {:?}", res, path), + &format!("unexpected anon const res {:?} in path: {:?}", segment.res, path), ); return None; } @@ -229,7 +224,7 @@ fn get_path_containing_arg_in_pat<'hir>( .iter() .filter_map(|seg| seg.args) .flat_map(|args| args.args) - .any(|arg| arg.id() == arg_id) + .any(|arg| arg.hir_id() == arg_id) }; let mut arg_path = None; pat.walk(|pat| match pat.kind { @@ -289,7 +284,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { icx.to_ty(ty) } } - ImplItemKind::TyAlias(ty) => { + ImplItemKind::Type(ty) => { if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() { check_feature_inherent_assoc_ty(tcx, item.span); } @@ -324,7 +319,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty), - ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty), + ItemKind::Impl(hir::Impl { self_ty, .. }) => { + match self_ty.find_self_aliases() { + spans if spans.len() > 0 => { + tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: (), }); + tcx.ty_error() + }, + _ => icx.to_ty(*self_ty), + } + }, ItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); tcx.mk_fn_def(def_id.to_def_id(), substs) @@ -338,7 +341,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { find_opaque_ty_constraints_for_tait(tcx, def_id) } // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { + ItemKind::OpaqueTy(OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), + in_trait, + .. + }) => { + if in_trait { + assert!(tcx.impl_defaultness(owner).has_value()); + } find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) @@ -379,7 +390,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Field(field) => icx.to_ty(field.ty), - Node::Expr(&Expr { kind: ExprKind::Closure{..}, .. }) => tcx.typeck(def_id).node_type(hir_id), + Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => { + tcx.typeck(def_id).node_type(hir_id) + } Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => { // We defer to `type_of` of the corresponding parameter @@ -411,40 +424,93 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id, + | hir::InlineAsmOperand::SymFn { anon_const } => { + anon_const.hir_id == hir_id + } _ => false, }) => { tcx.typeck(def_id).node_type(hir_id) } - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx - .adt_def(tcx.hir().get_parent_item(hir_id)) - .repr() - .discr_type() - .to_ty(tcx), + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { + tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) + } - Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) - if let Node::TraitRef(trait_ref) = tcx.hir().get( - tcx.hir().get_parent_node(binding_id) - ) => + Node::TypeBinding( + binding @ &TypeBinding { + hir_id: binding_id, + kind: TypeBindingKind::Equality { term: Term::Const(ref e) }, + .. + }, + ) if let Node::TraitRef(trait_ref) = + tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + && e.hir_id == hir_id => { - let Some(trait_def_id) = trait_ref.trait_def_id() else { - return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); - }; - let assoc_items = tcx.associated_items(trait_def_id); - let assoc_item = assoc_items.find_by_name_and_kind( - tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(), - ); - if let Some(assoc_item) = assoc_item { - tcx.type_of(assoc_item.def_id) - } else { - // FIXME(associated_const_equality): add a useful error message here. - tcx.ty_error_with_message( - DUMMY_SP, - "Could not find associated const on trait", - ) - } + let Some(trait_def_id) = trait_ref.trait_def_id() else { + return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + }; + let assoc_items = tcx.associated_items(trait_def_id); + let assoc_item = assoc_items.find_by_name_and_kind( + tcx, + binding.ident, + ty::AssocKind::Const, + def_id.to_def_id(), + ); + if let Some(assoc_item) = assoc_item { + tcx.type_of(assoc_item.def_id) + } else { + // FIXME(associated_const_equality): add a useful error message here. + tcx.ty_error_with_message( + DUMMY_SP, + "Could not find associated const on trait", + ) + } + } + + Node::TypeBinding( + binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. }, + ) if let Node::TraitRef(trait_ref) = + tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + && let Some((idx, _)) = + gen_args.args.iter().enumerate().find(|(_, arg)| { + if let GenericArg::Const(ct) = arg { + ct.value.hir_id == hir_id + } else { + false + } + }) => + { + let Some(trait_def_id) = trait_ref.trait_def_id() else { + return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + }; + let assoc_items = tcx.associated_items(trait_def_id); + let assoc_item = assoc_items.find_by_name_and_kind( + tcx, + binding.ident, + match kind { + // I think `<A: T>` type bindings requires that `A` is a type + TypeBindingKind::Constraint { .. } + | TypeBindingKind::Equality { term: Term::Ty(..) } => { + ty::AssocKind::Type + } + TypeBindingKind::Equality { term: Term::Const(..) } => { + ty::AssocKind::Const + } + }, + def_id.to_def_id(), + ); + if let Some(param) + = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const()) + { + tcx.type_of(param.def_id) + } else { + // FIXME(associated_const_equality): add a useful error message here. + tcx.ty_error_with_message( + DUMMY_SP, + "Could not find associated const on trait", + ) + } } Node::GenericParam(&GenericParam { @@ -453,8 +519,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { .. }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), - x => - tcx.ty_error_with_message( + x => tcx.ty_error_with_message( DUMMY_SP, &format!("unexpected const parent in type_of(): {x:?}"), ), @@ -508,6 +573,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T /// checked against it (we also carry the span of that first /// type). found: Option<ty::OpaqueHiddenType<'tcx>>, + + /// In the presence of dead code, typeck may figure out a hidden type + /// while borrowck will now. We collect these cases here and check at + /// the end that we actually found a type that matches (modulo regions). + typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>, } impl ConstraintLocator<'_> { @@ -534,18 +604,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); return; } - if !tables.concrete_opaque_types.contains_key(&self.def_id) { + let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { debug!("no constraints in typeck results"); return; + }; + if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) { + self.typeck_types.push(typeck_hidden_ty); } + // Use borrowck to get the type with unerased regions. let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; debug!(?concrete_opaque_types); if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { debug!(?concrete_type, "found constraint"); - if let Some(prev) = self.found { - if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() { + if let Some(prev) = &mut self.found { + if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { prev.report_mismatch(&concrete_type, self.tcx); + prev.ty = self.tcx.ty_error(); } } else { self.found = Some(concrete_type); @@ -568,31 +643,31 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_item(self, it); } } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.def_id); - self.check(it.def_id); + trace!(?it.owner_id); + self.check(it.owner_id.def_id); intravisit::walk_trait_item(self, it); } } let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None }; + let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] }; debug!(?scope); @@ -622,16 +697,32 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T } } - match locator.found { - Some(hidden) => hidden.ty, - None => { - tcx.sess.emit_err(UnconstrainedOpaqueType { - span: tcx.def_span(def_id), - name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), - }); - tcx.ty_error() + let Some(hidden) = locator.found else { + 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) { + _ if scope == hir::CRATE_HIR_ID => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", + _ => "item", + }, + }); + return tcx.ty_error(); + }; + + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, tcx); + } } } + + hidden.ty } fn find_opaque_ty_constraints_for_rpit( @@ -687,24 +778,24 @@ fn find_opaque_ty_constraints_for_rpit( intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_item(self, it); } } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.def_id); - self.check(it.def_id); + trace!(?it.owner_id); + self.check(it.owner_id.def_id); intravisit::walk_trait_item(self, it); } } @@ -732,20 +823,15 @@ fn find_opaque_ty_constraints_for_rpit( // the `concrete_opaque_types` table. tcx.ty_error() } else { - table - .concrete_opaque_types - .get(&def_id) - .copied() - .unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }) - .expect("RPIT always have a hidden type from typeck") + table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + tcx.mk_diverging_default() + }) } }) } @@ -801,6 +887,9 @@ fn infer_placeholder_type<'a>( match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { Some(mut err) => { if !ty.references_error() { + // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic) + let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" }; + // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. // We are typeck and have the real type, so remove that and suggest the actual type. // FIXME(eddyb) this looks like it should be functionality on `Diagnostic`. @@ -816,7 +905,7 @@ fn infer_placeholder_type<'a>( err.span_suggestion( span, &format!("provide a type for the {item}", item = kind), - format!("{}: {}", item_ident, sugg_ty), + format!("{colon} {sugg_ty}"), Applicability::MachineApplicable, ); } else { diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 8428e4664..213b89fc7 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -114,9 +114,9 @@ pub fn identify_constrained_generic_params<'tcx>( /// ``` /// The impl's predicates are collected from left to right. Ignoring /// the implicit `Sized` bounds, these are -/// * T: Debug -/// * U: Iterator -/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate +/// * `T: Debug` +/// * `U: Iterator` +/// * `<U as Iterator>::Item = T` -- a desugared ProjectionPredicate /// /// When we, for example, try to go over the trait-reference /// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh @@ -132,12 +132,16 @@ pub fn identify_constrained_generic_params<'tcx>( /// /// We *do* have to be somewhat careful when projection targets contain /// projections themselves, for example in +/// +/// ```ignore (illustrative) /// impl<S,U,V,W> Trait for U where /// /* 0 */ S: Iterator<Item = U>, /// /* - */ U: Iterator, /// /* 1 */ <U as Iterator>::Item: ToOwned<Owned=(W,<V as Iterator>::Item)> /// /* 2 */ W: Iterator<Item = V> /// /* 3 */ V: Debug +/// ``` +/// /// we have to evaluate the projections in the order I wrote them: /// `V: Debug` requires `V` to be evaluated. The only projection that /// *determines* `V` is 2 (1 contains it, but *does not determine it*, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs new file mode 100644 index 000000000..d5b1a7ce1 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -0,0 +1,282 @@ +//! Errors emitted by `rustc_hir_analysis`. + +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler}; +use rustc_errors::{IntoDiagnostic, MultiSpan}; +use rustc_macros::{Diagnostic, LintDiagnostic}; +use rustc_middle::ty::Ty; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(Diagnostic)] +#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")] +pub struct UnrecognizedAtomicOperation<'a> { + #[primary_span] + #[label] + pub span: Span, + pub op: &'a str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")] +pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { + #[primary_span] + #[label] + pub span: Span, + pub found: usize, + pub expected: usize, + pub descr: &'a str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_unrecognized_intrinsic_function, code = "E0093")] +pub struct UnrecognizedIntrinsicFunction { + #[primary_span] + #[label] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_trait, code = "E0195")] +pub struct LifetimesOrBoundsMismatchOnTrait { + #[primary_span] + #[label] + pub span: Span, + #[label(generics_label)] + pub generics_span: Option<Span>, + pub item_kind: &'static str, + pub ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")] +pub struct DropImplOnWrongItem { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_field_already_declared, code = "E0124")] +pub struct FieldAlreadyDeclared { + pub field_name: Ident, + #[primary_span] + #[label] + pub span: Span, + #[label(previous_decl_label)] + pub prev_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_copy_impl_on_type_with_dtor, code = "E0184")] +pub struct CopyImplOnTypeWithDtor { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_multiple_relaxed_default_bounds, code = "E0203")] +pub struct MultipleRelaxedDefaultBounds { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_copy_impl_on_non_adt, code = "E0206")] +pub struct CopyImplOnNonAdt { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")] +pub struct TraitObjectDeclaredWithNoTraits { + #[primary_span] + pub span: Span, + #[label(alias_span)] + pub trait_alias_span: Option<Span>, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_ambiguous_lifetime_bound, code = "E0227")] +pub struct AmbiguousLifetimeBound { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_assoc_type_binding_not_allowed, code = "E0229")] +pub struct AssocTypeBindingNotAllowed { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_typeof_reserved_keyword_used, code = "E0516")] +pub struct TypeofReservedKeywordUsed<'tcx> { + pub ty: Ty<'tcx>, + #[primary_span] + #[label] + pub span: Span, + #[suggestion_verbose(code = "{ty}")] + pub opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_value_of_associated_struct_already_specified, code = "E0719")] +pub struct ValueOfAssociatedStructAlreadySpecified { + #[primary_span] + #[label] + pub span: Span, + #[label(previous_bound_label)] + pub prev_span: Span, + pub item_name: Ident, + pub def_path: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_unconstrained_opaque_type)] +#[note] +pub struct UnconstrainedOpaqueType { + #[primary_span] + pub span: Span, + pub name: Symbol, + pub what: &'static str, +} + +pub struct MissingTypeParams { + pub span: Span, + pub def_span: Span, + pub span_snippet: Option<String>, + pub missing_type_params: Vec<Symbol>, + pub empty_generic_args: bool, +} + +// Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`. +impl<'a> IntoDiagnostic<'a> for MissingTypeParams { + fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut err = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::hir_analysis_missing_type_params, + error_code!(E0393), + ); + err.set_arg("parameterCount", self.missing_type_params.len()); + err.set_arg( + "parameters", + self.missing_type_params + .iter() + .map(|n| format!("`{}`", n)) + .collect::<Vec<_>>() + .join(", "), + ); + + err.span_label(self.def_span, rustc_errors::fluent::label); + + let mut suggested = false; + // Don't suggest setting the type params if there are some already: the order is + // tricky to get right and the user will already know what the syntax is. + if let Some(snippet) = self.span_snippet && self.empty_generic_args { + if snippet.ends_with('>') { + // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion + // we would have to preserve the right order. For now, as clearly the user is + // aware of the syntax, we do nothing. + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator<Type>`. + err.span_suggestion( + self.span, + rustc_errors::fluent::suggestion, + format!( + "{}<{}>", + snippet, + self.missing_type_params + .iter() + .map(|n| n.to_string()) + .collect::<Vec<_>>() + .join(", ") + ), + Applicability::HasPlaceholders, + ); + suggested = true; + } + } + if !suggested { + err.span_label(self.span, rustc_errors::fluent::no_suggestion_label); + } + + err.note(rustc_errors::fluent::note); + err + } +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_manual_implementation, code = "E0183")] +#[help] +pub struct ManualImplementation { + #[primary_span] + #[label] + pub span: Span, + pub trait_name: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_substs_on_overridden_impl)] +pub struct SubstsOnOverriddenImpl { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_unused_extern_crate)] +pub struct UnusedExternCrate { + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_extern_crate_not_idiomatic)] +pub struct ExternCrateNotIdiomatic { + #[suggestion_short(applicability = "machine-applicable", code = "{suggestion_code}")] + pub span: Span, + pub msg_code: String, + pub suggestion_code: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_expected_used_symbol)] +pub struct ExpectedUsedSymbol { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_const_impl_for_non_const_trait)] +pub struct ConstImplForNonConstTrait { + #[primary_span] + pub trait_ref_span: Span, + pub trait_name: String, + #[suggestion(applicability = "machine-applicable", code = "#[const_trait]")] + pub local_trait_span: Option<Span>, + #[note] + pub marking: (), + #[note(adding)] + pub adding: (), +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_const_bound_for_non_const_trait)] +pub struct ConstBoundForNonConstTrait { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_self_in_impl_self)] +pub struct SelfInImplSelf { + #[primary_span] + pub span: MultiSpan, + #[note] + pub note: (), +} diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 55c7a15f9..b0fdfcf38 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -3,11 +3,10 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ForeignItem, ForeignItemKind, HirId}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::TraitEngine; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder}; -use rustc_trait_selection::traits::{self, TraitEngineExt}; +use rustc_trait_selection::traits; pub fn provide(providers: &mut Providers) { *providers = Providers { diagnostic_hir_wf_check, ..*providers }; @@ -65,41 +64,36 @@ fn diagnostic_hir_wf_check<'tcx>( impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - self.tcx.infer_ctxt().enter(|infcx| { - let mut fulfill = <dyn TraitEngine<'tcx>>::new(self.tcx); - let tcx_ty = - self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }); - let cause = traits::ObligationCause::new( - ty.span, - self.hir_id, - traits::ObligationCauseCode::WellFormed(None), - ); - fulfill.register_predicate_obligation( - &infcx, - traits::Obligation::new( - cause, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())) - .to_predicate(self.tcx), - ), - ); - - let errors = fulfill.select_all_or_error(&infcx); - if !errors.is_empty() { - debug!("Wf-check got errors for {:?}: {:?}", ty, errors); - for error in errors { - if error.obligation.predicate == self.predicate { - // Save the cause from the greatest depth - this corresponds - // to picking more-specific types (e.g. `MyStruct<u8>`) - // over less-specific types (e.g. `Option<MyStruct<u8>>`) - if self.depth >= self.cause_depth { - self.cause = Some(error.obligation.cause); - self.cause_depth = self.depth - } + let infcx = self.tcx.infer_ctxt().build(); + let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }); + let cause = traits::ObligationCause::new( + ty.span, + self.hir_id, + traits::ObligationCauseCode::WellFormed(None), + ); + let errors = traits::fully_solve_obligation( + &infcx, + traits::Obligation::new( + cause, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())) + .to_predicate(self.tcx), + ), + ); + if !errors.is_empty() { + debug!("Wf-check got errors for {:?}: {:?}", ty, errors); + for error in errors { + if error.obligation.predicate == self.predicate { + // Save the cause from the greatest depth - this corresponds + // to picking more-specific types (e.g. `MyStruct<u8>`) + // over less-specific types (e.g. `Option<MyStruct<u8>>`) + if self.depth >= self.cause_depth { + self.cause = Some(error.obligation.cause); + self.cause_depth = self.depth } } } - }); + } self.depth += 1; intravisit::walk_ty(self, ty); self.depth -= 1; @@ -123,7 +117,7 @@ fn diagnostic_hir_wf_check<'tcx>( let ty = match loc { WellFormedLoc::Ty(_) => match hir.get(hir_id) { hir::Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::TyAlias(ty) => Some(ty), + hir::ImplItemKind::Type(ty) => Some(ty), hir::ImplItemKind::Const(ty, _) => Some(ty), ref item => bug!("Unexpected ImplItem {:?}", item), }, @@ -144,6 +138,10 @@ fn diagnostic_hir_wf_check<'tcx>( hir::Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Static(ty, _), .. }) => Some(*ty), + hir::Node::GenericParam(hir::GenericParam { + kind: hir::GenericParamKind::Type { default: Some(ty), .. }, + .. + }) => Some(*ty), ref node => bug!("Unexpected node {:?}", node), }, WellFormedLoc::Param { function: _, param_idx } => { diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 9fee1eaae..136f61999 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -11,7 +11,7 @@ use crate::constrained_generic_params as cgp; use min_specialization::check_min_specialization; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; @@ -19,8 +19,6 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::{Span, Symbol}; -use std::collections::hash_map::Entry::{Occupied, Vacant}; - mod min_specialization; /// Checks that all the type/lifetime parameters on an impl also @@ -57,11 +55,10 @@ fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let min_specialization = tcx.features().min_specialization; let module = tcx.hir_module_items(module_def_id); for id in module.items() { - if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { - enforce_impl_params_are_constrained(tcx, id.def_id); - enforce_impl_items_are_distinct(tcx, id.def_id); + if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) { + enforce_impl_params_are_constrained(tcx, id.owner_id.def_id); if min_specialization { - check_min_specialization(tcx, id.def_id); + check_min_specialization(tcx, id.owner_id.def_id); } } } @@ -194,35 +191,3 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol } err.emit(); } - -/// Enforce that we do not have two items in an impl with the same name. -fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { - let mut seen_type_items = FxHashMap::default(); - let mut seen_value_items = FxHashMap::default(); - for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) { - let impl_item = tcx.associated_item(impl_item_ref); - let seen_items = match impl_item.kind { - ty::AssocKind::Type => &mut seen_type_items, - _ => &mut seen_value_items, - }; - let span = tcx.def_span(impl_item_ref); - let ident = impl_item.ident(tcx); - match seen_items.entry(ident.normalize_to_macros_2_0()) { - Occupied(entry) => { - let mut err = struct_span_err!( - tcx.sess, - span, - E0201, - "duplicate definitions with name `{}`:", - ident - ); - err.span_label(*entry.get(), format!("previous definition of `{}` here", ident)); - err.span_label(span, "duplicate definition"); - err.emit(); - } - Vacant(entry) => { - entry.insert(span); - } - } - } -} diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 74abb71a1..e806e9487 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -65,27 +65,25 @@ //! cause use after frees with purely safe code in the same way as specializing //! on traits with methods can. -use crate::check::regionck::OutlivesEnvironmentExt; -use crate::check::wfcheck::impl_implied_bounds; use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::specialization_graph::Node; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::Span; -use rustc_trait_selection::traits::{self, translate_substs, wf}; +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}; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { - tcx.infer_ctxt().enter(|infcx| { - check_always_applicable(&infcx, impl_def_id, node); - }); + check_always_applicable(tcx, impl_def_id, node); } } @@ -105,16 +103,14 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti } /// Check that `impl1` is a sound specialization -fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, impl2_node: Node) { - if let Some((impl1_substs, impl2_substs)) = get_impl_substs(infcx, impl1_def_id, impl2_node) { +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 ); - let tcx = infcx.tcx; - let parent_substs = if impl2_node.is_from_trait() { impl2_substs.to_vec() } else { @@ -124,7 +120,7 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, let span = tcx.def_span(impl1_def_id); check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); - check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); + check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } @@ -134,30 +130,37 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, /// /// Example /// +/// ```ignore (illustrative) /// impl<A, B> Foo<A> for B { /* impl2 */ } /// impl<C> Foo<Vec<C>> for C { /* impl1 */ } +/// ``` /// /// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`. fn get_impl_substs<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, impl2_node: Node, ) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { - let tcx = infcx.tcx; + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); let param_env = tcx.param_env(impl1_def_id); + let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id); + + let assumed_wf_types = + ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id); let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id()); let impl2_substs = translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); - let mut outlives_env = OutlivesEnvironment::new(param_env); - let implied_bounds = - impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id)); - outlives_env.add_implied_bounds( - infcx, - implied_bounds, - tcx.hir().local_def_id_to_hir_id(impl1_def_id), - ); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return None; + } + + let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); @@ -224,13 +227,17 @@ fn unconstrained_parent_impl_substs<'tcx>( /// /// For example forbid the following: /// +/// ```ignore (illustrative) /// impl<A> Tr for A { } /// impl<B> Tr for (B, B) { } +/// ``` /// /// Note that only consider the unconstrained parameters of the base impl: /// +/// ```ignore (illustrative) /// impl<S, I: IntoIterator<Item = S>> Tr<S> for I { } /// impl<T> Tr<T> for Vec<T> { } +/// ``` /// /// The substs for the parent impl here are `[T, Vec<T>]`, which repeats `T`, /// but `S` is constrained in the parent impl, so `parent_substs` is only @@ -255,8 +262,10 @@ fn check_duplicate_params<'tcx>( /// /// For example forbid the following: /// +/// ```ignore (illustrative) /// impl<A> Tr for A { } /// impl Tr for &'static i32 { } +/// ``` fn check_static_lifetimes<'tcx>( tcx: TyCtxt<'tcx>, parent_substs: &Vec<GenericArg<'tcx>>, @@ -279,14 +288,13 @@ fn check_static_lifetimes<'tcx>( /// * a well-formed predicate of a type argument of the trait being implemented, /// including the `Self`-type. fn check_predicates<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, impl1_substs: SubstsRef<'tcx>, impl2_node: Node, impl2_substs: SubstsRef<'tcx>, span: Span, ) { - let tcx = infcx.tcx; let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); let impl1_predicates: Vec<_> = traits::elaborate_predicates_with_span( tcx, @@ -343,19 +351,21 @@ fn check_predicates<'tcx>( // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs { - if let Some(obligations) = wf::obligations( + let infcx = &tcx.infer_ctxt().build(); + let obligations = wf::obligations( infcx, tcx.param_env(impl1_def_id), tcx.hir().local_def_id_to_hir_id(impl1_def_id), 0, arg, span, - ) { - impl2_predicates.extend( - traits::elaborate_obligations(tcx, obligations) - .map(|obligation| obligation.predicate), - ) - } + ) + .unwrap(); + + assert!(!obligations.needs_infer()); + impl2_predicates.extend( + traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate), + ) } impl2_predicates.extend( traits::elaborate_predicates_with_span(tcx, always_applicable_traits) @@ -418,13 +428,10 @@ fn trait_predicate_kind<'tcx>( predicate: ty::Predicate<'tcx>, ) -> Option<TraitSpecializationKind> { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: _, - }) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), - ty::PredicateKind::Trait(_) - | ty::PredicateKind::RegionOutlives(_) + 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::WellFormed(_) diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs new file mode 100644 index 000000000..525cd2419 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -0,0 +1,552 @@ +/*! + +# typeck + +The type checker is responsible for: + +1. Determining the type of each expression. +2. Resolving methods and traits. +3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?" + Well, dear reader, read on.) + +The main entry point is [`check_crate()`]. Type checking operates in +several major phases: + +1. The collect phase first passes over all items and determines their + type, without examining their "innards". + +2. Variance inference then runs to compute the variance of each parameter. + +3. Coherence checks for overlapping or orphaned impls. + +4. Finally, the check phase then checks function bodies and so forth. + Within the check phase, we check each function body one at a time + (bodies of function expressions are checked as part of the + containing function). Inference is used to supply types wherever + they are unknown. The actual checking of a function itself has + several phases (check, regionck, writeback), as discussed in the + documentation for the [`check`] module. + +The type checker is defined into various submodules which are documented +independently: + +- astconv: converts the AST representation of types + into the `ty` representation. + +- collect: computes the types of each top-level item and enters them into + the `tcx.types` table for later use. + +- coherence: enforces coherence rules, builds some tables. + +- variance: variance inference + +- outlives: outlives inference + +- check: walks over function bodies and type checks them, inferring types for + local variables, type parameters, etc as necessary. + +- infer: finds the types to use for each type variable such that + all subtyping and assignment constraints are met. In essence, the check + module specifies the constraints, and the infer module solves them. + +## Note + +This API is completely unstable and subject to change. + +*/ + +#![allow(rustc::potential_query_instability)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(box_patterns)] +#![feature(control_flow_enum)] +#![feature(drain_filter)] +#![feature(hash_drain_filter)] +#![feature(if_let_guard)] +#![feature(is_sorted)] +#![feature(iter_intersperse)] +#![feature(let_chains)] +#![feature(min_specialization)] +#![feature(never_type)] +#![feature(once_cell)] +#![feature(slice_partition_dedup)] +#![feature(try_blocks)] +#![feature(is_some_and)] +#![feature(type_alias_impl_trait)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; + +#[macro_use] +extern crate rustc_middle; + +// These are used by Clippy. +pub mod check; + +pub mod astconv; +mod bounds; +mod check_unused; +mod coherence; +// FIXME: This module shouldn't be public. +pub mod collect; +mod constrained_generic_params; +mod errors; +pub mod hir_wf_check; +mod impl_wf_check; +mod outlives; +pub mod structured_errors; +mod variance; + +use rustc_errors::{struct_span_err, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{Node, CRATE_HIR_ID}; +use rustc_infer::infer::{InferOk, TyCtxtInferExt}; +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_span::{symbol::sym, Span, DUMMY_SP}; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; + +use std::iter; + +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, + 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(); + } + } +} + +fn require_same_types<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, +) -> bool { + let infcx = &tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::empty(); + let errors = match infcx.at(cause, param_env).eq(expected, actual) { + Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations), + Err(err) => { + infcx.err_ctxt().report_mismatched_types(cause, expected, actual, err).emit(); + return false; + } + }; + + match &errors[..] { + [] => true, + errors => { + infcx.err_ctxt().report_fulfillment_errors(errors, None, false); + false + } + } +} + +fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { + let main_fnsig = tcx.fn_sig(main_def_id); + let main_span = tcx.def_span(main_def_id); + + fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId { + if let Some(local_def_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); + let hir_type = tcx.type_of(local_def_id); + if !matches!(hir_type.kind(), ty::FnDef(..)) { + span_bug!(sp, "main has a non-function type: found `{}`", hir_type); + } + hir_id + } else { + CRATE_HIR_ID + } + } + + fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { + if !def_id.is_local() { + return None; + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().find(hir_id) { + Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => { + if !generics.params.is_empty() { + Some(generics.span) + } else { + None + } + } + _ => { + span_bug!(tcx.def_span(def_id), "main has a non-function type"); + } + } + } + + fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { + if !def_id.is_local() { + return None; + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().find(hir_id) { + Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => { + Some(generics.where_clause_span) + } + _ => { + span_bug!(tcx.def_span(def_id), "main has a non-function type"); + } + } + } + + fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { + if !def_id.is_local() { + return None; + } + Some(tcx.def_span(def_id)) + } + + fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> { + if !def_id.is_local() { + return None; + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().find(hir_id) { + Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => { + Some(fn_sig.decl.output.span()) + } + _ => { + span_bug!(tcx.def_span(def_id), "main has a non-function type"); + } + } + } + + let mut error = false; + let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span); + let main_fn_generics = tcx.generics_of(main_def_id); + let main_fn_predicates = tcx.predicates_of(main_def_id); + if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() { + let generics_param_span = main_fn_generics_params_span(tcx, main_def_id); + let msg = "`main` function is not allowed to have generic \ + parameters"; + let mut diag = + struct_span_err!(tcx.sess, generics_param_span.unwrap_or(main_span), E0131, "{}", msg); + if let Some(generics_param_span) = generics_param_span { + let label = "`main` cannot have generic parameters"; + diag.span_label(generics_param_span, label); + } + diag.emit(); + error = true; + } else if !main_fn_predicates.predicates.is_empty() { + // generics may bring in implicit predicates, so we skip this check if generics is present. + let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id); + let mut diag = struct_span_err!( + tcx.sess, + generics_where_clauses_span.unwrap_or(main_span), + E0646, + "`main` function is not allowed to have a `where` clause" + ); + if let Some(generics_where_clauses_span) = generics_where_clauses_span { + diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause"); + } + diag.emit(); + error = true; + } + + let main_asyncness = tcx.asyncness(main_def_id); + if let hir::IsAsync::Async = main_asyncness { + let mut diag = struct_span_err!( + tcx.sess, + main_span, + E0752, + "`main` function is not allowed to be `async`" + ); + let asyncness_span = main_fn_asyncness_span(tcx, main_def_id); + if let Some(asyncness_span) = asyncness_span { + diag.span_label(asyncness_span, "`main` function is not allowed to be `async`"); + } + diag.emit(); + error = true; + } + + for attr in tcx.get_attrs(main_def_id, sym::track_caller) { + tcx.sess + .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`") + .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`") + .emit(); + error = true; + } + + if error { + return; + } + + let expected_return_type; + if let Some(term_did) = tcx.lang_items().termination() { + let return_ty = main_fnsig.output(); + let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span); + if !return_ty.bound_vars().is_empty() { + let msg = "`main` function return type is not allowed to have generic \ + parameters"; + struct_span_err!(tcx.sess, return_ty_span, E0131, "{}", msg).emit(); + error = true; + } + let return_ty = return_ty.skip_binder(); + let infcx = tcx.infer_ctxt().build(); + // Main should have no WC, so empty param env is OK here. + let param_env = ty::ParamEnv::empty(); + let cause = traits::ObligationCause::new( + return_ty_span, + main_diagnostics_hir_id, + ObligationCauseCode::MainFunctionType, + ); + let ocx = traits::ObligationCtxt::new(&infcx); + let norm_return_ty = ocx.normalize(cause.clone(), 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); + error = true; + } + // now we can take the return type of the given main function + expected_return_type = main_fnsig.output(); + } else { + // standard () main return type + expected_return_type = ty::Binder::dummy(tcx.mk_unit()); + } + + if error { + return; + } + + let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| { + tcx.mk_fn_sig(iter::empty(), expected_return_type, false, hir::Unsafety::Normal, Abi::Rust) + })); + + require_same_types( + tcx, + &ObligationCause::new( + main_span, + main_diagnostics_hir_id, + ObligationCauseCode::MainFunctionType, + ), + se_ty, + tcx.mk_fn_ptr(main_fnsig), + ); +} +fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { + let start_def_id = start_def_id.expect_local(); + let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id); + let start_span = tcx.def_span(start_def_id); + let start_t = tcx.type_of(start_def_id); + match start_t.kind() { + ty::FnDef(..) => { + if let Some(Node::Item(it)) = tcx.hir().find(start_id) { + if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { + let mut error = false; + if !generics.params.is_empty() { + struct_span_err!( + tcx.sess, + generics.span, + E0132, + "start function is not allowed to have type parameters" + ) + .span_label(generics.span, "start function cannot have type parameters") + .emit(); + error = true; + } + if generics.has_where_clause_predicates { + struct_span_err!( + tcx.sess, + generics.where_clause_span, + E0647, + "start function is not allowed to have a `where` clause" + ) + .span_label( + generics.where_clause_span, + "start function cannot have a `where` clause", + ) + .emit(); + error = true; + } + if let hir::IsAsync::Async = sig.header.asyncness { + let span = tcx.def_span(it.owner_id); + struct_span_err!( + tcx.sess, + span, + E0752, + "`start` is not allowed to be `async`" + ) + .span_label(span, "`start` is not allowed to be `async`") + .emit(); + error = true; + } + + let attrs = tcx.hir().attrs(start_id); + for attr in attrs { + if attr.has_name(sym::track_caller) { + tcx.sess + .struct_span_err( + attr.span, + "`start` is not allowed to be `#[track_caller]`", + ) + .span_label( + start_span, + "`start` is not allowed to be `#[track_caller]`", + ) + .emit(); + error = true; + } + } + + if error { + return; + } + } + } + + let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig( + [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(), + tcx.types.isize, + false, + hir::Unsafety::Normal, + Abi::Rust, + ))); + + require_same_types( + tcx, + &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType), + se_ty, + tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)), + ); + } + _ => { + span_bug!(start_span, "start has a non-function type: found `{}`", start_t); + } + } +} + +fn check_for_entry_fn(tcx: TyCtxt<'_>) { + match tcx.entry_fn(()) { + Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), + Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), + _ => {} + } +} + +pub fn provide(providers: &mut Providers) { + collect::provide(providers); + coherence::provide(providers); + check::provide(providers); + variance::provide(providers); + outlives::provide(providers); + impl_wf_check::provide(providers); + hir_wf_check::provide(providers); +} + +pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { + let _prof_timer = tcx.sess.timer("type_check_crate"); + + // this ensures that later parts of type checking can assume that items + // have valid types and not error + // FIXME(matthewjasper) We shouldn't need to use `track_errors`. + tcx.sess.track_errors(|| { + tcx.sess.time("type_collecting", || { + tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module)) + }); + })?; + + if tcx.features().rustc_attrs { + tcx.sess.track_errors(|| { + tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); + })?; + } + + tcx.sess.track_errors(|| { + tcx.sess.time("impl_wf_inference", || { + tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)) + }); + })?; + + tcx.sess.track_errors(|| { + tcx.sess.time("coherence_checking", || { + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { + tcx.ensure().coherent_trait(trait_def_id); + } + + // these queries are executed for side-effects (error reporting): + tcx.ensure().crate_inherent_impls(()); + tcx.ensure().crate_inherent_impls_overlap_check(()); + }); + })?; + + if tcx.features().rustc_attrs { + tcx.sess.track_errors(|| { + tcx.sess.time("variance_testing", || variance::test::test_variance(tcx)); + })?; + } + + tcx.sess.track_errors(|| { + tcx.sess.time("wf_checking", || { + tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) + }); + })?; + + // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync. + tcx.sess.time("item_types_checking", || { + tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module)) + }); + + tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(())); + + check_unused::check_crate(tcx); + check_for_entry_fn(tcx); + + if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) } +} + +/// A quasi-deprecated helper used in rustdoc and clippy to get +/// the type from a HIR node. +pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + // In case there are any projections, etc., find the "environment" + // def-ID that will be used to determine the traits/predicates in + // scope. This is derived from the enclosing item-like thing. + let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); + <dyn AstConv<'_>>::ast_ty_to_ty(&item_cx, hir_ty) +} + +pub fn hir_trait_to_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + hir_trait: &hir::TraitRef<'_>, + self_ty: Ty<'tcx>, +) -> Bounds<'tcx> { + // In case there are any projections, etc., find the "environment" + // def-ID that will be used to determine the traits/predicates in + // scope. This is derived from the enclosing item-like thing. + let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); + let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); + let mut bounds = Bounds::default(); + let _ = <dyn AstConv<'_>>::instantiate_poly_trait_ref( + &item_cx, + hir_trait, + DUMMY_SP, + ty::BoundConstness::NotConst, + self_ty, + &mut bounds, + true, + ); + + bounds +} diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 7534482cc..7534482cc 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 3b779280e..90c6edb65 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -29,7 +29,7 @@ pub(super) fn infer_predicates<'tcx>( // Visit all the crates and infer predicates for id in tcx.hir().items() { - let item_did = id.def_id; + let item_did = id.owner_id; debug!("InferVisitor::visit_item(item={:?})", item_did); diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs new file mode 100644 index 000000000..e50c26765 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -0,0 +1,129 @@ +use hir::Node; +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_span::symbol::sym; +use rustc_span::Span; + +mod explicit; +mod implicit_infer; +/// Code to write unit test for outlives. +pub mod test; +mod utils; + +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)] { + 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() { + // 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) + // + // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ()); + // ^^^ ^^^^^^^ the def id we are calling + // ^^^ inferred_outlives_of on + // parent item we dont have set as the + // parent of generics returned by `generics_of` + // + // In the above code we want the anon const to have predicates in its param env for `'b: 'a` + let item_def_id = tcx.hir().get_parent_item(id); + // In the above code example we would be calling `inferred_outlives_of(Foo)` here + return tcx.inferred_outlives_of(item_def_id); + } + } + + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => { + let crate_map = tcx.inferred_outlives_crate(()); + + let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]); + + 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), + }) + .collect(); + pred.sort(); + + let span = tcx.def_span(item_def_id); + let mut err = tcx.sess.struct_span_err(span, "rustc_outlives"); + for p in &pred { + err.note(p); + } + err.emit(); + } + + debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates); + + predicates + } + + _ => &[], + }, + + _ => &[], + } +} + +fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { + // Compute a map from each struct/enum/union S to the **explicit** + // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote. + // Typically there won't be many of these, except in older code where + // they were mandatory. Nonetheless, we have to ensure that every such + // predicate is satisfied, so they form a kind of base set of requirements + // for the type. + + // Compute the inferred predicates + let global_inferred_outlives = implicit_infer::infer_predicates(tcx); + + // Convert the inferred predicates into the "collected" form the + // global data structure expects. + // + // FIXME -- consider correcting impedance mismatch in some way, + // probably by updating the global data structure. + let predicates = global_inferred_outlives + .iter() + .map(|(&def_id, set)| { + let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map( + |(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), + span, + )), + GenericArgKind::Lifetime(region1) => Some(( + ty::Binder::dummy(ty::PredicateKind::RegionOutlives( + ty::OutlivesPredicate(region1, *region2), + )) + .to_predicate(tcx), + span, + )), + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + None + } + } + }, + )); + (def_id, predicates) + }) + .collect(); + + ty::CratePredicatesMap { predicates } +} diff --git a/compiler/rustc_hir_analysis/src/outlives/test.rs b/compiler/rustc_hir_analysis/src/outlives/test.rs new file mode 100644 index 000000000..fa2ac5659 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/test.rs @@ -0,0 +1,21 @@ +use rustc_errors::struct_span_err; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; + +pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { + for id in tcx.hir().items() { + // For unit testing: check for a special "rustc_outlives" + // attribute and report an error with various results if found. + if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_outlives) { + let inferred_outlives_of = tcx.inferred_outlives_of(id.owner_id); + struct_span_err!( + tcx.sess, + tcx.def_span(id.owner_id), + E0640, + "{:?}", + inferred_outlives_of + ) + .emit(); + } + } +} diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs new file mode 100644 index 000000000..0409c7081 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -0,0 +1,186 @@ +use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Region, Ty, TyCtxt}; +use rustc_span::Span; +use smallvec::smallvec; +use std::collections::BTreeMap; + +/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred +/// must be added to the struct header. +pub(crate) type RequiredPredicates<'tcx> = + BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>; + +/// Given a requirement `T: 'a` or `'b: 'a`, deduce the +/// outlives_component and add it to `required_predicates` +pub(crate) fn insert_outlives_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + kind: GenericArg<'tcx>, + outlived_region: Region<'tcx>, + span: Span, + required_predicates: &mut RequiredPredicates<'tcx>, +) { + // If the `'a` region is bound within the field type itself, we + // don't want to propagate this constraint to the header. + if !is_free_region(outlived_region) { + return; + } + + match kind.unpack() { + GenericArgKind::Type(ty) => { + // `T: 'outlived_region` for some type `T` + // But T could be a lot of things: + // e.g., if `T = &'b u32`, then `'b: 'outlived_region` is + // what we want to add. + // + // Or if within `struct Foo<U>` you had `T = Vec<U>`, then + // we would want to add `U: 'outlived_region` + let mut components = smallvec![]; + push_outlives_components(tcx, ty, &mut components); + for component in components { + match component { + Component::Region(r) => { + // This would arise from something like: + // + // ``` + // struct Foo<'a, 'b> { + // x: &'a &'b u32 + // } + // ``` + // + // Here `outlived_region = 'a` and `kind = &'b + // u32`. Decomposing `&'b u32` into + // components would yield `'b`, and we add the + // where clause that `'b: 'a`. + insert_outlives_predicate( + tcx, + r.into(), + outlived_region, + span, + required_predicates, + ); + } + + Component::Param(param_ty) => { + // param_ty: ty::ParamTy + // This would arise from something like: + // + // ``` + // struct Foo<'a, U> { + // x: &'a Vec<U> + // } + // ``` + // + // Here `outlived_region = 'a` and `kind = + // Vec<U>`. Decomposing `Vec<U>` into + // components would yield `U`, and we add the + // where clause that `U: 'a`. + let ty: Ty<'tcx> = param_ty.to_ty(tcx); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::Projection(proj_ty) => { + // This would arise from something like: + // + // ``` + // struct Foo<'a, T: Iterator> { + // x: &'a <T as Iterator>::Item + // } + // ``` + // + // Here we want to add an explicit `where <T as Iterator>::Item: 'a`. + let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::Opaque(def_id, substs) => { + // This would arise from something like: + // + // ```rust + // type Opaque<T> = impl Sized; + // fn defining<T>() -> Opaque<T> {} + // struct Ss<'a, T>(&'a Opaque<T>); + // ``` + // + // Here we want to have an implied bound `Opaque<T>: 'a` + + let ty = tcx.mk_opaque(def_id, substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::EscapingProjection(_) => { + // As above, but the projection involves + // late-bound regions. Therefore, the WF + // requirement is not checked in type definition + // but at fn call site, so ignore it. + // + // ``` + // struct Foo<'a, T: Iterator> { + // x: for<'b> fn(<&'b T as Iterator>::Item) + // // ^^^^^^^^^^^^^^^^^^^^^^^^^ + // } + // ``` + // + // Since `'b` is not in scope on `Foo`, can't + // do anything here, ignore it. + } + + Component::UnresolvedInferenceVariable(_) => bug!("not using infcx"), + } + } + } + + GenericArgKind::Lifetime(r) => { + if !is_free_region(r) { + return; + } + required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span); + } + + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + } + } +} + +fn is_free_region(region: Region<'_>) -> bool { + // First, screen for regions that might appear in a type header. + match *region { + // These correspond to `T: 'a` relationships: + // + // struct Foo<'a, T> { + // field: &'a T, // this would generate a ReEarlyBound referencing `'a` + // } + // + // We care about these, so fall through. + ty::ReEarlyBound(_) => true, + + // These correspond to `T: 'static` relationships which can be + // rather surprising. + // + // struct Foo<'a, T> { + // field: &'static T, // this would generate a ReStatic + // } + ty::ReStatic => false, + + // Late-bound regions can appear in `fn` types: + // + // struct Foo<T> { + // field: for<'b> fn(&'b T) // e.g., 'b here + // } + // + // The type above might generate a `T: 'b` bound, but we can + // ignore it. We can't put it on the struct header anyway. + ty::ReLateBound(..) => false, + + // These regions don't appear in types from type declarations: + ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { + bug!("unexpected region in outlives inference: {:?}", region); + } + } +} diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs index 0b46fce17..0b46fce17 100644 --- a/compiler/rustc_typeck/src/structured_errors.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors.rs diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs index 324df313e..324df313e 100644 --- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs index bb6088054..bb6088054 100644 --- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 99729391e..435912464 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -4,7 +4,6 @@ use rustc_errors::{ MultiSpan, }; use rustc_hir as hir; -use rustc_middle::hir::map::fn_sig; use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::DefId; @@ -292,62 +291,60 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // Creates lifetime name suggestions from the lifetime parameter names fn get_lifetime_args_suggestions_from_param_names( &self, - path_hir_id: Option<hir::HirId>, + path_hir_id: hir::HirId, num_params_to_take: usize, ) -> String { debug!(?path_hir_id); - if let Some(path_hir_id) = path_hir_id { - let mut ret = Vec::new(); - 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()) - })); - // Suggest `'static` when in const/static item-like. - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. }, - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const { .. }, - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Const { .. }, - .. - }) - | hir::Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Static { .. }, - .. - }) - | hir::Node::AnonConst(..) = node - { - ret.extend( - std::iter::repeat("'static".to_owned()) - .take(num_params_to_take.saturating_sub(ret.len())), - ); - } - if ret.len() >= num_params_to_take { - return ret[..num_params_to_take].join(", "); - } - // We cannot refer to lifetimes defined in an outer function. - if let hir::Node::Item(_) = node { - break; - } + let mut ret = Vec::new(); + 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()) + })); + // Suggest `'static` when in const/static item-like. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. }, + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const { .. }, + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const { .. }, + .. + }) + | hir::Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Static { .. }, + .. + }) + | hir::Node::AnonConst(..) = node + { + ret.extend( + std::iter::repeat("'static".to_owned()) + .take(num_params_to_take.saturating_sub(ret.len())), + ); + } + if ret.len() >= num_params_to_take { + return ret[..num_params_to_take].join(", "); + } + // We cannot refer to lifetimes defined in an outer function. + if let hir::Node::Item(_) = node { + break; } } @@ -368,7 +365,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, num_params_to_take: usize, ) -> String { - let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(fn_sig); + let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig); let is_used_in_input = |def_id| { fn_sig.map_or(false, |fn_sig| { fn_sig.decl.inputs.iter().any(|ty| match ty.kind { @@ -524,6 +521,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { if self.not_enough_args_provided() { self.suggest_adding_args(err); } else if self.too_many_args_provided() { + self.suggest_moving_args_from_assoc_fn_to_trait(err); self.suggest_removing_args_or_generics(err); } else { unreachable!(); @@ -654,6 +652,144 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } + /// Suggests moving redundant argument(s) of an associate function to the + /// trait it belongs to. + /// + /// ```compile_fail + /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)` + /// ``` + fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) { + let trait_ = match self.tcx.trait_of_item(self.def_id) { + Some(def_id) => def_id, + None => return, + }; + + // Skip suggestion when the associated function is itself generic, it is unclear + // how to split the provided parameters between those to suggest to the trait and + // those to remain on the associated type. + let num_assoc_fn_expected_args = + self.num_expected_type_or_const_args() + self.num_expected_lifetime_args(); + if num_assoc_fn_expected_args > 0 { + return; + } + + let num_assoc_fn_excess_args = + self.num_excess_type_or_const_args() + self.num_excess_lifetime_args(); + + let trait_generics = self.tcx.generics_of(trait_); + let num_trait_generics_except_self = + trait_generics.count() - if trait_generics.has_self { 1 } else { 0 }; + + let msg = format!( + "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}", + these = pluralize!("this", num_assoc_fn_excess_args), + s = pluralize!(num_assoc_fn_excess_args), + name = self.tcx.item_name(trait_), + num = num_trait_generics_except_self, + ); + + if let Some(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.hir_id) + && let Some(parent_node) = self.tcx.hir().find(parent_node) + && let hir::Node::Expr(expr) = parent_node { + match expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + err, + qpath, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + hir::ExprKind::MethodCall(..) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + err, + trait_, + expr, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + _ => return, + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + &self, + err: &mut Diagnostic, + qpath: &'tcx hir::QPath<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + if let hir::QPath::Resolved(_, path) = qpath + && 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 let Some(span) = self.gen_args.span_ext() + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let sugg = vec![ + (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)), + (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned()) + ]; + + err.multipart_suggestion( + msg, + sugg, + Applicability::MaybeIncorrect + ); + } + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + &self, + err: &mut Diagnostic, + trait_def_id: DefId, + expr: &'tcx hir::Expr<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + let sm = self.tcx.sess.source_map(); + let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else { return; }; + if num_assoc_fn_excess_args != num_trait_generics_except_self { + return; + } + let Some(gen_args) = self.gen_args.span_ext() else { return; }; + let Ok(generics) = sm.span_to_snippet(gen_args) else { return; }; + let Ok(rcvr) = sm.span_to_snippet( + rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span) + ) else { return; }; + let Ok(rest) = + (match args { + [] => Ok(String::new()), + [arg] => sm.span_to_snippet( + arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span), + ), + [first, .., last] => { + let first_span = + first.span.find_ancestor_inside(expr.span).unwrap_or(first.span); + let last_span = + last.span.find_ancestor_inside(expr.span).unwrap_or(last.span); + sm.span_to_snippet(first_span.to(last_span)) + } + }) else { return; }; + let comma = if args.len() > 0 { ", " } else { "" }; + let trait_path = self.tcx.def_path_str(trait_def_id); + let method_name = self.tcx.item_name(self.def_id); + err.span_suggestion( + expr.span, + msg, + format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"), + Applicability::MaybeIncorrect, + ); + } + /// Suggests to remove redundant argument(s): /// /// ```text @@ -763,16 +899,13 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // If there is a single unbound associated type and a single excess generic param // suggest replacing the generic param with the associated type bound if provided_args_matches_unbound_traits && !unbound_types.is_empty() { - let mut suggestions = vec![]; let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..]; - for (potential, name) in iter::zip(unused_generics, &unbound_types) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) { - suggestions.push((potential.span(), format!("{} = {}", name, snippet))); - } - } + let suggestions = iter::zip(unused_generics, &unbound_types) + .map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = "))) + .collect::<Vec<_>>(); if !suggestions.is_empty() { - err.multipart_suggestion( + err.multipart_suggestion_verbose( &format!( "replace the generic bound{s} with the associated type{s}", s = pluralize!(unbound_types.len()) diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index d79450e1a..eaf0310d5 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -257,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_invariant_substs(current, substs, variance); } - ty::Dynamic(data, r) => { + ty::Dynamic(data, r, _) => { // The type `Foo<T+'a>` is contravariant w/r/t `'a`: let contra = self.contravariant(variance); self.add_constraints_from_region(current, r, contra); @@ -271,11 +271,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } for projection in data.projection_bounds() { - match projection.skip_binder().term { - ty::Term::Ty(ty) => { + match projection.skip_binder().term.unpack() { + ty::TermKind::Ty(ty) => { self.add_constraints_from_ty(current, ty, self.invariant); } - ty::Term::Const(c) => { + ty::TermKind::Const(c) => { self.add_constraints_from_const(current, c, self.invariant) } } @@ -411,11 +411,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // way early-bound regions do, so we skip them here. } - ty::ReFree(..) - | ty::ReVar(..) - | ty::RePlaceholder(..) - | ty::ReEmpty(_) - | ty::ReErased => { + ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. bug!( diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 82103c5a0..82103c5a0 100644 --- a/compiler/rustc_typeck/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs index 97aca621a..97aca621a 100644 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ b/compiler/rustc_hir_analysis/src/variance/solve.rs diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 1f763011e..1f763011e 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs diff --git a/compiler/rustc_hir_analysis/src/variance/test.rs b/compiler/rustc_hir_analysis/src/variance/test.rs new file mode 100644 index 000000000..83ed3e44b --- /dev/null +++ b/compiler/rustc_hir_analysis/src/variance/test.rs @@ -0,0 +1,15 @@ +use rustc_errors::struct_span_err; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; + +pub fn test_variance(tcx: TyCtxt<'_>) { + // For unit testing: check for a special "rustc_variance" + // attribute and report an error with various results if found. + for id in tcx.hir().items() { + if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_variance) { + let variances_of = tcx.variances_of(id.owner_id); + struct_span_err!(tcx.sess, tcx.def_span(id.owner_id), E0208, "{:?}", variances_of) + .emit(); + } + } +} diff --git a/compiler/rustc_typeck/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs index 027f0859f..027f0859f 100644 --- a/compiler/rustc_typeck/src/variance/xform.rs +++ b/compiler/rustc_hir_analysis/src/variance/xform.rs diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml index 46a8e7dee..1ea7be1ae 100644 --- a/compiler/rustc_hir_pretty/Cargo.toml +++ b/compiler/rustc_hir_pretty/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e0179bd3e..da27554a2 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1,4 +1,6 @@ #![recursion_limit = "256"] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] use rustc_ast as ast; use rustc_ast::util::parser::{self, AssocOp, Fixity}; @@ -7,7 +9,9 @@ use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; use rustc_hir::LifetimeParamKind; -use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term}; +use rustc_hir::{ + BindingAnnotation, ByRef, GenericArg, GenericParam, GenericParamKind, Mutability, Node, Term, +}; use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol}; @@ -83,12 +87,14 @@ impl<'a> State<'a> { Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), Node::Expr(a) => self.print_expr(a), + Node::ExprField(a) => self.print_expr_field(&a), Node::Stmt(a) => self.print_stmt(a), Node::PathSegment(a) => self.print_path_segment(a), Node::Ty(a) => self.print_type(a), Node::TypeBinding(a) => self.print_type_binding(a), Node::TraitRef(a) => self.print_trait_ref(a), Node::Pat(a) => self.print_pat(a), + Node::PatField(a) => self.print_patfield(&a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::Block(a) => { @@ -881,7 +887,7 @@ impl<'a> State<'a> { self.end(); // need to close a box self.ann.nested(self, Nested::Body(body)); } - hir::ImplItemKind::TyAlias(ty) => { + hir::ImplItemKind::Type(ty) => { self.print_associated_type(ii.ident, ii.generics, None, Some(ty)); } } @@ -911,6 +917,10 @@ impl<'a> State<'a> { if let Some(els) = els { self.nbsp(); self.word_space("else"); + // containing cbox, will be closed by print-block at `}` + self.cbox(0); + // head-box, will be closed by print-block after `{` + self.ibox(0); self.print_block(els); } @@ -1123,20 +1133,7 @@ impl<'a> State<'a> { ) { self.print_qpath(qpath, true); self.word("{"); - self.commasep_cmnt( - Consistent, - fields, - |s, field| { - s.ibox(INDENT_UNIT); - if !field.is_shorthand { - s.print_ident(field.ident); - s.word_space(":"); - } - s.print_expr(field.expr); - s.end() - }, - |f| f.span, - ); + self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); if let Some(expr) = wth { self.ibox(INDENT_UNIT); if !fields.is_empty() { @@ -1153,6 +1150,20 @@ impl<'a> State<'a> { self.word("}"); } + fn print_expr_field(&mut self, field: &hir::ExprField<'_>) { + if self.attrs(field.hir_id).is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.print_outer_attributes(&self.attrs(field.hir_id)); + if !field.is_shorthand { + self.print_ident(field.ident); + self.word_space(":"); + } + self.print_expr(&field.expr); + self.end() + } + fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { self.popen(); self.commasep_exprs(Inconsistent, exprs); @@ -1172,15 +1183,20 @@ impl<'a> State<'a> { self.print_call_post(args) } - fn print_expr_method_call(&mut self, segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>]) { - let base_args = &args[1..]; - self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); + fn print_expr_method_call( + &mut self, + segment: &hir::PathSegment<'_>, + receiver: &hir::Expr<'_>, + args: &[hir::Expr<'_>], + ) { + let base_args = args; + self.print_expr_maybe_paren(&receiver, parser::PREC_POSTFIX); self.word("."); self.print_ident(segment.ident); let generic_args = segment.args(); if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() { - self.print_generic_args(generic_args, segment.infer_args, true); + self.print_generic_args(generic_args, true); } self.print_call_post(base_args) @@ -1240,7 +1256,7 @@ impl<'a> State<'a> { fn print_literal(&mut self, lit: &hir::Lit) { self.maybe_print_comment(lit.span.lo()); - self.word(lit.node.to_lit_token().to_string()) + self.word(lit.node.to_token_lit().to_string()) } fn print_inline_asm(&mut self, asm: &hir::InlineAsm<'_>) { @@ -1385,8 +1401,8 @@ impl<'a> State<'a> { hir::ExprKind::Call(func, args) => { self.print_expr_call(func, args); } - hir::ExprKind::MethodCall(segment, args, _) => { - self.print_expr_method_call(segment, args); + hir::ExprKind::MethodCall(segment, receiver, args, _) => { + self.print_expr_method_call(segment, receiver, args); } hir::ExprKind::Binary(op, lhs, rhs) => { self.print_expr_binary(op, lhs, rhs); @@ -1583,7 +1599,7 @@ impl<'a> State<'a> { } if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); - self.print_generic_args(segment.args(), segment.infer_args, colons_before_params); + self.print_generic_args(segment.args(), colons_before_params); } } } @@ -1591,7 +1607,7 @@ impl<'a> State<'a> { pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) { if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); - self.print_generic_args(segment.args(), segment.infer_args, false); + self.print_generic_args(segment.args(), false); } } @@ -1610,11 +1626,7 @@ impl<'a> State<'a> { } if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); - self.print_generic_args( - segment.args(), - segment.infer_args, - colons_before_params, - ); + self.print_generic_args(segment.args(), colons_before_params); } } @@ -1622,11 +1634,7 @@ impl<'a> State<'a> { self.word("::"); let item_segment = path.segments.last().unwrap(); self.print_ident(item_segment.ident); - self.print_generic_args( - item_segment.args(), - item_segment.infer_args, - colons_before_params, - ) + self.print_generic_args(item_segment.args(), colons_before_params) } hir::QPath::TypeRelative(qself, item_segment) => { // If we've got a compound-qualified-path, let's push an additional pair of angle @@ -1642,11 +1650,7 @@ impl<'a> State<'a> { self.word("::"); self.print_ident(item_segment.ident); - self.print_generic_args( - item_segment.args(), - item_segment.infer_args, - colons_before_params, - ) + self.print_generic_args(item_segment.args(), colons_before_params) } hir::QPath::LangItem(lang_item, span, _) => { self.word("#[lang = \""); @@ -1659,7 +1663,6 @@ impl<'a> State<'a> { fn print_generic_args( &mut self, generic_args: &hir::GenericArgs<'_>, - infer_args: bool, colons_before_params: bool, ) { if generic_args.parenthesized { @@ -1684,7 +1687,11 @@ impl<'a> State<'a> { let mut nonelided_generic_args: bool = false; let elide_lifetimes = generic_args.args.iter().all(|arg| match arg { - GenericArg::Lifetime(lt) => lt.is_elided(), + GenericArg::Lifetime(lt) if lt.is_elided() => true, + GenericArg::Lifetime(_) => { + nonelided_generic_args = true; + false + } _ => { nonelided_generic_args = true; true @@ -1706,13 +1713,6 @@ impl<'a> State<'a> { ); } - // FIXME(eddyb): this would leak into error messages (e.g., - // "non-exhaustive patterns: `Some::<..>(_)` not covered"). - if infer_args && false { - start_or_comma(self); - self.word(".."); - } - for binding in generic_args.bindings { start_or_comma(self); self.print_type_binding(binding); @@ -1726,7 +1726,7 @@ impl<'a> State<'a> { pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) { self.print_ident(binding.ident); - self.print_generic_args(binding.gen_args, false, false); + self.print_generic_args(binding.gen_args, false); self.space(); match binding.kind { hir::TypeBindingKind::Equality { ref term } => { @@ -1749,20 +1749,12 @@ impl<'a> State<'a> { // is that it doesn't matter match pat.kind { PatKind::Wild => self.word("_"), - PatKind::Binding(binding_mode, _, ident, sub) => { - match binding_mode { - hir::BindingAnnotation::Ref => { - self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Not, false); - } - hir::BindingAnnotation::RefMut => { - self.word_nbsp("ref"); - self.print_mutability(hir::Mutability::Mut, false); - } - hir::BindingAnnotation::Unannotated => {} - hir::BindingAnnotation::Mutable => { - self.word_nbsp("mut"); - } + PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => { + if by_ref == ByRef::Yes { + self.word_nbsp("ref"); + } + if mutbl == Mutability::Mut { + self.word_nbsp("mut"); } self.print_ident(ident); if let Some(p) = sub { @@ -1773,7 +1765,8 @@ impl<'a> State<'a> { PatKind::TupleStruct(ref qpath, elts, ddpos) => { self.print_qpath(qpath, true); self.popen(); - if let Some(ddpos) = ddpos { + if let Some(ddpos) = ddpos.as_opt_usize() { + let ddpos = ddpos as usize; self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); @@ -1799,20 +1792,7 @@ impl<'a> State<'a> { if !empty { self.space(); } - self.commasep_cmnt( - Consistent, - fields, - |s, f| { - s.cbox(INDENT_UNIT); - if !f.is_shorthand { - s.print_ident(f.ident); - s.word_nbsp(":"); - } - s.print_pat(f.pat); - s.end() - }, - |f| f.pat.span, - ); + self.commasep_cmnt(Consistent, &fields, |s, f| s.print_patfield(f), |f| f.pat.span); if etc { if !fields.is_empty() { self.word_space(","); @@ -1829,7 +1809,7 @@ impl<'a> State<'a> { } PatKind::Tuple(elts, ddpos) => { self.popen(); - if let Some(ddpos) = ddpos { + if let Some(ddpos) = ddpos.as_opt_usize() { self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); @@ -1907,6 +1887,20 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Pat(pat)) } + pub fn print_patfield(&mut self, field: &hir::PatField<'_>) { + if self.attrs(field.hir_id).is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.print_outer_attributes(&self.attrs(field.hir_id)); + if !field.is_shorthand { + self.print_ident(field.ident); + self.word_nbsp(":"); + } + self.print_pat(field.pat); + self.end(); + } + pub fn print_param(&mut self, arg: &hir::Param<'_>) { self.print_outer_attributes(self.attrs(arg.hir_id)); self.print_pat(arg.pat); @@ -2403,9 +2397,9 @@ fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { contains_exterior_struct_lit(x) } - hir::ExprKind::MethodCall(.., exprs, _) => { + hir::ExprKind::MethodCall(_, receiver, ..) => { // `X { y: 1 }.bar(...)` - contains_exterior_struct_lit(&exprs[0]) + contains_exterior_struct_lit(receiver) } _ => false, diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml new file mode 100644 index 000000000..093f9bb84 --- /dev/null +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "rustc_hir_typeck" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +tracing = "0.1" +rustc_ast = { path = "../rustc_ast" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_graphviz = { path = "../rustc_graphviz" } +rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } +rustc_hir = { path = "../rustc_hir" } +rustc_hir_analysis = { path = "../rustc_hir_analysis" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } +rustc_lint = { path = "../rustc_lint" } +rustc_middle = { path = "../rustc_middle" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_type_ir = { path = "../rustc_type_ir" } diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 1b13c98e4..2b15d4dcd 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,10 +1,10 @@ -use crate::check::coercion::{AsCoercionSite, CoerceMany}; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; +use crate::coercion::{AsCoercionSite, CoerceMany}; +use crate::{Diverges, Expectation, FnCtxt, Needs}; use rustc_errors::{Applicability, 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, TypeVisitable}; +use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub fn check_match( &self, expr: &'tcx hir::Expr<'tcx>, @@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arm_ty = self.check_expr_with_expectation(&arm.body, expected); all_arms_diverge &= self.diverges.get(); - let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected); + let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| { + self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected) + }); let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { (Some(blk.hir_id), self.find_block_span(blk)) @@ -135,9 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err| { - let Some(ret) = self.ret_type_span else { - return; - }; + 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 }; @@ -210,9 +216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - let match_ty = coercion.complete(self); - debug!(?match_ty); - match_ty + coercion.complete(self) } /// When the previously checked expression (the scrutinee) diverges, @@ -255,7 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("consider adding an `else` block that evaluates to the expected type"); error = true; }, - ret_reason.is_none(), + false, ); error } @@ -468,53 +472,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - // we check if the different arms would work with boxed trait objects instead and - // provide a structured suggestion in that case. + /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` + /// we check if the different arms would work with boxed trait objects instead and + /// provide a structured suggestion in that case. pub(crate) fn opt_suggest_box_span( &self, - outer_ty: Ty<'tcx>, + first_ty: Ty<'tcx>, + second_ty: Ty<'tcx>, orig_expected: Expectation<'tcx>, ) -> Option<Span> { + // FIXME(compiler-errors): This really shouldn't need to be done during the + // "good" path of typeck, but here we are. match orig_expected { - Expectation::ExpectHasType(expected) - if self.in_tail_expr - && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types() - && self.can_coerce(outer_ty, expected) => - { - let obligations = self.fulfillment_cx.borrow().pending_obligations(); - let mut suggest_box = !obligations.is_empty(); - for o in obligations { - match o.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(t) => { - let pred = - ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: t.def_id(), - substs: self.tcx.mk_substs_trait(outer_ty, &[]), - }, - constness: t.constness, - polarity: t.polarity, - })); - let obl = Obligation::new( - o.cause.clone(), - self.param_env, - pred.to_predicate(self.tcx), - ); - suggest_box &= self.predicate_must_hold_modulo_regions(&obl); - if !suggest_box { - // We've encountered some obligation that didn't hold, so the - // return expression can't just be boxed. We don't need to - // evaluate the rest of the obligations. - break; + Expectation::ExpectHasType(expected) => { + let TypeVariableOrigin { + span, + kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id), + .. + } = 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 substs = sig.output().walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Opaque(def_id, substs) = *ty.kind() + && def_id == rpit_def_id + { + Some(substs) + } else { + None + } + })?; + let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs); + + if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { + return None; + } + + for ty in [first_ty, second_ty] { + for (pred, _) in self + .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) => { + 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::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) + } + _ => continue, + }; + if !self.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::misc(span, self.body_id), + self.param_env, + pred, + )) { + return None; } - _ => {} } } - // If all the obligations hold (or there are no obligations) the tail expression - // we can suggest to return a boxed trait object instead of an opaque type. - if suggest_box { self.ret_type_span } else { None } + + Some(span) } _ => None, } diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 59c366ad7..59c366ad7 100644 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs new file mode 100644 index 000000000..1b33f2f02 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -0,0 +1,831 @@ +use super::method::probe::{IsSuggestion, Mode, ProbeScope}; +use super::method::MethodCallee; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; + +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_id::DefId; +use rustc_infer::{ + infer, + traits::{self, Obligation}, +}; +use rustc_infer::{ + infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + traits::ObligationCause, +}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::SubstsRef; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; +use rustc_target::spec::abi; +use rustc_trait_selection::autoderef::Autoderef; +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; + +/// Checks that it is legal to call methods of the trait corresponding +/// to `trait_id` (this only cares about the trait, not the specific +/// method that is called). +pub fn check_legal_trait_for_method_call( + tcx: TyCtxt<'_>, + span: Span, + receiver: Option<Span>, + expr_span: Span, + trait_id: DefId, +) { + if tcx.lang_items().drop_trait() == Some(trait_id) { + let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); + err.span_label(span, "explicit destructor calls not allowed"); + + let (sp, suggestion) = receiver + .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) + .filter(|snippet| !snippet.is_empty()) + .map(|snippet| (expr_span, format!("drop({snippet})"))) + .unwrap_or_else(|| (span, "drop".to_string())); + + err.span_suggestion( + sp, + "consider using `drop` function", + suggestion, + Applicability::MaybeIncorrect, + ); + + err.emit(); + } +} + +#[derive(Debug)] +enum CallStep<'tcx> { + Builtin(Ty<'tcx>), + DeferredClosure(LocalDefId, ty::FnSig<'tcx>), + /// E.g., enum variant constructors. + Overloaded(MethodCallee<'tcx>), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn check_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let original_callee_ty = match &callee_expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self + .check_expr_with_expectation_and_args( + callee_expr, + Expectation::NoExpectation, + arg_exprs, + ), + _ => self.check_expr(callee_expr), + }; + + let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); + + let mut autoderef = self.autoderef(callee_expr.span, expr_ty); + let mut result = None; + while result.is_none() && autoderef.next().is_some() { + result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); + } + self.register_predicates(autoderef.into_obligations()); + + let output = match result { + None => { + // this will report an error since original_callee_ty is not a fn + self.confirm_builtin_call( + call_expr, + callee_expr, + original_callee_ty, + arg_exprs, + expected, + ) + } + + Some(CallStep::Builtin(callee_ty)) => { + self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) + } + + Some(CallStep::DeferredClosure(def_id, fn_sig)) => { + self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig) + } + + Some(CallStep::Overloaded(method_callee)) => { + self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) + } + }; + + // we must check that return type of called functions is WF: + self.register_wf_obligation(output.into(), call_expr.span, traits::WellFormed(None)); + + output + } + + fn try_overloaded_call_step( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + autoderef: &Autoderef<'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() { + ty::FnDef(..) | ty::FnPtr(_) => { + let adjustments = self.adjust_steps(autoderef); + self.apply_adjustments(callee_expr, adjustments); + return Some(CallStep::Builtin(adjusted_ty)); + } + + ty::Closure(def_id, substs) => { + let def_id = def_id.expect_local(); + + // Check whether this is a call to a closure where we + // haven't yet decided on whether the closure is fn vs + // fnmut vs fnonce. If so, we have to defer further processing. + if self.closure_kind(substs).is_none() { + let closure_sig = substs.as_closure().sig(); + let closure_sig = self.replace_bound_vars_with_fresh_vars( + call_expr.span, + infer::FnCall, + closure_sig, + ); + let adjustments = self.adjust_steps(autoderef); + self.record_deferred_call_resolution( + def_id, + DeferredCallResolution { + call_expr, + callee_expr, + adjusted_ty, + adjustments, + fn_sig: closure_sig, + closure_substs: substs, + }, + ); + return Some(CallStep::DeferredClosure(def_id, closure_sig)); + } + } + + // 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, ())`. + ty::Ref(..) if autoderef.step_count() == 0 => { + return None; + } + + ty::Error(_) => { + return None; + } + + _ => {} + } + + // Now, we look for the implementation of a Fn trait on the object's type. + // We first do it with the explicit instruction to look for an impl of + // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding + // to the number of call parameters. + // If that fails (or_else branch), we try again without specifying the + // shape of the tuple (hence the None). This allows to detect an Fn trait + // is implemented, and use this information for diagnostic. + self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) + .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) + .map(|(autoref, method)| { + let mut adjustments = self.adjust_steps(autoderef); + adjustments.extend(autoref); + self.apply_adjustments(callee_expr, adjustments); + CallStep::Overloaded(method) + }) + } + + fn try_overloaded_call_traits( + &self, + call_expr: &hir::Expr<'_>, + adjusted_ty: Ty<'tcx>, + opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, + ) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> { + // Try the options that are least restrictive on the caller first. + for (opt_trait_def_id, method_name, borrow) in [ + (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), + (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), + (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), + ] { + 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| { + 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, + ) { + let method = self.register_infer_ok_obligations(ok); + let mut autoref = None; + if borrow { + // Check for &self vs &mut self in the method signature. Since this is either + // the Fn or FnMut trait, it should be one of those. + let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() else { + // The `fn`/`fn_mut` lang item is ill-formed, which should have + // caused an error elsewhere. + self.tcx + .sess + .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?"); + 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, + }, + }; + autoref = Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), + target: method.sig.inputs()[0], + }); + } + return Some((autoref, method)); + } + } + + None + } + + /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the + /// likely intention is to call the closure, suggest `(||{})()`. (#55851) + fn identify_bad_closure_def_and_call( + &self, + err: &mut Diagnostic, + hir_id: hir::HirId, + callee_node: &hir::ExprKind<'_>, + callee_span: Span, + ) { + let hir = self.tcx.hir(); + let parent_hir_id = hir.get_parent_node(hir_id); + let parent_node = hir.get(parent_hir_id); + if let ( + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }), + .. + }), + hir::ExprKind::Block(..), + ) = (parent_node, callee_node) + { + let fn_decl_span = if hir.body(body).generator_kind + == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) + { + // Actually need to unwrap a few more layers of HIR to get to + // the _real_ closure... + let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), + .. + }) = hir.get(async_closure) + { + fn_decl_span + } else { + return; + } + } else { + fn_decl_span + }; + + let start = fn_decl_span.shrink_to_lo(); + let end = callee_span.shrink_to_hi(); + err.multipart_suggestion( + "if you meant to create this closure and immediately call it, surround the \ + closure with parentheses", + vec![(start, "(".to_string()), (end, ")".to_string())], + Applicability::MaybeIncorrect, + ); + } + } + + /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the + /// likely intention is to create an array containing tuples. + fn maybe_suggest_bad_array_definition( + &self, + err: &mut Diagnostic, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + ) -> bool { + let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id); + let parent_node = self.tcx.hir().get(hir_id); + if let ( + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), + hir::ExprKind::Tup(exp), + hir::ExprKind::Call(_, args), + ) = (parent_node, &callee_expr.kind, &call_expr.kind) + && args.len() == exp.len() + { + let start = callee_expr.span.shrink_to_hi(); + err.span_suggestion( + start, + "consider separating array elements with a comma", + ",", + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + + fn confirm_builtin_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + callee_ty: Ty<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let (fn_sig, def_id) = match *callee_ty.kind() { + ty::FnDef(def_id, subst) => { + let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst); + + // Unit testing: function items annotated with + // `#[rustc_evaluate_where_clauses]` trigger special output + // to let us test the trait evaluation system. + if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { + let predicates = self.tcx.predicates_of(def_id); + let predicates = predicates.instantiate(self.tcx, subst); + for (predicate, predicate_span) in + predicates.predicates.iter().zip(&predicates.spans) + { + let obligation = Obligation::new( + ObligationCause::dummy_with_span(callee_expr.span), + self.param_env, + *predicate, + ); + let result = self.evaluate_obligation(&obligation); + self.tcx + .sess + .struct_span_err( + callee_expr.span, + &format!("evaluate({:?}) = {:?}", predicate, result), + ) + .span_label(*predicate_span, "predicate") + .emit(); + } + } + (fn_sig, Some(def_id)) + } + ty::FnPtr(sig) => (sig, None), + _ => { + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind + && let [segment] = path.segments + && let Some(mut diag) = self + .tcx + .sess + .diagnostic() + .steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod) + { + // Try suggesting `foo(a)` -> `a.foo()` if possible. + if let Some(ty) = + self.suggest_call_as_method( + &mut diag, + segment, + arg_exprs, + call_expr, + expected + ) + { + diag.emit(); + return ty; + } else { + diag.emit(); + } + } + + self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); + + // This is the "default" function signature, used in case of error. + // In that case, we check each argument against "error" in order to + // set up all the node type bindings. + ( + ty::Binder::dummy(self.tcx.mk_fn_sig( + self.err_args(arg_exprs.len()).into_iter(), + self.tcx.ty_error(), + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + )), + None, + ) + } + }; + + // Replace any late-bound regions that appear in the function + // signature with region variables. We also have to + // renormalize the associated types at this point, since they + // 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); + + // Call the generic checker. + let expected_arg_tys = self.expected_inputs_for_expected_output( + call_expr.span, + expected, + fn_sig.output(), + fn_sig.inputs(), + ); + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + expected_arg_tys, + arg_exprs, + fn_sig.c_variadic, + TupleArgumentsFlag::DontTupleArguments, + def_id, + ); + + fn_sig.output() + } + + /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)` + /// and suggesting the fix if the method probe is successful. + fn suggest_call_as_method( + &self, + diag: &mut Diagnostic, + segment: &'tcx hir::PathSegment<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + call_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Option<Ty<'tcx>> { + if let [callee_expr, rest @ ..] = arg_exprs { + let callee_ty = self.check_expr(callee_expr); + // First, do a probe with `IsSuggestion(true)` to avoid emitting + // any strange errors. If it's successful, then we'll do a true + // method lookup. + let Ok(pick) = self + .probe_for_name( + call_expr.span, + Mode::MethodCall, + segment.ident, + IsSuggestion(true), + callee_ty, + call_expr.hir_id, + // We didn't record the in scope traits during late resolution + // so we need to probe AllTraits unfortunately + ProbeScope::AllTraits, + ) else { + return None; + }; + + let pick = self.confirm_method( + call_expr.span, + callee_expr, + call_expr, + callee_ty, + pick, + segment, + ); + if pick.illegal_sized_bound.is_some() { + return None; + } + + let up_to_rcvr_span = segment.ident.span.until(callee_expr.span); + let rest_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + let rest_snippet = if let Some(first) = rest.first() { + self.tcx + .sess + .source_map() + .span_to_snippet(first.span.to(call_expr.span.shrink_to_hi())) + } else { + Ok(")".to_string()) + }; + + if let Ok(rest_snippet) = rest_snippet { + let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX { + vec![ + (up_to_rcvr_span, "".to_string()), + (rest_span, format!(".{}({rest_snippet}", segment.ident)), + ] + } else { + vec![ + (up_to_rcvr_span, "(".to_string()), + (rest_span, format!(").{}({rest_snippet}", segment.ident)), + ] + }; + let self_ty = self.resolve_vars_if_possible(pick.callee.sig.inputs()[0]); + diag.multipart_suggestion( + format!( + "use the `.` operator to call the method `{}{}` on `{self_ty}`", + self.tcx + .associated_item(pick.callee.def_id) + .trait_container(self.tcx) + .map_or_else( + || String::new(), + |trait_def_id| self.tcx.def_path_str(trait_def_id) + "::" + ), + segment.ident + ), + sugg, + Applicability::MaybeIncorrect, + ); + + // Let's check the method fully now + let return_ty = self.check_method_argument_types( + segment.ident.span, + call_expr, + Ok(pick.callee), + rest, + TupleArgumentsFlag::DontTupleArguments, + expected, + ); + + return Some(return_ty); + } + } + + None + } + + fn report_invalid_callee( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + callee_ty: Ty<'tcx>, + arg_exprs: &'tcx [hir::Expr<'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), _) + = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + // Only suggest removing parens if there are no arguments + && arg_exprs.is_empty() + { + let descr = match kind { + def::CtorOf::Struct => "struct", + def::CtorOf::Variant => "enum variant", + }; + let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + unit_variant = Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); + } + + let callee_ty = self.resolve_vars_if_possible(callee_ty); + let mut err = type_error_struct!( + self.tcx.sess, + callee_expr.span, + callee_ty, + E0618, + "expected function, found {}", + match &unit_variant { + Some((_, kind, path)) => format!("{kind} `{path}`"), + None => format!("`{callee_ty}`"), + } + ); + + self.identify_bad_closure_def_and_call( + &mut err, + call_expr.hir_id, + &callee_expr.kind, + callee_expr.span, + ); + + if let Some((removal_span, kind, path)) = &unit_variant { + err.span_suggestion_verbose( + *removal_span, + &format!( + "`{path}` is a unit {kind}, and does not take parentheses to be constructed", + ), + "", + Applicability::MachineApplicable, + ); + } + + let mut inner_callee_path = None; + let def = match callee_expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + } + hir::ExprKind::Call(ref inner_callee, _) => { + // If the call spans more than one line and the callee kind is + // itself another `ExprCall`, that's a clue that we might just be + // missing a semicolon (Issue #51055) + let call_is_multiline = self.tcx.sess.source_map().is_multiline(call_expr.span); + if call_is_multiline { + err.span_suggestion( + callee_expr.span.shrink_to_hi(), + "consider using a semicolon here", + ";", + Applicability::MaybeIncorrect, + ); + } + if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { + inner_callee_path = Some(inner_qpath); + self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) + } else { + Res::Err + } + } + _ => Res::Err, + }; + + if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { + if let Some((maybe_def, output_ty, _)) = + self.extract_callable_info(callee_expr, callee_ty) + && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) + { + let descr = match maybe_def { + DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), + DefIdOrName::Name(name) => name, + }; + err.span_label( + callee_expr.span, + format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") + ); + if let DefIdOrName::DefId(def_id) = maybe_def + && let Some(def_span) = self.tcx.hir().span_if_local(def_id) + { + err.span_label(def_span, "the callable type is defined here"); + } + } else { + err.span_label(call_expr.span, "call expression requires function"); + } + } + + if let Some(span) = self.tcx.hir().res_span(def) { + let callee_ty = callee_ty.to_string(); + let label = match (unit_variant, inner_callee_path) { + (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), + (_, Some(hir::QPath::Resolved(_, path))) => self + .tcx + .sess + .source_map() + .span_to_snippet(path.span) + .ok() + .map(|p| format!("`{p}` defined here returns `{callee_ty}`")), + _ => { + match def { + // Emit a different diagnostic for local variables, as they are not + // type definitions themselves, but rather variables *of* that type. + Res::Local(hir_id) => Some(format!( + "`{}` has type `{}`", + self.tcx.hir().name(hir_id), + callee_ty + )), + Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { + Some(format!("`{}` defined here", self.tcx.def_path_str(def_id),)) + } + _ => Some(format!("`{callee_ty}` defined here")), + } + } + }; + if let Some(label) = label { + err.span_label(span, label); + } + } + err.emit(); + } + + fn confirm_deferred_closure_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + closure_def_id: LocalDefId, + fn_sig: ty::FnSig<'tcx>, + ) -> Ty<'tcx> { + // `fn_sig` is the *signature* of the closure being called. We + // don't know the full details yet (`Fn` vs `FnMut` etc), but we + // do know the types expected for each argument and the return + // type. + + let expected_arg_tys = self.expected_inputs_for_expected_output( + call_expr.span, + expected, + fn_sig.output(), + fn_sig.inputs(), + ); + + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + expected_arg_tys, + arg_exprs, + fn_sig.c_variadic, + TupleArgumentsFlag::TupleArguments, + Some(closure_def_id.to_def_id()), + ); + + fn_sig.output() + } + + fn confirm_overloaded_call( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + method_callee: MethodCallee<'tcx>, + ) -> Ty<'tcx> { + let output_type = self.check_method_argument_types( + call_expr.span, + call_expr, + Ok(method_callee), + arg_exprs, + TupleArgumentsFlag::TupleArguments, + expected, + ); + + self.write_method_call(call_expr.hir_id, method_callee); + output_type + } +} + +#[derive(Debug)] +pub struct DeferredCallResolution<'tcx> { + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + adjusted_ty: Ty<'tcx>, + adjustments: Vec<Adjustment<'tcx>>, + fn_sig: ty::FnSig<'tcx>, + closure_substs: SubstsRef<'tcx>, +} + +impl<'a, 'tcx> DeferredCallResolution<'tcx> { + pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) { + debug!("DeferredCallResolution::resolve() {:?}", self); + + // we should not be invoked until the closure kind has been + // determined by upvar inference + assert!(fcx.closure_kind(self.closure_substs).is_some()); + + // We may now know enough to figure out fn vs fnmut etc. + match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { + Some((autoref, method_callee)) => { + // One problem is that when we get here, we are going + // to have a newly instantiated function signature + // from the call trait. This has to be reconciled with + // the older function signature we had before. In + // principle we *should* be able to fn_sigs(), but we + // can't because of the annoying need for a TypeTrace. + // (This always bites me, should find a way to + // refactor it.) + let method_sig = method_callee.sig; + + debug!("attempt_resolution: method_callee={:?}", method_callee); + + for (method_arg_ty, self_arg_ty) in + iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs()) + { + fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty); + } + + fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); + + let mut adjustments = self.adjustments; + adjustments.extend(autoref); + fcx.apply_adjustments(self.callee_expr, adjustments); + + fcx.write_method_call(self.call_expr.hir_id, method_callee); + } + None => { + // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once` + // lang items are not defined (issue #86238). + let mut err = fcx.inh.tcx.sess.struct_span_err( + self.call_expr.span, + "failed to find an overloaded call trait for closure call", + ); + err.help( + "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \ + and have associated `call`/`call_mut`/`call_once` functions", + ); + err.emit(); + } + } + } +} diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7aaddc2bd..d1dab0540 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -30,35 +30,38 @@ use super::FnCtxt; -use crate::hir::def_id::DefId; use crate::type_error_struct; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; use rustc_session::lint; use rustc_session::Session; +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; 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. #[derive(Debug)] pub struct CastCheck<'tcx> { + /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, + /// The source type for the cast expression expr_ty: Ty<'tcx>, expr_span: Span, + /// The target type. That is, the type we are casting to. cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + /// whether the cast is made in a const context or not. + pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -96,13 +99,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(reported); } - if self.type_is_known_to_be_sized_modulo_regions(t, span) { + if self.type_is_sized_modulo_regions(self.param_env, t, span) { return Ok(Some(PointerKind::Thin)); } Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { None => Some(PointerKind::Thin), Some(f) => { @@ -139,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Generator(..) | ty::Adt(..) | ty::Never + | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { let reported = self .tcx @@ -173,6 +177,7 @@ pub enum CastError { /// or "a length". If this argument is None, then the metadata is unknown, for example, /// when we're typechecking a type parameter with a ?Sized bound. IntToFatCast(Option<&'static str>), + ForeignNonExhaustiveAdt, } impl From<ErrorGuaranteed> for CastError { @@ -207,15 +212,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + constness: hir::Constness, ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(..) | ty::Slice(..) => { + ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { let reported = check.report_cast_to_unsized_type(fcx); Err(reported) } @@ -523,7 +529,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic}; + use rustc_hir_analysis::structured_errors::{ + SizedUnsizedCast, StructuredDiagnostic, + }; SizedUnsizedCast { sess: &fcx.tcx.sess, @@ -591,6 +599,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { } err.emit(); } + CastError::ForeignNonExhaustiveAdt => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") + .emit(); + } } } @@ -670,19 +689,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { ("", lint::builtin::TRIVIAL_CASTS) }; - fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { - err.build(&format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - )) - .help(&format!( - "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" - )) - .emit(); - }); + fcx.tcx.struct_span_lint_hir( + lint, + self.expr.hir_id, + self.span, + DelayDm(|| { + format!( + "trivial {}cast: `{}` as `{}`", + adjective, + fcx.ty_to_string(t_expr), + fcx.ty_to_string(t_cast) + ) + }), + |lint| { + lint.help(format!( + "cast can be replaced by coercion; this might \ + require {type_asc_or}a temporary variable" + )) + }, + ); } #[instrument(skip(fcx), level = "debug")] @@ -692,7 +717,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) + if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span) && !self.cast_ty.has_infer_types() { self.report_cast_to_unsized_type(fcx); @@ -789,6 +814,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { _ => return Err(CastError::NonScalar), }; + if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { + if adt_def.did().krate != LOCAL_CRATE { + if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { + return Err(CastError::ForeignNonExhaustiveAdt); + } + } + } + match (t_from, t_cast) { // These types have invariants! can't cast into them. (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), @@ -835,6 +868,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + + (_, DynStar) | (DynStar, _) => { + if fcx.tcx.features().dyn_star { + bug!("should be handled by `try_coerce`") + } else { + Err(CastError::IllegalCast) + } + } } } @@ -976,12 +1017,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::CENUM_IMPL_DROP_CAST, self.expr.hir_id, self.span, - |err| { - err.build(&format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )) - .emit(); + DelayDm(|| format!( + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", + self.expr_ty, self.cast_ty + )), + |lint| { + lint }, ); } @@ -992,12 +1033,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( + DelayDm(|| format!( "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", self.expr_ty, self.cast_ty - )); - + )), + |lint| { let msg = "use `.addr()` to obtain the address of a pointer"; let expr_prec = self.expr.precedence().order(); @@ -1016,9 +1056,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { (cast_span, format!(").addr(){scalar_cast}")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); } else { - err.span_suggestion( + lint.span_suggestion( cast_span, msg, format!(".addr(){scalar_cast}"), @@ -1026,12 +1066,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } - err.help( + lint.help( "if you can't comply with strict provenance and need to expose the pointer \ provenance you can use `.expose_addr()` instead" ); - err.emit(); + lint }, ); } @@ -1041,32 +1081,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::FUZZY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( - "strict provenance disallows casting integer `{}` to pointer `{}`", - self.expr_ty, self.cast_ty - )); + DelayDm(|| format!( + "strict provenance disallows casting integer `{}` to pointer `{}`", + self.expr_ty, self.cast_ty + )), + |lint| { let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; let suggestions = vec![ (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")), (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - err.help( + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.help( "if you can't comply with strict provenance and don't have a pointer with \ the correct provenance you can use `std::ptr::from_exposed_addr()` instead" ); - err.emit(); + lint }, ); } } - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) - } -} diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs new file mode 100644 index 000000000..7f76364e1 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -0,0 +1,324 @@ +use crate::coercion::CoerceMany; +use crate::gather_locals::GatherLocalsVisitor; +use crate::{FnCtxt, Inherited}; +use crate::{GeneratorTypes, UnsafetyState}; +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; + +/// Helper used for fns and closures. Does the grungy work of checking a function +/// body and returns the function context used for that purpose, since in the case of a fn item +/// there is still a bit more to do. +/// +/// * ... +/// * inherited: other fields inherited from the enclosing fn (if any) +#[instrument(skip(inherited, body), level = "debug")] +pub(super) fn check_fn<'a, 'tcx>( + inherited: &'a Inherited<'tcx>, + param_env: ty::ParamEnv<'tcx>, + fn_sig: ty::FnSig<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + fn_id: hir::HirId, + 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; + + let tcx = fcx.tcx; + let hir = tcx.hir(); + + let declared_ret_ty = fn_sig.output(); + + let ret_ty = + fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars( + declared_ret_ty, + body.value.hir_id, + decl.output.span(), + 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))); + + let span = body.value.span; + + 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() + } 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); + + // 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()); + + fcx.resume_yield_tys = Some((resume_ty, yield_ty)); + } + + GatherLocalsVisitor::new(&fcx).visit_body(body); + + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let maybe_va_list = if fn_sig.c_variadic { + let span = body.params.last().unwrap().span; + let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); + let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); + + Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) + } else { + None + }; + + // Add formal parameters. + let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); + let inputs_fn = fn_sig.inputs().iter().copied(); + for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { + // Check the pattern. + let ty_span = try { inputs_hir?.get(idx)?.span }; + fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + + // Check that argument is Sized. + // The check for a non-trivial pattern is a hack to avoid duplicate warnings + // for simple cases like `fn foo(x: Trait)`, + // where we would error once on the parameter as a whole, and once on the binding `x`. + if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { + fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); + } + + fcx.write_ty(param.hir_id, param_ty); + } + + inherited.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() { + // 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 + // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, + // while keeping the current ordering we will ignore the tail expression's type because we + // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` + // because we will trigger "unreachable expression" lints unconditionally. + // Because of all of this, we perform a crude check to know whether the simplest `!Sized` + // case that a newcomer might make, returning a bare trait, and in that case we populate + // the tail expression's type so that the suggestion will be correct, but ignore all other + // possible cases. + fcx.check_expr(&body.value); + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + } else { + 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. + // resolve_generator_interiors relies on this property. + let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { + let interior = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); + fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); + + let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); + Some(GeneratorTypes { + resume_ty, + yield_ty, + interior, + movability: can_be_generator.unwrap(), + }) + } else { + None + }; + + // Finalize the return check by taking the LUB of the return types + // we saw and assigning it to the expected return type. This isn't + // really expected to fail, since the coercions would have failed + // earlier when trying to find a LUB. + let coercion = fcx.ret_coercion.take().unwrap().into_inner(); + let mut actual_return_ty = coercion.complete(&fcx); + debug!("actual_return_ty = {:?}", actual_return_ty); + if let ty::Dynamic(..) = declared_ret_ty.kind() { + // We have special-cased the case where the function is declared + // `-> dyn Foo` and we don't actually relate it to the + // `fcx.ret_coercion`, so just substitute a type variable. + actual_return_ty = + fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); + debug!("actual_return_ty replaced with {:?}", actual_return_ty); + } + + // HACK(oli-obk, compiler-errors): We should be comparing this against + // `declared_ret_ty`, but then anything uninferred would be inferred to + // the opaque type itself. That again would cause writeback to assume + // we have a recursive call site and do the sadly stabilized fallback to `()`. + fcx.demand_suptype(span, ret_ty, actual_return_ty); + + // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` + if let Some(panic_impl_did) = tcx.lang_items().panic_impl() + && panic_impl_did == hir.local_def_id(fn_id).to_def_id() + { + 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) +} + +fn check_panic_info_fn( + tcx: TyCtxt<'_>, + fn_id: LocalDefId, + fn_sig: ty::FnSig<'_>, + decl: &hir::FnDecl<'_>, + declared_ret_ty: Ty<'_>, +) { + let Some(panic_info_did) = tcx.lang_items().panic_info() else { + tcx.sess.err("language item required, but not found: `panic_info`"); + 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_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() + } + _ => false, + }, + _ => false, + }; + + if !arg_is_panic_info { + tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); + } + + let DefKind::Fn = tcx.def_kind(fn_id) else { + let span = tcx.def_span(fn_id); + tcx.sess.span_err(span, "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, "should have no type parameters"); + } + if generic_counts.consts != 0 { + let span = tcx.def_span(fn_id); + 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 new file mode 100644 index 000000000..a5a45f75e --- /dev/null +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -0,0 +1,824 @@ +//! Code for type-checking closure expressions. + +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::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_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::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)] +struct ExpectedSig<'tcx> { + /// Span that gave us this expectation, if we know that. + cause_span: Option<Span>, + sig: ty::PolyFnSig<'tcx>, +} + +struct ClosureSignatures<'tcx> { + /// The signature users of the closure see. + 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 + /// 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")] + 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>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + trace!("decl = {:#?}", decl); + trace!("expr = {:#?}", expr); + + // It's always helpful for inference if we know the kind of + // closure sooner rather than later, so first examine the expected + // type, and see if can glean a closure kind from there. + let (expected_sig, expected_kind) = match expected.to_option(self) { + 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) + } + + #[instrument(skip(self, expr, body, decl), level = "debug", ret)] + fn check_closure( + &self, + expr: &hir::Expr<'_>, + 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); + 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); + + debug!(?bound_sig, ?liberated_sig); + + let return_type_pre_known = !liberated_sig.output().is_ty_infer(); + + let generator_types = check_fn( + self, + self.param_env.without_const(), + liberated_sig, + decl, + expr.hir_id, + body, + gen, + return_type_pre_known, + ) + .1; + + let parent_substs = InternalSubsts::identity_for_item( + self.tcx, + self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), + ); + + let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: self.tcx.hir().span(expr.hir_id), + }); + + if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types + { + let generator_substs = ty::GeneratorSubsts::new( + self.tcx, + ty::GeneratorSubstsParts { + parent_substs, + resume_ty, + yield_ty, + return_ty: liberated_sig.output(), + witness: interior, + tupled_upvars_ty, + }, + ); + + return self.tcx.mk_generator( + expr_def_id.to_def_id(), + generator_substs.substs, + movability, + ); + } + + // Tuple up the arguments and insert the resulting function type into + // the `closures` table. + let sig = bound_sig.map_bound(|sig| { + self.tcx.mk_fn_sig( + iter::once(self.tcx.intern_tup(sig.inputs())), + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }); + + debug!(?sig, ?opt_kind); + + let closure_kind_ty = match opt_kind { + Some(kind) => kind.to_ty(self.tcx), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish closure kind inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr.span, + }), + }; + + let closure_substs = ty::ClosureSubsts::new( + self.tcx, + ty::ClosureSubstsParts { + parent_substs, + closure_kind_ty, + closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig), + tupled_upvars_ty, + }, + ); + + self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs) + } + + /// Given the expected type, figures out what it can about this closure we + /// are about to type check: + #[instrument(skip(self), level = "debug")] + fn deduce_expectations_from_expected_type( + &self, + 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::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); + self.deduce_sig_from_projection(None, pb) + }); + let kind = object_type + .principal_def_id() + .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); + (sig, kind) + } + ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), + ty::FnPtr(sig) => { + let expected_sig = ExpectedSig { cause_span: None, sig }; + (Some(expected_sig), Some(ty::ClosureKind::Fn)) + } + _ => (None, None), + } + } + + fn deduce_expectations_from_obligations( + &self, + expected_vid: ty::TyVid, + ) -> (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. + self.deduce_sig_from_projection( + 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)))); + + (expected_sig, expected_kind) + } + + /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce + /// everything we need to know about a closure or generator. + /// + /// The `cause_span` should be the span that caused us to + /// have this expected signature, or `None` if we can't readily + /// know that. + #[instrument(level = "debug", skip(self, cause_span), ret)] + fn deduce_sig_from_projection( + &self, + cause_span: Option<Span>, + projection: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<ExpectedSig<'tcx>> { + let tcx = self.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 gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); + let is_gen = gen_trait == trait_def_id; + if !is_fn && !is_gen { + debug!("not fn or generator"); + return None; + } + + if is_gen { + // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` + // associated item and not yield. + let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; + if return_assoc_item != projection.projection_def_id() { + debug!("not return assoc item of generator"); + return None; + } + } + + let input_tys = if is_fn { + let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1); + let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); + debug!(?arg_param_ty); + + match arg_param_ty.kind() { + &ty::Tuple(tys) => tys, + _ => return None, + } + } else { + // Generators with a `()` resume type may be defined with 0 or 1 explicit arguments, + // else they must have exactly 1 argument. For now though, just give up in this case. + return None; + }; + + // Since this is a return parameter type it is safe to unwrap. + let ret_param_ty = projection.skip_binder().term.ty().unwrap(); + let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); + debug!(?ret_param_ty); + + let sig = projection.rebind(self.tcx.mk_fn_sig( + input_tys.iter(), + ret_param_ty, + false, + hir::Unsafety::Normal, + Abi::Rust, + )); + + Some(ExpectedSig { cause_span, sig }) + } + + fn sig_of_closure( + &self, + hir_id: hir::HirId, + expr_def_id: DefId, + 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) + } else { + self.sig_of_closure_no_expectation(hir_id, 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")] + fn sig_of_closure_no_expectation( + &self, + hir_id: hir::HirId, + expr_def_id: DefId, + decl: &hir::FnDecl<'_>, + body: &hir::Body<'_>, + ) -> ClosureSignatures<'tcx> { + let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); + + self.closure_sigs(expr_def_id, body, bound_sig) + } + + /// Invoked to compute the signature of a closure expression. This + /// combines any user-provided type annotations (e.g., `|x: u32| + /// -> u32 { .. }`) with the expected signature. + /// + /// The approach is as follows: + /// + /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. + /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. + /// - If we have no expectation `E`, then the signature of the closure is `S`. + /// - Otherwise, the signature of the closure is E. Moreover: + /// - Skolemize the late-bound regions in `E`, yielding `E'`. + /// - Instantiate all the late-bound regions bound in the closure within `S` + /// with fresh (existential) variables, yielding `S'` + /// - Require that `E' = S'` + /// - We could use some kind of subtyping relationship here, + /// I imagine, but equality is easier and works fine for + /// our purposes. + /// + /// The key intuition here is that the user's types must be valid + /// from "the inside" of the closure, but the expectation + /// ultimately drives the overall signature. + /// + /// # Examples + /// + /// ```ignore (illustrative) + /// fn with_closure<F>(_: F) + /// where F: Fn(&u32) -> &u32 { .. } + /// + /// with_closure(|x: &u32| { ... }) + /// ``` + /// + /// Here: + /// - E would be `fn(&u32) -> &u32`. + /// - S would be `fn(&u32) -> + /// - E' is `&'!0 u32 -> &'!0 u32` + /// - S' is `&'?0 u32 -> ?T` + /// + /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. + /// + /// # Arguments + /// + /// - `expr_def_id`: the `DefId` 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")] + fn sig_of_closure_with_expectation( + &self, + hir_id: hir::HirId, + expr_def_id: DefId, + decl: &hir::FnDecl<'_>, + body: &hir::Body<'_>, + expected_sig: ExpectedSig<'tcx>, + ) -> ClosureSignatures<'tcx> { + // Watch out for some surprises and just ignore the + // 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); + } 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, + decl, + body, + expected_sig, + ); + } + + // Create a `PolyFnSig`. Note the oddity that late bound + // regions appearing free in `expected_sig` are now bound up + // in this binder we are creating. + assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); + let bound_sig = expected_sig.sig.map_bound(|sig| { + self.tcx.mk_fn_sig( + sig.inputs().iter().cloned(), + sig.output(), + sig.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ) + }); + + // `deduce_expectations_from_expected_type` introduces + // late-bound lifetimes defined elsewhere, which we now + // anonymize away, so as not to confuse the user. + let bound_sig = self.tcx.anonymize_late_bound_regions(bound_sig); + + let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig); + + // Up till this point, we have ignored the annotations that the user + // gave. This function will check that they unify successfully. + // 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, + ) { + Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), + Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), + } + } + + fn sig_of_closure_with_mismatched_number_of_arguments( + &self, + expr_def_id: DefId, + 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 expected_args: Vec<_> = expected_sig + .sig + .skip_binder() + .inputs() + .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 expected_span = + expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); + self.report_arg_count_mismatch( + expected_span, + closure_span, + expected_args, + found_args, + true, + ) + .emit(); + + let error_sig = self.error_sig_of_closure(decl); + + self.closure_sigs(expr_def_id, body, error_sig) + } + + /// 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))] + fn merge_supplied_sig_with_expectation( + &self, + hir_id: hir::HirId, + expr_def_id: DefId, + decl: &hir::FnDecl<'_>, + body: &hir::Body<'_>, + mut expected_sigs: ClosureSignatures<'tcx>, + ) -> InferResult<'tcx, ClosureSignatures<'tcx>> { + // Get the signature S that the user gave. + // + // (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); + + debug!(?supplied_sig); + + // FIXME(#45727): As discussed in [this comment][c1], naively + // forcing equality here actually results in suboptimal error + // messages in some cases. For now, if there would have been + // an obvious error, we fallback to declaring the type of the + // closure to be the one the user gave, which allows other + // error message code to trigger. + // + // However, I think [there is potential to do even better + // here][c2], since in *this* code we have the precise span of + // the type parameter in question in hand when we report the + // error. + // + // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 + // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 + self.commit_if_ok(|_| { + let mut all_obligations = vec![]; + let inputs: Vec<_> = iter::zip( + decl.inputs, + supplied_sig.inputs().skip_binder(), // binder moved to (*) below + ) + .map(|(hir_ty, &supplied_ty)| { + // Instantiate (this part of..) S to S', i.e., with fresh variables. + self.replace_bound_vars_with_fresh_vars( + hir_ty.span, + LateBoundRegionConversionTime::FnCall, + // (*) binder moved to here + supplied_sig.inputs().rebind(supplied_ty), + ) + }) + .collect(); + + // The liberated version of this signature should be a subtype + // of the liberated form of the expectation. + for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( + iter::zip(decl.inputs, &inputs), + expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. + ) { + // Check that E' = S'. + let cause = self.misc(hir_ty.span); + let InferOk { value: (), obligations } = + self.at(&cause, self.param_env).eq(*expected_ty, supplied_ty)?; + all_obligations.extend(obligations); + } + + let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( + decl.output.span(), + LateBoundRegionConversionTime::FnCall, + supplied_sig.output(), + ); + let cause = &self.misc(decl.output.span()); + let InferOk { value: (), obligations } = self + .at(cause, self.param_env) + .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; + all_obligations.extend(obligations); + + let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty)); + + expected_sigs.liberated_sig = self.tcx.mk_fn_sig( + inputs, + supplied_output_ty, + expected_sigs.liberated_sig.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ); + + Ok(InferOk { value: expected_sigs, obligations: all_obligations }) + }) + } + + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. + /// + /// Also, record this closure signature for later. + #[instrument(skip(self, decl, body), level = "debug", ret)] + fn supplied_sig_of_closure( + &self, + hir_id: hir::HirId, + expr_def_id: DefId, + decl: &hir::FnDecl<'_>, + body: &hir::Body<'_>, + ) -> ty::PolyFnSig<'tcx> { + let astconv: &dyn AstConv<'_> = self; + + trace!("decl = {:#?}", decl); + debug!(?body.generator_kind); + + let bound_vars = self.tcx.late_bound_vars(hir_id); + + // First, convert the types that the user supplied (if any). + let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); + let supplied_return = match decl.output { + hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(&output), + hir::FnRetTy::DefaultReturn(_) => match body.generator_kind { + // In the case of the async block that we create for a function body, + // we expect the return type of the block to match that of the enclosing + // function. + Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => { + debug!("closure is async fn body"); + self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id) + .unwrap_or_else(|| { + // AFAIK, deducing the future output + // always succeeds *except* in error cases + // like #65159. I'd like to return Error + // here, but I can't because I can't + // easily (and locally) prove that we + // *have* reported an + // error. --nikomatsakis + astconv.ty_infer(None, decl.output.span()) + }) + } + + _ => astconv.ty_infer(None, decl.output.span()), + }, + }; + + let result = ty::Binder::bind_with_vars( + self.tcx.mk_fn_sig( + supplied_arguments, + supplied_return, + decl.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ), + bound_vars, + ); + // 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 c_result = self.inh.infcx.canonicalize_response(result); + self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); + + result + } + + /// Invoked when we are translating the generator that results + /// from desugaring an `async fn`. Returns the "sugared" return + /// type of the `async fn` -- that is, the return type that the + /// user specified. The "desugared" return type is an `impl + /// Future<Output = T>`, so we do this by searching through the + /// obligations to extract the `T`. + #[instrument(skip(self), level = "debug", ret)] + fn deduce_future_output_from_obligations( + &self, + expr_def_id: DefId, + body_id: hir::HirId, + ) -> Option<Ty<'tcx>> { + let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { + span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn") + }); + + let ret_ty = ret_coercion.borrow().expected_ty(); + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); + + let get_future_output = |predicate: ty::Predicate<'tcx>, span| { + // Search for a pending obligation like + // + // `<R as Future>::Output = T` + // + // 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() { + self.deduce_future_output_from_projection( + span, + bound_predicate.rebind(proj_predicate), + ) + } else { + None + } + }; + + let output_ty = match *ret_ty.kind() { + ty::Infer(ty::TyVar(ret_vid)) => { + self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { + get_future_output(obligation.predicate, obligation.cause.span) + })? + } + ty::Opaque(def_id, substs) => self + .tcx + .bound_explicit_item_bounds(def_id) + .subst_iter_copied(self.tcx, substs) + .find_map(|(p, s)| get_future_output(p, s))?, + ty::Error(_) => return None, + ty::Projection(proj) + if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder => + { + self.tcx + .bound_explicit_item_bounds(proj.item_def_id) + .subst_iter_copied(self.tcx, proj.substs) + .find_map(|(p, s)| get_future_output(p, s))? + } + _ => span_bug!( + self.tcx.def_span(expr_def_id), + "async fn generator return type not an inference variable: {ret_ty}" + ), + }; + + // async fn that have opaque types in their return type need to redo the conversion to inference variables + // as they fetch the still opaque version from the signature. + let InferOk { value: output_ty, obligations } = self + .replace_opaque_types_with_inference_vars( + output_ty, + body_id, + self.tcx.def_span(expr_def_id), + self.param_env, + ); + self.register_predicates(obligations); + + Some(output_ty) + } + + /// Given a projection like + /// + /// `<X as Future>::Output = T` + /// + /// where `X` is some type that has no late-bound regions, returns + /// `Some(T)`. If the projection is for some other trait, returns + /// `None`. + fn deduce_future_output_from_projection( + &self, + cause_span: Span, + predicate: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<Ty<'tcx>> { + debug!("deduce_future_output_from_projection(predicate={:?})", predicate); + + // We do not expect any bound regions in our predicate, so + // skip past the bound vars. + let Some(predicate) = predicate.no_bound_vars() else { + debug!("deduce_future_output_from_projection: has late-bound regions"); + return None; + }; + + // Check that this is a projection from the `Future` trait. + let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx); + let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); + if trait_def_id != future_trait { + debug!("deduce_future_output_from_projection: not a future"); + return None; + } + + // The `Future` trait has only one associated item, `Output`, + // so check that this is what we see. + let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; + if output_assoc_item != predicate.projection_ty.item_def_id { + span_bug!( + cause_span, + "projecting associated item `{:?}` from future, which is not Output `{:?}`", + predicate.projection_ty.item_def_id, + output_assoc_item, + ); + } + + // Extract the type from the projection. Note that there can + // be no bound variables in this type because the "self type" + // does not have any regions in it. + let output_ty = self.resolve_vars_if_possible(predicate.term); + debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); + // This is a projection on a Fn trait so will always be a type. + Some(output_ty.ty().unwrap()) + } + + /// Converts the types that the user supplied, in case that doing + /// so should yield an error, but returns back a signature where + /// all parameters are of type `TyErr`. + fn error_sig_of_closure(&self, decl: &hir::FnDecl<'_>) -> ty::PolyFnSig<'tcx> { + let astconv: &dyn AstConv<'_> = self; + + let supplied_arguments = decl.inputs.iter().map(|a| { + // Convert the types that the user supplied (if any), but ignore them. + astconv.ast_ty_to_ty(a); + self.tcx.ty_error() + }); + + if let hir::FnRetTy::Return(ref output) = decl.output { + astconv.ast_ty_to_ty(&output); + } + + let result = ty::Binder::dummy(self.tcx.mk_fn_sig( + supplied_arguments, + self.tcx.ty_error(), + decl.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + )); + + debug!("supplied_sig_of_closure: result={:?}", result); + + result + } + + fn closure_sigs( + &self, + expr_def_id: DefId, + 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, + ); + ClosureSignatures { bound_sig, liberated_sig } + } +} diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 2ed5f569b..86597a703 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -35,13 +35,15 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use crate::astconv::AstConv; -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, Visitor}; +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}; @@ -59,7 +61,7 @@ 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::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>; +struct CollectRetsVisitor<'tcx> { + ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Ret(_) = expr.kind { + self.ret_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -201,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); } + ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => { + return self.coerce_dyn_star(a, b, predicates, region); + } _ => {} } @@ -687,7 +705,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Object safety violations or miscellaneous. Err(err) => { - self.report_selection_error(obligation.clone(), &obligation, &err, false); + self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + false, + ); // 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. @@ -725,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(coercion) } + fn coerce_dyn_star( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + b_region: ty::Region<'tcx>, + ) -> CoerceResult<'tcx> { + if !self.tcx.features().dyn_star { + return Err(TypeError::Mismatch); + } + + if let ty::Dynamic(a_data, _, _) = a.kind() + && let ty::Dynamic(b_data, _, _) = b.kind() + { + 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(); + } + } + + // Check the obligations of the cast -- for example, when casting + // `usize` to `dyn* Clone + 'static`: + let obligations = predicates + .iter() + .map(|predicate| { + // For each existential predicate (e.g., `?Self: Clone`) substitute + // the type of the expression (e.g., `usize` in our example above) + // 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) + }) + // 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), + ))), + )]) + .collect(); + + Ok(InferOk { + value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), + obligations, + }) + } + fn coerce_from_safe_fn<F, G>( &self, a: Ty<'tcx>, @@ -1464,23 +1544,29 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } Err(coercion_error) => { + // 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(); 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" // since it is implied by the syntax. // (Note: not all force-units work this way.)" - (expression_ty, self.final_ty.unwrap_or(self.expected_ty)) + (expression_ty, self.merged_ty()) } else { // Otherwise, the "expected" type for error // reporting is the current unification type, // which is basically the LUB of the expressions // we've seen so far (combined with the expected // type) - (self.final_ty.unwrap_or(self.expected_ty), expression_ty) + (self.merged_ty(), expression_ty) }; + let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); let mut err; let mut unsized_return = false; + let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; match *cause.code() { ObligationCauseCode::ReturnNoExpression => { err = struct_span_err!( @@ -1506,6 +1592,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_unsized(fcx, blk_id); } + if let Some(expression) = expression + && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { + intravisit::walk_block(& mut visitor, loop_blk); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1524,7 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } _ => { - err = fcx.report_mismatched_types( + err = fcx.err_ctxt().report_mismatched_types( cause, expected, found, @@ -1551,12 +1641,47 @@ 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); self.final_ty = Some(fcx.tcx.ty_error()); } } } + fn note_unreachable_loop_return( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, + ) { + let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;}; + let mut span: MultiSpan = vec![loop_span].into(); + span.push_span_label(loop_span, "this might have zero elements to iterate on"); + const MAXITER: usize = 3; + let iter = ret_exprs.iter().take(MAXITER); + for ret_expr in iter { + span.push_span_label( + ret_expr.span, + "if the loop doesn't execute, this value would never get returned", + ); + } + err.span_note( + span, + "the function expects a value to always be returned, but loops might run zero times", + ); + if MAXITER < ret_exprs.len() { + err.note(&format!( + "if the loop doesn't execute, {} other values would never get returned", + ret_exprs.len() - MAXITER + )); + } + err.help( + "return a value for the case when the loop has zero elements to iterate on, or \ + consider changing the return type to account for that possibility", + ); + } fn report_return_mismatched_types<'a>( &self, @@ -1569,7 +1694,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expression: Option<&'tcx hir::Expr<'tcx>>, blk_id: Option<hir::HirId>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); + let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); let mut pointing_at_return_type = false; let mut fn_output = None; @@ -1623,7 +1748,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, can_suggest, - fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)), + fcx.tcx.hir().get_parent_item(id).into(), ); } if !pointing_at_return_type { @@ -1632,7 +1757,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); + let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id); if let (Some(expr), Some(_), Some((fn_decl, _, _))) = (expression, blk_id, fcx.get_node_fn_decl(parent_item)) @@ -1644,13 +1769,34 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, id, - fcx.tcx.hir().local_def_id_to_hir_id(parent_id), + parent_id.into(), + ); + } + + let ret_coercion_span = fcx.ret_coercion_span.get(); + + if let Some(sp) = ret_coercion_span + // If the closure has an explicit return type annotation, or if + // the closure's return type has been inferred from outside + // requirements (such as an Fn* trait bound), then a type error + // 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 + { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + expected + ), ); } - if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { + if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } + err } diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 4de48dc5b..16febfc46 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,21 +1,20 @@ -use crate::check::FnCtxt; -use rustc_infer::infer::InferOk; -use rustc_middle::middle::stability::EvalResult; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::ObligationCause; - +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::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; +use rustc_infer::infer::InferOk; use rustc_middle::lint::in_external_macro; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -32,20 +31,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option<TypeError<'tcx>>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); - self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty); - if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { - return; - } - self.suggest_no_capture_closure(err, expected, expr_ty); - self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); - self.suggest_missing_parentheses(err, expr); - self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); + + // Use `||` to give these suggestions a precedence + let _ = self.suggest_missing_parentheses(err, expr) + || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) + || self.suggest_compatible_variants(err, expr, expected, expr_ty) + || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty) + || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_no_capture_closure(err, expected, expr_ty) + || 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.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); - self.report_closure_inferred_return_type(err, expected); } // Requires that the two types unify, and prints an error message if @@ -77,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, expected, actual, e)), } } @@ -107,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)), } } @@ -131,7 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". - #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] + #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, expr: &hir::Expr<'tcx>, @@ -151,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); + let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone()); let is_insufficiently_polymorphic = matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..)); @@ -286,7 +287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { if let ty::Adt(expected_adt, substs) = expected.kind() { if let hir::ExprKind::Field(base, ident) = expr.kind { let base_ty = self.typeck_results.borrow().expr_ty(base); @@ -299,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "", Applicability::MaybeIncorrect, ); - return + return true; } } @@ -338,7 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) { vec!["None", "Some(())"] } else { - return; + return false; }; if let Some(indent) = self.tcx.sess.source_map().indentation_before(span.shrink_to_lo()) @@ -358,7 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - return; + return true; } } } @@ -375,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_is_local = sole_field.did.is_local(); let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx) + sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) // Skip suggestions for unstable public fields (for example `Pin::pointer`) && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); @@ -417,6 +418,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::def::CtorKind::Const => unreachable!(), }; + // Suggest constructor as deep into the block tree as possible. + // This fixes https://github.com/rust-lang/rust/issues/101065, + // and also just helps make the most minimal suggestions. + let mut expr = expr; + while let hir::ExprKind::Block(block, _) = &expr.kind + && let Some(expr_) = &block.expr + { + expr = expr_ + } + vec![ (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), (expr.span.shrink_to_hi(), close.to_owned()), @@ -435,6 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestions_for(&**variant, *ctor_kind, *field_name), Applicability::MaybeIncorrect, ); + return true; } _ => { // More than one matching variant. @@ -450,9 +462,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), Applicability::MaybeIncorrect, ); + return true; } } } + + false } fn suggest_non_zero_new_unwrap( @@ -461,19 +476,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { let tcx = self.tcx; let (adt, unwrap) = match expected.kind() { // In case Option<NonZero*> is wanted, but * is provided, suggest calling new ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => { // Unwrap option - let ty::Adt(adt, _) = substs.type_at(0).kind() else { return }; + let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; }; (adt, "") } // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types ty::Adt(adt, _) => (adt, ".unwrap()"), - _ => return, + _ => return false, }; let map = [ @@ -492,7 +507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some((s, _)) = map .iter() .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)) - else { return }; + else { return false; }; let path = self.tcx.def_path_str(adt.non_enum_variant().def_id); @@ -504,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Applicability::MaybeIncorrect, ); + + true } pub fn get_conversion_methods( @@ -513,24 +530,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, hir_id: hir::HirId, ) -> Vec<AssocItem> { - let mut methods = - self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); - methods.retain(|m| { - self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) - }); + let methods = self.probe_for_return_type( + span, + probe::Mode::MethodCall, + expected, + checked_ty, + hir_id, + |m| { + self.has_only_self_parameter(m) + && self + .tcx + // This special internal attribute is used to permit + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + .has_attr(m.def_id, sym::rustc_conversion_suggestion) + }, + ); methods } @@ -590,7 +612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_params_len = closure_fn_decl.inputs.len(); let ( Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(method_path, method_expr, _), + kind: hir::ExprKind::MethodCall(method_path, receiver, ..), .. })), 1, @@ -598,14 +620,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]); - let self_ty = format!("{:?}", self_ty); + let self_ty = self.typeck_results.borrow().expr_ty(receiver); let name = method_path.ident.name; - let is_as_ref_able = (self_ty.starts_with("&std::option::Option") - || self_ty.starts_with("&std::result::Result") - || self_ty.starts_with("std::option::Option") - || self_ty.starts_with("std::result::Result")) - && (name == sym::map || name == sym::and_then); + let is_as_ref_able = match self_ty.peel_refs().kind() { + ty::Adt(def, _) => { + (self.tcx.is_diagnostic_item(sym::Option, def.did()) + || self.tcx.is_diagnostic_item(sym::Result, def.did())) + && (name == sym::map || name == sym::and_then) + } + _ => false, + }; match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { (true, Ok(src)) => { let suggestion = format!("as_ref().{}", src); @@ -637,11 +661,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }?; match hir.find(hir.get_parent_node(expr.hir_id))? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { - for field in *fields { - if field.ident.name == local.name && field.is_shorthand { - return Some(local.name); - } + Node::ExprField(field) => { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } _ => {} @@ -697,7 +719,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { + ) -> Option<( + Span, + String, + String, + Applicability, + bool, /* verbose */ + bool, /* suggest `&` or `&mut` type annotation */ + )> { let sess = self.sess(); let sp = expr.span; @@ -729,6 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + false, )); } } @@ -743,6 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "b".to_string(), Applicability::MachineApplicable, true, + false, )); } } @@ -767,22 +798,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind { + if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind { let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span)); - if let ([arg], Some(true), sym::clone) = ( - &args[..], - self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( + if args.is_empty() + && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( |did| { let ai = self.tcx.associated_item(did); ai.trait_container(self.tcx) == Some(clone_trait) }, - ), - segment.ident.name, - ) { + ) == Some(true) + && segment.ident.name == sym::clone + { // If this expression had a clone call when suggesting borrowing // we want to suggest removing it because it'd now be unnecessary. - sugg_sp = arg.span; + sugg_sp = receiver.span; } } if let Ok(src) = sm.span_to_snippet(sugg_sp) { @@ -793,7 +823,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ if is_range_literal(expr) => true, _ => false, }; - let sugg_expr = if needs_parens { format!("({src})") } else { src }; if let Some(sugg) = self.can_use_as_ref(expr) { return Some(( @@ -802,6 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg.2, Applicability::MachineApplicable, false, + false, )); } @@ -821,6 +851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let sugg_expr = if needs_parens { format!("({src})") } else { src }; return Some(match mutability { hir::Mutability::Mut => ( sp, @@ -828,6 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&mut {sugg_expr}"), Applicability::MachineApplicable, false, + false, ), hir::Mutability::Not => ( sp, @@ -835,6 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&{sugg_expr}"), Applicability::MachineApplicable, false, + false, ), }); } @@ -864,6 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true )); } return None; @@ -877,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true, )); } } @@ -943,6 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { src, applicability, true, + false, )); } } @@ -983,6 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable }, true, + false, )); } @@ -1034,6 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestion, Applicability::MachineApplicable, true, + false, )); } } @@ -1072,21 +1110,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + if let Some(hir::Node::ExprField(field)) = + self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate - match (*fields) - .iter() - .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) - { + if field.is_shorthand { // This is a field literal - Some(field) => { - sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); - } + sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); + } else { // Likely a field was meant, but this field wasn't found. Do not suggest anything. - None => return false, + return false; } }; @@ -1418,25 +1451,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, } } - - // Report the type inferred by the return statement. - fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) { - if let Some(sp) = self.ret_coercion_span.get() - // If the closure has an explicit return type annotation, or if - // the closure's return type has been inferred from outside - // requirements (such as an Fn* trait bound), then a type error - // 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. - && !self.return_type_pre_known - { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - self.resolve_vars_if_possible(expected) - ), - ); - } - } } diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs index 963a93a95..963a93a95 100644 --- a/compiler/rustc_typeck/src/check/diverges.rs +++ b/compiler/rustc_hir_typeck/src/diverges.rs diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs new file mode 100644 index 000000000..175037f9b --- /dev/null +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -0,0 +1,126 @@ +//! Errors emitted by `rustc_hir_analysis`. +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")] +pub struct FieldMultiplySpecifiedInInitializer { + #[primary_span] + #[label] + pub span: Span, + #[label(previous_use_label)] + pub prev_span: Span, + pub ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_return_stmt_outside_of_fn_body, code = "E0572")] +pub struct ReturnStmtOutsideOfFnBody { + #[primary_span] + pub span: Span, + #[label(encl_body_label)] + pub encl_body_span: Option<Span>, + #[label(encl_fn_label)] + pub encl_fn_span: Option<Span>, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_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")] +pub struct StructExprNonExhaustive { + #[primary_span] + pub span: Span, + pub what: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_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")] +pub struct FunctionalRecordUpdateOnNonStruct { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_address_of_temporary_taken, code = "E0745")] +pub struct AddressOfTemporaryTaken { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Subdiagnostic)] +pub enum AddReturnTypeSuggestion { + #[suggestion( + hir_analysis_add_return_type_add, + code = "-> {found} ", + applicability = "machine-applicable" + )] + Add { + #[primary_span] + span: Span, + found: String, + }, + #[suggestion( + hir_analysis_add_return_type_missing_here, + code = "-> _ ", + applicability = "has-placeholders" + )] + MissingHere { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +pub enum ExpectedReturnTypeLabel<'tcx> { + #[label(hir_analysis_expected_default_return_type)] + Unit { + #[primary_span] + span: Span, + }, + #[label(hir_analysis_expected_return_type)] + Other { + #[primary_span] + span: Span, + expected: Ty<'tcx>, + }, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_missing_parentheses_in_range, code = "E0689")] +pub struct MissingParentheseInRange { + #[primary_span] + #[label(hir_analysis_missing_parentheses_in_range)] + pub span: Span, + pub ty_str: String, + pub method_name: String, + #[subdiagnostic] + pub add_missing_parentheses: Option<AddMissingParenthesesInRange>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion_verbose( + hir_analysis_add_missing_parentheses_in_range, + applicability = "maybe-incorrect" +)] +pub struct AddMissingParenthesesInRange { + pub func_name: String, + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index e9e810344..e9e810344 100644 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs new file mode 100644 index 000000000..9fde62a81 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -0,0 +1,2896 @@ +//! Type checking expressions. +//! +//! See `mod.rs` for more context on type checking in general. + +use crate::cast; +use crate::coercion::CoerceMany; +use crate::coercion::DynamicCoerceMany; +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; +use crate::errors::{ + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + YieldExprOutsideOfGenerator, +}; +use crate::fatally_break_rust; +use crate::method::SelfSource; +use crate::type_error_struct; +use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; +use crate::{ + report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs, + TupleArgumentsFlag::DontTupleArguments, +}; +use rustc_ast as ast; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, + ErrorGuaranteed, StashKey, +}; +use rustc_hir as hir; +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_analysis::astconv::AstConv as _; +use rustc_hir_analysis::check::ty_kind_suggestion; +use rustc_infer::infer; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferOk; +use rustc_infer::traits::ObligationCause; +use rustc_middle::middle::stability; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::error::TypeError::FieldMisMatch; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; +use rustc_session::errors::ExprParenthesesNeeded; +use rustc_session::parse::feature_err; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::lev_distance::find_best_match_for_name; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_target::spec::abi::Abi::RustIntrinsic; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::{self, ObligationCauseCode}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { + let ty = self.check_expr_with_hint(expr, expected); + self.demand_eqtype(expr.span, expected, ty); + } + + pub fn check_expr_has_type_or_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + extend_err: impl FnMut(&mut Diagnostic), + ) -> Ty<'tcx> { + self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) + } + + fn check_expr_meets_expectation_or_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + mut extend_err: impl FnMut(&mut Diagnostic), + ) -> Ty<'tcx> { + let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); + let mut ty = self.check_expr_with_expectation(expr, expected); + + // While we don't allow *arbitrary* coercions here, we *do* allow + // 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( + 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() + }; + } + + let adj_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::AdjustmentType, + span: expr.span, + }); + self.apply_adjustments( + expr, + vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], + ); + ty = adj_ty; + } + + 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); + extend_err(&mut err); + err.emit(); + } + ty + } + + pub(super) fn check_expr_coercable_to_type( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> Ty<'tcx> { + let ty = self.check_expr_with_hint(expr, expected); + // checks don't need two phase + self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) + } + + pub(super) fn check_expr_with_hint( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + ) -> Ty<'tcx> { + self.check_expr_with_expectation(expr, ExpectHasType(expected)) + } + + fn check_expr_with_expectation_and_needs( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + needs: Needs, + ) -> Ty<'tcx> { + let ty = self.check_expr_with_expectation(expr, expected); + + // If the expression is used in a place whether mutable place is required + // e.g. LHS of assignment, perform the conversion. + if let Needs::MutPlace = needs { + self.convert_place_derefs_to_mutable(expr); + } + + ty + } + + pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { + self.check_expr_with_expectation(expr, NoExpectation) + } + + pub(super) fn check_expr_with_needs( + &self, + expr: &'tcx hir::Expr<'tcx>, + needs: Needs, + ) -> Ty<'tcx> { + self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) + } + + /// Invariant: + /// If an expression has any sub-expressions that result in a type error, + /// inspecting that expression's type with `ty.references_error()` will return + /// true. Likewise, if an expression is known to diverge, inspecting its + /// type with `ty::type_is_bot` will return true (n.b.: since Rust is + /// strict, _|_ can appear in the type of an expression that does not, + /// itself, diverge: for example, fn() -> _|_.) + /// Note that inspecting a type's structure *directly* may expose the fact + /// that there are actually multiple representations for `Error`, so avoid + /// that when err needs to be handled differently. + #[instrument(skip(self, expr), level = "debug")] + pub(super) fn check_expr_with_expectation( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + self.check_expr_with_expectation_and_args(expr, expected, &[]) + } + + /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a + /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`. + pub(super) fn check_expr_with_expectation_and_args( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + ) -> Ty<'tcx> { + if self.tcx().sess.verbose() { + // make this code only run with -Zverbose because it is probably slow + if let Ok(lint_str) = self.tcx.sess.source_map().span_to_snippet(expr.span) { + if !lint_str.contains('\n') { + debug!("expr text: {lint_str}"); + } else { + let mut lines = lint_str.lines(); + if let Some(line0) = lines.next() { + let remaining_lines = lines.count(); + debug!("expr text: {line0}"); + debug!("expr text: ...(and {remaining_lines} more lines)"); + } + } + } + } + + // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block + // without the final expr (e.g. `try { return; }`). We don't want to generate an + // unreachable_code lint for it since warnings for autogenerated code are confusing. + let is_try_block_generated_unit_expr = match expr.kind { + ExprKind::Call(_, args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => { + args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock) + } + + _ => false, + }; + + // Warn for expressions after diverging siblings. + if !is_try_block_generated_unit_expr { + self.warn_if_unreachable(expr.hir_id, expr.span, "expression"); + } + + // 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( + qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..), + ) => self.check_expr_path(qpath, expr, args), + _ => self.check_expr_kind(expr, expected), + }); + + // Warn for non-block expressions with diverging children. + match expr.kind { + ExprKind::Block(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) => {} + // If `expr` is a result of desugaring the try block and is an ok-wrapped + // diverging expression (e.g. it arose from desugaring of `try { return }`), + // we skip issuing a warning because it is autogenerated code. + ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} + ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), + ExprKind::MethodCall(segment, ..) => { + self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") + } + _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), + } + + // Any expression that produces a value of type `!` must have diverged + if ty.is_never() { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); + } + + // Record the type, which applies it effects. + // We need to do this after the warning above, so that + // we don't warn for the diverging expression itself. + self.write_ty(expr.hir_id, ty); + + // 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); + + ty + } + + #[instrument(skip(self, expr), level = "debug")] + fn check_expr_kind( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + trace!("expr={:#?}", expr); + + let tcx = self.tcx; + match expr.kind { + ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected), + ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), + ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), + ExprKind::Assign(lhs, rhs, span) => { + self.check_expr_assign(expr, expected, lhs, rhs, span) + } + ExprKind::AssignOp(op, lhs, rhs) => { + self.check_binop_assign(expr, op, lhs, rhs, expected) + } + ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), + ExprKind::AddrOf(kind, mutbl, oprnd) => { + self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) + } + ExprKind::Path(QPath::LangItem(lang_item, _, hir_id)) => { + self.check_lang_item_path(lang_item, expr, hir_id) + } + ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]), + ExprKind::InlineAsm(asm) => { + // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). + self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); + self.check_expr_asm(asm) + } + ExprKind::Break(destination, ref expr_opt) => { + self.check_expr_break(destination, expr_opt.as_deref(), expr) + } + ExprKind::Continue(destination) => { + if destination.target_id.is_ok() { + tcx.types.never + } else { + // There was an error; make type-check fail. + tcx.ty_error() + } + } + ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), + ExprKind::Let(let_expr) => self.check_expr_let(let_expr), + ExprKind::Loop(body, _, source, _) => { + self.check_expr_loop(body, source, expected, expr) + } + 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::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, _) => { + self.check_method_call(expr, segment, receiver, args, expected) + } + ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), + ExprKind::Type(e, t) => { + let ty = self.to_ty_saving_user_provided_ty(&t); + self.check_expr_eq_type(&e, ty); + ty + } + ExprKind::If(cond, then_expr, opt_else_expr) => { + self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) + } + ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), + ExprKind::Array(args) => self.check_expr_array(args, expected, expr), + ExprKind::ConstBlock(ref anon_const) => { + self.check_expr_const_block(anon_const, expected, expr) + } + ExprKind::Repeat(element, ref count) => { + self.check_expr_repeat(element, count, expected, expr) + } + ExprKind::Tup(elts) => self.check_expr_tuple(elts, expected, expr), + ExprKind::Struct(qpath, fields, ref base_expr) => { + self.check_expr_struct(expr, expected, qpath, fields, base_expr) + } + ExprKind::Field(base, field) => self.check_field(expr, &base, field), + ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), + ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), + hir::ExprKind::Err => tcx.ty_error(), + } + } + + fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { + let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { + ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), + _ => NoExpectation, + }); + let referent_ty = self.check_expr_with_expectation(expr, expected_inner); + self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType); + self.tcx.mk_box(referent_ty) + } + + fn check_expr_unary( + &self, + unop: hir::UnOp, + oprnd: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let expected_inner = match unop { + hir::UnOp::Not | hir::UnOp::Neg => expected, + hir::UnOp::Deref => NoExpectation, + }; + let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); + + if !oprnd_t.references_error() { + oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); + match unop { + hir::UnOp::Deref => { + if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { + oprnd_t = ty; + } else { + let mut err = type_error_struct!( + tcx.sess, + expr.span, + oprnd_t, + E0614, + "type `{oprnd_t}` cannot be dereferenced", + ); + let sp = tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = + tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) + { + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + } + err.emit(); + oprnd_t = tcx.ty_error(); + } + } + hir::UnOp::Not => { + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); + // If it's builtin, we can reuse the type, this helps inference. + if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { + oprnd_t = result; + } + } + hir::UnOp::Neg => { + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); + // If it's builtin, we can reuse the type, this helps inference. + if !oprnd_t.is_numeric() { + oprnd_t = result; + } + } + } + } + oprnd_t + } + + fn check_expr_addr_of( + &self, + kind: hir::BorrowKind, + mutbl: hir::Mutability, + oprnd: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { + match ty.kind() { + ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + if oprnd.is_syntactic_place_expr() { + // Places may legitimately have unsized types. + // For example, dereferences of a fat pointer and + // the last field of a struct can be unsized. + ExpectHasType(*ty) + } else { + Expectation::rvalue_hint(self, *ty) + } + } + _ => NoExpectation, + } + }); + let ty = + self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); + + let tm = ty::TypeAndMut { ty, mutbl }; + match kind { + _ if tm.ty.references_error() => self.tcx.ty_error(), + hir::BorrowKind::Raw => { + self.check_named_place_expr(oprnd); + self.tcx.mk_ptr(tm) + } + hir::BorrowKind::Ref => { + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until + // type inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select a suitable value. + // Finally, borrowck will infer the value of the region again, + // this time with enough precision to check that the value + // whose address was taken can actually be made to live as long + // as it needs to live. + let region = self.next_region_var(infer::AddrOfRegion(expr.span)); + self.tcx.mk_ref(region, tm) + } + } + } + + /// Does this expression refer to a place that either: + /// * Is based on a local or static. + /// * Contains a dereference + /// Note that the adjustments for the children of `expr` should already + /// have been resolved. + fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) { + let is_named = oprnd.is_place_expr(|base| { + // Allow raw borrows if there are any deref adjustments. + // + // const VAL: (i32,) = (0,); + // const REF: &(i32,) = &(0,); + // + // &raw const VAL.0; // ERROR + // &raw const REF.0; // OK, same as &raw const (*REF).0; + // + // This is maybe too permissive, since it allows + // `let u = &raw const Box::new((1,)).0`, which creates an + // immediately dangling raw pointer. + self.typeck_results + .borrow() + .adjustments() + .get(base.hir_id) + .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + }); + if !is_named { + self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }); + } + } + + fn check_lang_item_path( + &self, + lang_item: hir::LangItem, + expr: &'tcx hir::Expr<'tcx>, + hir_id: Option<hir::HirId>, + ) -> Ty<'tcx> { + self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1 + } + + pub(crate) fn check_expr_path( + &self, + qpath: &'tcx hir::QPath<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + ) -> Ty<'tcx> { + let tcx = self.tcx; + let (res, opt_ty, segs) = + 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() + } + Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { + report_unexpected_variant_res(tcx, res, qpath, expr.span); + tcx.ty_error() + } + _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, + }; + + if let ty::FnDef(did, ..) = *ty.kind() { + let fn_sig = ty.fn_sig(tcx); + if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute { + let from = fn_sig.inputs().skip_binder()[0]; + let to = fn_sig.output().skip_binder(); + // We defer the transmute to the end of typeck, once all inference vars have + // been resolved or we errored. This is important as we can only check transmute + // on concrete types, but the output type may not be known yet (it would only + // be known if explicitly specified via turbofish). + self.deferred_transmute_checks.borrow_mut().push((from, to, expr.hir_id)); + } + if !tcx.features().unsized_fn_params { + // We want to remove some Sized bounds from std functions, + // but don't want to expose the removal to stable Rust. + // i.e., we don't want to allow + // + // ```rust + // drop as fn(str); + // ``` + // + // to work in stable even if the Sized bound on `drop` is relaxed. + for i in 0..fn_sig.inputs().skip_binder().len() { + // We just want to check sizedness, so instead of introducing + // placeholder lifetimes with probing, we just replace higher lifetimes + // with fresh vars. + let span = args.get(i).map(|a| a.span).unwrap_or(expr.span); + let input = self.replace_bound_vars_with_fresh_vars( + span, + infer::LateBoundRegionConversionTime::FnCall, + fn_sig.input(i), + ); + self.require_type_is_sized_deferred( + input, + span, + traits::SizedArgumentType(None), + ); + } + } + // Here we want to prevent struct constructors from returning unsized types. + // There were two cases this happened: fn pointer coercion in stable + // and usual function call in presence of unsized_locals. + // Also, as we just want to check sizedness, instead of introducing + // placeholder lifetimes with probing, we just replace higher lifetimes + // with fresh vars. + let output = self.replace_bound_vars_with_fresh_vars( + expr.span, + infer::LateBoundRegionConversionTime::FnCall, + fn_sig.output(), + ); + self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); + } + + // We always require that the type provided as the value for + // a type parameter outlives the moment of instantiation. + let substs = self.typeck_results.borrow().node_substs(expr.hir_id); + self.add_wf_bounds(substs, expr); + + ty + } + + fn check_expr_break( + &self, + destination: hir::Destination, + expr_opt: Option<&'tcx hir::Expr<'tcx>>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + if let Ok(target_id) = destination.target_id { + let (e_ty, cause); + if let Some(e) = expr_opt { + // If this is a break with a value, we need to type-check + // the expression. Get an expected type from the loop context. + let opt_coerce_to = { + // We should release `enclosing_breakables` before the `check_expr_with_hint` + // below, so can't move this block of code to the enclosing scope and share + // `ctxt` with the second `enclosing_breakables` borrow below. + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + match enclosing_breakables.opt_find_breakable(target_id) { + Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), + None => { + // Avoid ICE when `break` is inside a closure (#65383). + return tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + } + } + }; + + // If the loop context is not a `loop { }`, then break with + // a value is illegal, and `opt_coerce_to` will be `None`. + // Just set expectation to error in that case. + let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); + + // Recurse without `enclosing_breakables` borrowed. + e_ty = self.check_expr_with_hint(e, coerce_to); + cause = self.misc(e.span); + } else { + // Otherwise, this is a break *without* a value. That's + // always legal, and is equivalent to `break ()`. + e_ty = tcx.mk_unit(); + cause = self.misc(expr.span); + } + + // Now that we have type-checked `expr_opt`, borrow + // the `enclosing_loops` field and let's coerce the + // type of `expr_opt` into what is expected. + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else { + // Avoid ICE when `break` is inside a closure (#65383). + return tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + }; + + if let Some(ref mut coerce) = ctxt.coerce { + if let Some(ref e) = expr_opt { + coerce.coerce(self, &cause, e, e_ty); + } else { + assert!(e_ty.is_unit()); + let ty = coerce.expected_ty(); + coerce.coerce_forced_unit( + self, + &cause, + &mut |mut err| { + self.suggest_mismatched_types_on_tail( + &mut err, expr, ty, e_ty, target_id, + ); + if let Some(val) = ty_kind_suggestion(ty) { + let label = destination + .label + .map(|l| format!(" {}", l.ident)) + .unwrap_or_else(String::new); + err.span_suggestion( + expr.span, + "give it a value of the expected type", + format!("break{label} {val}"), + Applicability::HasPlaceholders, + ); + } + }, + false, + ); + } + } else { + // If `ctxt.coerce` is `None`, we can just ignore + // the type of the expression. This is because + // either this was a break *without* a value, in + // which case it is always a legal type (`()`), or + // else an error would have been flagged by the + // `loops` pass for using break with an expression + // where you are not supposed to. + assert!(expr_opt.is_none() || self.tcx.sess.has_errors().is_some()); + } + + // If we encountered a `break`, then (no surprise) it may be possible to break from the + // loop... unless the value being returned from the loop diverges itself, e.g. + // `break return 5` or `break loop {}`. + ctxt.may_break |= !self.diverges.get().is_always(); + + // the type of a `break` is always `!`, since it diverges + tcx.types.never + } else { + // Otherwise, we failed to find the enclosing loop; + // this can only happen if the `break` was not + // inside a loop at all, which is caught by the + // loop-checking pass. + let err = self.tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); + + // We still need to assign a type to the inner expression to + // prevent the ICE in #43162. + if let Some(e) = expr_opt { + self.check_expr_with_hint(e, err); + + // ... except when we try to 'break rust;'. + // ICE this expression in particular (see #43162). + if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { + if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { + fatally_break_rust(self.tcx.sess); + } + } + } + + // There was an error; make type-check fail. + err + } + } + + fn check_expr_return( + &self, + expr_opt: Option<&'tcx hir::Expr<'tcx>>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + if self.ret_coercion.is_none() { + let mut err = ReturnStmtOutsideOfFnBody { + span: expr.span, + encl_body_span: None, + encl_fn_span: None, + }; + + let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id); + + if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(..), + span: encl_fn_span, + .. + })) + | Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), + span: encl_fn_span, + .. + })) + | Some(hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), + span: encl_fn_span, + .. + })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id) + { + // We are inside a function body, so reporting "return statement + // outside of function body" needs an explanation. + + let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id); + + // If this didn't hold, we would not have to report an error in + // the first place. + assert_ne!(encl_item_id.def_id, encl_body_owner_id); + + let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); + let encl_body = self.tcx.hir().body(encl_body_id); + + err.encl_body_span = Some(encl_body.value.span); + err.encl_fn_span = Some(*encl_fn_span); + } + + self.tcx.sess.emit_err(err); + + if let Some(e) = expr_opt { + // We still have to type-check `e` (issue #86188), but calling + // `check_return_expr` only works inside fn bodies. + self.check_expr(e); + } + } else if let Some(e) = expr_opt { + if self.ret_coercion_span.get().is_none() { + self.ret_coercion_span.set(Some(e.span)); + } + self.check_return_expr(e, true); + } else { + let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); + if self.ret_coercion_span.get().is_none() { + self.ret_coercion_span.set(Some(expr.span)); + } + let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); + if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { + coercion.coerce_forced_unit( + self, + &cause, + &mut |db| { + let span = fn_decl.output.span(); + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + db.span_label( + span, + format!("expected `{snippet}` because of this return type"), + ); + } + }, + true, + ); + } else { + coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); + } + } + self.tcx.types.never + } + + /// `explicit_return` is `true` if we're checking an explicit `return expr`, + /// and `false` if we're checking a trailing expression. + pub(super) fn check_return_expr( + &self, + return_expr: &'tcx hir::Expr<'tcx>, + explicit_return: bool, + ) { + let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { + span_bug!(return_expr.span, "check_return_expr called outside fn body") + }); + + let ret_ty = ret_coercion.borrow().expected_ty(); + let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); + let mut span = return_expr.span; + // Use the span of the trailing expression for our cause, + // not the span of the entire function + if !explicit_return { + if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr { + span = last_expr.span; + } + } + ret_coercion.borrow_mut().coerce( + self, + &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), + return_expr, + return_expr_ty, + ); + + if self.return_type_has_opaque { + // Point any obligations that were registered due to opaque type + // inference at the return expression. + self.select_obligations_where_possible(false, |errors| { + self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); + }); + } + } + + fn point_at_return_for_opaque_ty_error( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + span: Span, + return_expr_ty: Ty<'tcx>, + ) { + // Don't point at the whole block if it's empty + if span == self.tcx.hir().span(self.body_id) { + return; + } + for err in errors { + let cause = &mut err.obligation.cause; + if let ObligationCauseCode::OpaqueReturnType(None) = cause.code() { + let new_cause = ObligationCause::new( + cause.span, + cause.body_id, + ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))), + ); + *cause = new_cause; + } + } + } + + pub(crate) fn check_lhs_assignable( + &self, + lhs: &'tcx hir::Expr<'tcx>, + err_code: &'static str, + op_span: Span, + adjust_err: impl FnOnce(&mut Diagnostic), + ) { + if lhs.is_syntactic_place_expr() { + return; + } + + // FIXME: Make this use Diagnostic once error codes can be dynamically set. + let mut err = self.tcx.sess.struct_span_err_with_code( + op_span, + "invalid left-hand side of assignment", + DiagnosticId::Error(err_code.into()), + ); + err.span_label(lhs.span, "cannot assign to this expression"); + + self.comes_from_while_condition(lhs.hir_id, |expr| { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern destructuring", + "let ", + Applicability::MachineApplicable, + ); + }); + + adjust_err(&mut err); + + err.emit(); + } + + // 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... + pub(super) fn comes_from_while_condition( + &self, + original_expr_id: HirId, + then: impl FnOnce(&hir::Expr<'_>), + ) { + let mut parent = self.tcx.hir().get_parent_node(original_expr_id); + while let Some(node) = self.tcx.hir().find(parent) { + match node { + hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Loop( + hir::Block { + expr: + Some(hir::Expr { + kind: + hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..), + .. + }), + .. + }, + _, + hir::LoopSource::While, + _, + ), + .. + }) => { + // Check if our original expression is a child of the condition of a while loop + let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| { + self.tcx.hir().find_parent_node(*id) + }) + .take_while(|id| *id != parent) + .any(|id| id == expr.hir_id); + // if it is, then we have a situation like `while Some(0) = value.get(0) {`, + // where `while let` was more likely intended. + if expr_is_ancestor { + then(expr); + } + break; + } + hir::Node::Item(_) + | hir::Node::ImplItem(_) + | hir::Node::TraitItem(_) + | hir::Node::Crate(_) => break, + _ => { + parent = self.tcx.hir().get_parent_node(parent); + } + } + } + } + + // A generic function for checking the 'then' and 'else' clauses in an 'if' + // or 'if-else' expression. + fn check_then_else( + &self, + cond_expr: &'tcx hir::Expr<'tcx>, + then_expr: &'tcx hir::Expr<'tcx>, + opt_else_expr: Option<&'tcx hir::Expr<'tcx>>, + sp: Span, + orig_expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); + + self.warn_if_unreachable( + cond_expr.hir_id, + then_expr.span, + "block in `if` or `while` expression", + ); + + let cond_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); + + let expected = orig_expected.adjust_for_branches(self); + let then_ty = self.check_expr_with_expectation(then_expr, expected); + let then_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); + + // We've already taken the expected type's preferences + // into account when typing the `then` branch. To figure + // out the initial shot at a LUB, we thus only consider + // `expected` if it represents a *hard* constraint + // (`only_has_type`); otherwise, we just go with a + // fresh type variable. + let coerce_to_ty = expected.coercion_target_type(self, sp); + let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); + + coerce.coerce(self, &self.misc(sp), then_expr, then_ty); + + if let Some(else_expr) = opt_else_expr { + let else_ty = self.check_expr_with_expectation(else_expr, expected); + let else_diverges = self.diverges.get(); + + let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected); + let if_cause = self.if_cause( + sp, + cond_expr.span, + then_expr, + else_expr, + then_ty, + else_ty, + opt_suggest_box_span, + ); + + coerce.coerce(self, &if_cause, else_expr, else_ty); + + // We won't diverge unless both branches do (or the condition does). + self.diverges.set(cond_diverges | then_diverges & else_diverges); + } else { + self.if_fallback_coercion(sp, then_expr, &mut coerce); + + // If the condition is false we can't diverge. + self.diverges.set(cond_diverges); + } + + let result_ty = coerce.complete(self); + if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty } + } + + /// Type check assignment expression `expr` of form `lhs = rhs`. + /// The expected type is `()` and is passed to the function for the purposes of diagnostics. + fn check_expr_assign( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + lhs: &'tcx hir::Expr<'tcx>, + rhs: &'tcx hir::Expr<'tcx>, + span: Span, + ) -> Ty<'tcx> { + let expected_ty = expected.coercion_target_type(self, expr.span); + if expected_ty == self.tcx.types.bool { + // The expected type is `bool` but this will result in `()` so we can reasonably + // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. + // The likely cause of this is `if foo = bar { .. }`. + let actual_ty = self.tcx.mk_unit(); + let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); + let lhs_ty = self.check_expr(&lhs); + let rhs_ty = self.check_expr(&rhs); + let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) { + (Applicability::MachineApplicable, true) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + _, + rhs_expr, + ) = lhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_lhs_ty = self.check_expr(&rhs_expr); + (Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty)) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs_expr, + _, + ) = rhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_rhs_ty = self.check_expr(&lhs_expr); + (Applicability::MaybeIncorrect, self.can_coerce(actual_rhs_ty, lhs_ty)) + } else { + (Applicability::MaybeIncorrect, false) + }; + if !lhs.is_syntactic_place_expr() + && lhs.is_approximately_pattern() + && !matches!(lhs.kind, hir::ExprKind::Lit(_)) + { + // Do not suggest `if let x = y` as `==` is way more likely to be the intention. + let hir = self.tcx.hir(); + if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = + hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id))) + { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + applicability, + ); + }; + } + if eq { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "you might have meant to compare for equality", + '=', + applicability, + ); + } + + // 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 lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); + + let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| { + if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { + // Can only assign if the type is sized, so if `DerefMut` yields a type that is + // unsized, do not suggest dereferencing it. + 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.param_env, + ) + .may_apply(); + if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) { + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing here to assign to the mutably borrowed value", + "*", + Applicability::MachineApplicable, + ); + } + } + }; + + // This is (basically) inlined `check_expr_coercable_to_type`, but we want + // to suggest an additional fixup here in `suggest_deref_binop`. + let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); + if let (_, Some(mut diag)) = + self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No) + { + suggest_deref_binop(&mut diag, rhs_ty); + diag.emit(); + } + + self.check_lhs_assignable(lhs, "E0070", span, |err| { + if let Some(rhs_ty) = self.typeck_results.borrow().expr_ty_opt(rhs) { + suggest_deref_binop(err, rhs_ty); + } + }); + + self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); + + if lhs_ty.references_error() || rhs_ty.references_error() { + self.tcx.ty_error() + } else { + self.tcx.mk_unit() + } + } + + pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> { + // for let statements, this is done in check_stmt + let init = let_expr.init; + self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); + // otherwise check exactly as a let statement + self.check_decl(let_expr.into()); + // but return a bool, for this is a boolean expression + self.tcx.types.bool + } + + fn check_expr_loop( + &self, + body: &'tcx hir::Block<'tcx>, + source: hir::LoopSource, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + hir::LoopSource::Loop => { + let coerce_to = expected.coercion_target_type(self, body.span); + Some(CoerceMany::new(coerce_to)) + } + + hir::LoopSource::While | hir::LoopSource::ForLoop => None, + }; + + let ctxt = BreakableCtxt { + coerce, + may_break: false, // Will get updated if/when we find a `break`. + }; + + let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || { + self.check_block_no_value(&body); + }); + + if ctxt.may_break { + // No way to know whether it's diverging because + // of a `break` or an outer `break` or `return`. + self.diverges.set(Diverges::Maybe); + } + + // If we permit break with a value, then result type is + // the LUB of the breaks (possibly ! if none); else, it + // is nil. This makes sense because infinite loops + // (which would have type !) are only possible iff we + // permit break with a value [1]. + if ctxt.coerce.is_none() && !ctxt.may_break { + // [1] + self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break"); + } + ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit()) + } + + /// Checks a method call. + fn check_method_call( + &self, + expr: &'tcx hir::Expr<'tcx>, + segment: &hir::PathSegment<'_>, + rcvr: &'tcx hir::Expr<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let rcvr_t = self.check_expr(&rcvr); + // no need to check for bot/err -- callee does that + let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t); + let span = segment.ident.span; + + let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { + Ok(method) => { + // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to + // trigger this codepath causing `structurally_resolved_type` to emit an error. + + self.write_method_call(expr.hir_id, method); + Ok(method) + } + Err(error) => { + if segment.ident.name != kw::Empty { + if let Some(mut err) = self.report_method_error( + span, + rcvr_t, + segment.ident, + SelfSource::MethodCall(rcvr), + error, + Some((rcvr, args)), + ) { + err.emit(); + } + } + Err(()) + } + }; + + // Call the generic checker. + self.check_method_argument_types(span, expr, method, &args, DontTupleArguments, expected) + } + + fn check_expr_cast( + &self, + e: &'tcx hir::Expr<'tcx>, + t: &'tcx hir::Ty<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // Find the type of `e`. Supply hints based on the type we are casting to, + // if appropriate. + let t_cast = self.to_ty_saving_user_provided_ty(t); + let t_cast = self.resolve_vars_if_possible(t_cast); + let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); + let t_expr = self.resolve_vars_if_possible(t_expr); + + // Eagerly check for some obvious errors. + if t_expr.references_error() || t_cast.references_error() { + self.tcx.ty_error() + } else { + // Defer other checks until we're done type checking. + let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); + match cast::CastCheck::new( + self, + e, + t_expr, + t_cast, + t.span, + expr.span, + self.param_env.constness(), + ) { + Ok(cast_check) => { + debug!( + "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", + t_cast, t_expr, cast_check, + ); + deferred_cast_checks.push(cast_check); + t_cast + } + Err(_) => self.tcx.ty_error(), + } + } + } + + fn check_expr_array( + &self, + args: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let element_ty = if !args.is_empty() { + let coerce_to = expected + .to_option(self) + .and_then(|uty| match *uty.kind() { + ty::Array(ty, _) | ty::Slice(ty) => Some(ty), + _ => None, + }) + .unwrap_or_else(|| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr.span, + }) + }); + let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + assert_eq!(self.diverges.get(), Diverges::Maybe); + for e in args { + let e_ty = self.check_expr_with_hint(e, coerce_to); + let cause = self.misc(e.span); + coerce.coerce(self, &cause, e, e_ty); + } + coerce.complete(self) + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: expr.span, + }) + }; + let array_len = args.len() as u64; + self.suggest_array_len(expr, array_len); + self.tcx.mk_array(element_ty, array_len) + } + + fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) { + let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| { + !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) + }); + let Some((_, + hir::Node::Local(hir::Local { ty: Some(ty), .. }) + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) + ) = parent_node else { + return + }; + if let hir::TyKind::Array(_, length) = ty.peel_refs().kind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(hir_id) + { + match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { + Some(mut err) => { + err.span_suggestion( + span, + "consider specifying the array length", + array_len, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + None => () + } + } + } + + fn check_expr_const_block( + &self, + anon_const: &'tcx hir::AnonConst, + expected: Expectation<'tcx>, + _expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let body = self.tcx.hir().body(anon_const.body); + + // Create a new function context. + let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); + crate::GatherLocalsVisitor::new(&fcx).visit_body(body); + + let ty = fcx.check_expr_with_expectation(&body.value, expected); + fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); + fcx.write_ty(anon_const.hir_id, ty); + ty + } + + fn check_expr_repeat( + &self, + element: &'tcx hir::Expr<'tcx>, + count: &'tcx hir::ArrayLen, + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let count = self.array_length_to_const(count); + if let Some(count) = count.try_eval_usize(tcx, self.param_env) { + self.suggest_array_len(expr, count); + } + + let uty = match expected { + ExpectHasType(uty) => match *uty.kind() { + ty::Array(ty, _) | ty::Slice(ty) => Some(ty), + _ => None, + }, + _ => None, + }; + + let (element_ty, t) = match uty { + Some(uty) => { + self.check_expr_coercable_to_type(&element, uty, None); + (uty, uty) + } + None => { + let ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: element.span, + }); + let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); + (element_ty, ty) + } + }; + + if element_ty.references_error() { + return tcx.ty_error(); + } + + self.check_repeat_element_needs_copy_bound(element, count, element_ty); + + tcx.mk_ty(ty::Array(t, count)) + } + + fn check_repeat_element_needs_copy_bound( + &self, + element: &hir::Expr<'_>, + count: ty::Const<'tcx>, + element_ty: Ty<'tcx>, + ) { + let tcx = self.tcx; + // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy. + match &element.kind { + hir::ExprKind::ConstBlock(..) => return, + hir::ExprKind::Path(qpath) => { + let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); + if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res + { + return; + } + } + _ => {} + } + // If someone calls a const fn, they can extract that call out into a separate constant (or a const + // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck. + let is_const_fn = match element.kind { + hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { + ty::FnDef(def_id, _) => tcx.is_const_fn(def_id), + _ => false, + }, + _ => false, + }; + + // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we + // don't copy that one element, we move it. Only check for Copy if the length is larger. + if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); + let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn }; + self.require_type_meets(element_ty, element.span, code, lang_item); + } + } + + fn check_expr_tuple( + &self, + elts: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let flds = expected.only_has_type(self).and_then(|ty| { + let ty = self.resolve_vars_with_obligations(ty); + match ty.kind() { + ty::Tuple(flds) => Some(&flds[..]), + _ => None, + } + }); + + let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { + Some(fs) if i < fs.len() => { + let ety = fs[i]; + self.check_expr_coercable_to_type(&e, ety, None); + ety + } + _ => self.check_expr_with_expectation(&e, NoExpectation), + }); + let tuple = self.tcx.mk_tup(elt_ts_iter); + if tuple.references_error() { + self.tcx.ty_error() + } else { + self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); + tuple + } + } + + fn check_expr_struct( + &self, + expr: &hir::Expr<'_>, + expected: Expectation<'tcx>, + qpath: &QPath<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + ) -> Ty<'tcx> { + // Find the relevant variant + let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else { + self.check_struct_fields_on_error(fields, base_expr); + return self.tcx.ty_error(); + }; + + // Prohibit struct expressions when non-exhaustive flag is set. + let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); + if !adt.did().is_local() && variant.is_field_list_non_exhaustive() { + self.tcx + .sess + .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); + } + + self.check_expr_struct_fields( + adt_ty, + expected, + expr.hir_id, + qpath.span(), + variant, + fields, + base_expr, + expr.span, + ); + + self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); + adt_ty + } + + fn check_expr_struct_fields( + &self, + adt_ty: Ty<'tcx>, + expected: Expectation<'tcx>, + expr_id: hir::HirId, + span: Span, + variant: &'tcx ty::VariantDef, + ast_fields: &'tcx [hir::ExprField<'tcx>], + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + expr_span: Span, + ) { + let tcx = self.tcx; + + let expected_inputs = + self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]); + let adt_ty_hint = if let Some(expected_inputs) = expected_inputs { + expected_inputs.get(0).cloned().unwrap_or(adt_ty) + } else { + adt_ty + }; + // re-link the regions that EIfEO can erase. + self.demand_eqtype(span, adt_ty_hint, adt_ty); + + let ty::Adt(adt, substs) = adt_ty.kind() else { + span_bug!(span, "non-ADT passed to check_expr_struct_fields"); + }; + let adt_kind = adt.adt_kind(); + + let mut remaining_fields = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) + .collect::<FxHashMap<_, _>>(); + + let mut seen_fields = FxHashMap::default(); + + let mut error_happened = false; + + // Type-check each field. + for (idx, field) in ast_fields.iter().enumerate() { + let ident = tcx.adjust_ident(field.ident, variant.def_id); + let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { + seen_fields.insert(ident, field.span); + self.write_field_index(field.hir_id, i); + + // We don't look at stability attributes on + // struct-like enums (yet...), but it's definitely not + // a bug to have constructed one. + if adt_kind != AdtKind::Enum { + tcx.check_stability(v_field.did, Some(expr_id), field.span, None); + } + + self.field_ty(field.span, v_field, substs) + } else { + error_happened = true; + if let Some(prev_span) = seen_fields.get(&ident) { + tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }); + } else { + self.report_unknown_field( + adt_ty, + variant, + field, + ast_fields, + adt.variant_descr(), + expr_span, + ); + } + + tcx.ty_error() + }; + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + let ty = self.check_expr_with_hint(&field.expr, field_type); + let (_, diag) = + 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); + } + diag.emit(); + } + } + + // Make sure the programmer specified correct number of fields. + if adt_kind == AdtKind::Union { + if ast_fields.len() != 1 { + struct_span_err!( + tcx.sess, + span, + E0784, + "union expressions should have exactly one field", + ) + .emit(); + } + } + + // If check_expr_struct_fields hit an error, do not attempt to populate + // 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 { + return; + } + + if let Some(base_expr) = base_expr { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update { + if adt.is_struct() { + // Make some fresh substitutions for our ADT type. + let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + let fru_tys = variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr_span, + self.field_ty(base_expr.span, f, fresh_substs), + ); + let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = self.field_ty(base_expr.span, f, substs); + let cause = self.misc(base_expr.span); + match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + Err(_) => { + // This should never happen, since we're just subtyping the + // remaining_fields, but it's fine to emit this, I guess. + self.err_ctxt() + .report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch(variant.name, ident.name), + ) + .emit(); + } + } + } + self.resolve_vars_if_possible(fru_ty) + }) + .collect(); + // The use of fresh substs that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs); + self.check_expr_has_type_or_error( + base_expr, + self.resolve_vars_if_possible(fresh_base_ty), + |_| {}, + ); + fru_tys + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.check_expr(base_expr); + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + return; + } + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); + let same_adt = match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, + _ => false, + }; + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess.parse_sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + match adt_ty.kind() { + 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)) + }) + .collect(), + _ => { + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + return; + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); + } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { + debug!(?remaining_fields); + let private_fields: Vec<&ty::FieldDef> = variant + .fields + .iter() + .filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr_id), tcx)) + .collect(); + + if !private_fields.is_empty() { + self.report_private_fields(adt_ty, span, private_fields, ast_fields); + } else { + self.report_missing_fields( + adt_ty, + span, + remaining_fields, + variant, + ast_fields, + substs, + ); + } + } + } + + fn check_struct_fields_on_error( + &self, + fields: &'tcx [hir::ExprField<'tcx>], + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, + ) { + for field in fields { + self.check_expr(&field.expr); + } + if let Some(base) = *base_expr { + self.check_expr(&base); + } + } + + /// Report an error for a struct field expression when there are fields which aren't provided. + /// + /// ```text + /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ missing `you_can_use_this_field` + /// + /// error: aborting due to previous error + /// ``` + fn report_missing_fields( + &self, + adt_ty: Ty<'tcx>, + span: Span, + remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, + variant: &'tcx ty::VariantDef, + ast_fields: &'tcx [hir::ExprField<'tcx>], + substs: SubstsRef<'tcx>, + ) { + let len = remaining_fields.len(); + + let mut displayable_field_names: Vec<&str> = + remaining_fields.keys().map(|ident| ident.as_str()).collect(); + // sorting &str primitives here, sort_unstable is ok + displayable_field_names.sort_unstable(); + + let mut truncated_fields_error = String::new(); + let remaining_fields_names = match &displayable_field_names[..] { + [field1] => format!("`{}`", field1), + [field1, field2] => format!("`{field1}` and `{field2}`"), + [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"), + _ => { + truncated_fields_error = + format!(" and {} other field{}", len - 3, pluralize!(len - 3)); + displayable_field_names + .iter() + .take(3) + .map(|n| format!("`{n}`")) + .collect::<Vec<_>>() + .join(", ") + } + }; + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0063, + "missing field{} {}{} in initializer of `{}`", + pluralize!(len), + remaining_fields_names, + truncated_fields_error, + adt_ty + ); + err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); + + if let Some(last) = ast_fields.last() { + self.suggest_fru_from_range(last, variant, substs, &mut err); + } + + err.emit(); + } + + /// If the last field is a range literal, but it isn't supposed to be, then they probably + /// meant to use functional update syntax. + fn suggest_fru_from_range( + &self, + last_expr_field: &hir::ExprField<'tcx>, + variant: &ty::VariantDef, + substs: SubstsRef<'tcx>, + err: &mut Diagnostic, + ) { + // I don't use 'is_range_literal' because only double-sided, half-open ranges count. + if let ExprKind::Struct( + QPath::LangItem(LangItem::Range, ..), + &[ref range_start, ref range_end], + _, + ) = last_expr_field.expr.kind + && let variant_field = + variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident) + && let range_def_id = self.tcx.lang_items().range_struct() + && variant_field + .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) + .map(|adt| adt.did()) + != range_def_id + { + let instead = 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, + ); + } + } + + /// Report an error for a struct field expression when there are invisible fields. + /// + /// ```text + /// error: cannot construct `Foo` with struct literal syntax due to private fields + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ + /// + /// error: aborting due to previous error + /// ``` + fn report_private_fields( + &self, + adt_ty: Ty<'tcx>, + span: Span, + private_fields: Vec<&ty::FieldDef>, + used_fields: &'tcx [hir::ExprField<'tcx>], + ) { + let mut err = self.tcx.sess.struct_span_err( + span, + &format!( + "cannot construct `{adt_ty}` with struct literal syntax due to private fields", + ), + ); + let (used_private_fields, remaining_private_fields): ( + Vec<(Symbol, Span, bool)>, + Vec<(Symbol, Span, bool)>, + ) = private_fields + .iter() + .map(|field| { + match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { + Some(used_field) => (field.name, used_field.span, true), + None => (field.name, self.tcx.def_span(field.did), false), + } + }) + .partition(|field| field.2); + err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); + if !remaining_private_fields.is_empty() { + let remaining_private_fields_len = remaining_private_fields.len(); + let names = match &remaining_private_fields + .iter() + .map(|(name, _, _)| name) + .collect::<Vec<_>>()[..] + { + _ if remaining_private_fields_len > 6 => String::new(), + [name] => format!("`{name}` "), + [names @ .., last] => { + let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); + format!("{} and `{last}` ", names.join(", ")) + } + [] => unreachable!(), + }; + err.note(format!( + "... and other private field{s} {names}that {were} not provided", + s = pluralize!(remaining_private_fields_len), + were = pluralize!("was", remaining_private_fields_len), + )); + } + err.emit(); + } + + fn report_unknown_field( + &self, + ty: Ty<'tcx>, + variant: &'tcx ty::VariantDef, + field: &hir::ExprField<'_>, + skip_fields: &[hir::ExprField<'_>], + kind_name: &str, + expr_span: Span, + ) { + if variant.is_recovered() { + self.set_tainted_by_errors(); + return; + } + let mut err = self.err_ctxt().type_error_struct_with_diag( + field.ident.span, + |actual| match ty.kind() { + ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( + self.tcx.sess, + field.ident.span, + E0559, + "{} `{}::{}` has no field named `{}`", + kind_name, + actual, + variant.name, + field.ident + ), + _ => struct_span_err!( + self.tcx.sess, + field.ident.span, + E0560, + "{} `{}` has no field named `{}`", + kind_name, + actual, + field.ident + ), + }, + ty, + ); + + let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); + match variant.ctor_kind { + CtorKind::Fn => match ty.kind() { + ty::Adt(adt, ..) if adt.is_enum() => { + err.span_label( + variant_ident_span, + format!( + "`{adt}::{variant}` defined here", + adt = ty, + variant = variant.name, + ), + ); + err.span_label(field.ident.span, "field does not exist"); + err.span_suggestion_verbose( + expr_span, + &format!( + "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", + adt = ty, + variant = variant.name, + ), + format!( + "{adt}::{variant}(/* fields */)", + adt = ty, + variant = variant.name, + ), + Applicability::HasPlaceholders, + ); + } + _ => { + err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); + err.span_label(field.ident.span, "field does not exist"); + err.span_suggestion_verbose( + expr_span, + &format!( + "`{adt}` is a tuple {kind_name}, use the appropriate syntax", + adt = ty, + kind_name = kind_name, + ), + format!("{adt}(/* fields */)", adt = ty), + Applicability::HasPlaceholders, + ); + } + }, + _ => { + // prevent all specified fields from being suggested + let skip_fields = skip_fields.iter().map(|x| x.ident.name); + if let Some(field_name) = self.suggest_field_name( + variant, + field.ident.name, + skip_fields.collect(), + expr_span, + ) { + err.span_suggestion( + field.ident.span, + "a field with a similar name exists", + field_name, + Applicability::MaybeIncorrect, + ); + } else { + match ty.kind() { + ty::Adt(adt, ..) => { + if adt.is_enum() { + err.span_label( + field.ident.span, + format!("`{}::{}` does not have this field", ty, variant.name), + ); + } else { + err.span_label( + field.ident.span, + format!("`{ty}` does not have this field"), + ); + } + let available_field_names = + self.available_field_names(variant, expr_span); + if !available_field_names.is_empty() { + err.note(&format!( + "available fields are: {}", + self.name_series_display(available_field_names) + )); + } + } + _ => bug!("non-ADT passed to report_unknown_field"), + } + }; + } + } + err.emit(); + } + + // Return a hint about the closest match in field names + fn suggest_field_name( + &self, + variant: &'tcx ty::VariantDef, + field: Symbol, + skip: Vec<Symbol>, + // The span where stability will be checked + span: Span, + ) -> Option<Symbol> { + let names = variant + .fields + .iter() + .filter_map(|field| { + // ignore already set fields and private fields from non-local crates + // and unstable fields. + if skip.iter().any(|&x| x == field.name) + || (!variant.def_id.is_local() && !field.vis.is_public()) + || matches!( + self.tcx.eval_stability(field.did, None, span, None), + stability::EvalResult::Deny { .. } + ) + { + None + } else { + Some(field.name) + } + }) + .collect::<Vec<Symbol>>(); + + find_best_match_for_name(&names, field, None) + } + + fn available_field_names( + &self, + variant: &'tcx ty::VariantDef, + access_span: Span, + ) -> Vec<Symbol> { + variant + .fields + .iter() + .filter(|field| { + let def_scope = self + .tcx + .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) + .1; + field.vis.is_accessible_from(def_scope, self.tcx) + && !matches!( + self.tcx.eval_stability(field.did, None, access_span, None), + stability::EvalResult::Deny { .. } + ) + }) + .filter(|field| !self.tcx.is_doc_hidden(field.did)) + .map(|field| field.name) + .collect() + } + + fn name_series_display(&self, names: Vec<Symbol>) -> String { + // dynamic limit, to never omit just one field + let limit = if names.len() == 6 { 6 } else { 5 }; + let mut display = + names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); + if names.len() > limit { + display = format!("{} ... and {} others", display, names.len() - limit); + } + display + } + + // Check field access expressions + fn check_field( + &self, + expr: &'tcx hir::Expr<'tcx>, + base: &'tcx hir::Expr<'tcx>, + field: Ident, + ) -> Ty<'tcx> { + debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); + let base_ty = self.check_expr(base); + let base_ty = self.structurally_resolved_type(base.span, base_ty); + let mut private_candidate = None; + let mut autoderef = self.autoderef(expr.span, base_ty); + while let Some((deref_base_ty, _)) = autoderef.next() { + debug!("deref_base_ty: {:?}", deref_base_ty); + match deref_base_ty.kind() { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + debug!("struct named {:?}", deref_base_ty); + let (ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); + let fields = &base_def.non_enum_variant().fields; + if let Some(index) = fields + .iter() + .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) + { + let field = &fields[index]; + let field_ty = self.field_ty(expr.span, field, substs); + // Save the index of all fields regardless of their visibility in case + // of error recovery. + self.write_field_index(expr.hir_id, index); + let adjustments = self.adjust_steps(&autoderef); + if field.vis.is_accessible_from(def_scope, self.tcx) { + self.apply_adjustments(base, adjustments); + self.register_predicates(autoderef.into_obligations()); + + self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); + return field_ty; + } + private_candidate = Some((adjustments, base_def.did(), field_ty)); + } + } + ty::Tuple(tys) => { + let fstr = field.as_str(); + if let Ok(index) = fstr.parse::<usize>() { + if fstr == index.to_string() { + if let Some(&field_ty) = tys.get(index) { + let adjustments = self.adjust_steps(&autoderef); + self.apply_adjustments(base, adjustments); + self.register_predicates(autoderef.into_obligations()); + + self.write_field_index(expr.hir_id, index); + return field_ty; + } + } + } + } + _ => {} + } + } + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); + + if let Some((adjustments, did, field_ty)) = private_candidate { + // (#90483) apply adjustments to avoid ExprUseVisitor from + // creating erroneous projection. + self.apply_adjustments(base, adjustments); + self.ban_private_field_access(expr, base_ty, field, did); + return field_ty; + } + + if field.name == kw::Empty { + } else if self.method_exists(field, base_ty, expr.hir_id, true) { + self.ban_take_value_of_method(expr, base_ty, field); + } else if !base_ty.is_primitive_ty() { + self.ban_nonexisting_field(field, base, expr, base_ty); + } else { + let field_name = field.to_string(); + let mut err = type_error_struct!( + self.tcx().sess, + field.span, + base_ty, + E0610, + "`{base_ty}` is a primitive type and therefore doesn't have fields", + ); + let is_valid_suffix = |field: &str| { + if field == "f32" || field == "f64" { + return true; + } + let mut chars = field.chars().peekable(); + match chars.peek() { + Some('e') | Some('E') => { + chars.next(); + if let Some(c) = chars.peek() + && !c.is_numeric() && *c != '-' && *c != '+' + { + return false; + } + while let Some(c) = chars.peek() { + if !c.is_numeric() { + break; + } + chars.next(); + } + } + _ => (), + } + let suffix = chars.collect::<String>(); + suffix.is_empty() || suffix == "f32" || suffix == "f64" + }; + let maybe_partial_suffix = |field: &str| -> Option<&str> { + let first_chars = ['f', 'l']; + if field.len() >= 1 + && field.to_lowercase().starts_with(first_chars) + && field[1..].chars().all(|c| c.is_ascii_digit()) + { + if field.to_lowercase().starts_with(['f']) { Some("f32") } else { Some("f64") } + } else { + None + } + }; + if let ty::Infer(ty::IntVar(_)) = base_ty.kind() + && let ExprKind::Lit(Spanned { + node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), + .. + }) = base.kind + && !base.span.from_expansion() + { + if is_valid_suffix(&field_name) { + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "if intended to be a floating point literal, consider adding a `0` after the period", + '0', + Applicability::MaybeIncorrect, + ); + } else if let Some(correct_suffix) = maybe_partial_suffix(&field_name) { + err.span_suggestion_verbose( + field.span, + format!("if intended to be a floating point literal, consider adding a `0` after the period and a `{correct_suffix}` suffix"), + format!("0{correct_suffix}"), + Applicability::MaybeIncorrect, + ); + } + } + err.emit(); + } + + self.tcx().ty_error() + } + + fn suggest_await_on_field_access( + &self, + err: &mut Diagnostic, + field_ident: Ident, + 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 mut add_label = true; + if let ty::Adt(def, _) = output_ty.skip_binder().kind() { + // no field access on enum type + if !def.is_enum() { + if def + .non_enum_variant() + .fields + .iter() + .any(|field| field.ident(self.tcx) == field_ident) + { + add_label = false; + err.span_label( + field_ident.span, + "field not available in `impl Future`, but it is available in its `Output`", + ); + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider `await`ing on the `Future` and access the field of its `Output`", + ".await", + Applicability::MaybeIncorrect, + ); + } + } + } + if add_label { + err.span_label(field_ident.span, &format!("field not found in `{ty}`")); + } + } + + fn ban_nonexisting_field( + &self, + ident: Ident, + base: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + base_ty: Ty<'tcx>, + ) { + debug!( + "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", + ident, base, expr, base_ty + ); + let mut err = self.no_such_field_err(ident, base_ty, base.hir_id); + + match *base_ty.peel_refs().kind() { + ty::Array(_, len) => { + self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len); + } + ty::RawPtr(..) => { + self.suggest_first_deref_field(&mut err, expr, base, ident); + } + ty::Adt(def, _) if !def.is_enum() => { + self.suggest_fields_on_recordish(&mut err, def, ident, expr.span); + } + ty::Param(param_ty) => { + self.point_at_param_definition(&mut err, param_ty); + } + ty::Opaque(_, _) => { + self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); + } + _ => {} + } + + self.suggest_fn_call(&mut err, base, base_ty, |output_ty| { + if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() { + def.non_enum_variant().fields.iter().any(|field| { + field.ident(self.tcx) == ident + && field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) + }) + } else if let ty::Tuple(tys) = output_ty.kind() + && let Ok(idx) = ident.as_str().parse::<usize>() + { + idx < tys.len() + } else { + false + } + }); + + if ident.name == kw::Await { + // We know by construction that `<expr>.await` is either on Rust 2015 + // or results in `ExprKind::Await`. Suggest switching the edition to 2018. + err.note("to `.await` a `Future`, switch to Rust 2018 or later"); + err.help_use_latest_edition(); + } + + err.emit(); + } + + fn ban_private_field_access( + &self, + expr: &hir::Expr<'_>, + expr_t: Ty<'tcx>, + field: Ident, + base_did: DefId, + ) { + let struct_path = self.tcx().def_path_str(base_did); + let kind_name = self.tcx().def_kind(base_did).descr(base_did); + let mut err = struct_span_err!( + self.tcx().sess, + field.span, + E0616, + "field `{field}` of {kind_name} `{struct_path}` is private", + ); + err.span_label(field.span, "private field"); + // Also check if an accessible method exists, which is often what is meant. + if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) + { + self.suggest_method_call( + &mut err, + &format!("a method `{field}` also exists, call it with parentheses"), + field, + expr_t, + expr, + None, + ); + } + err.emit(); + } + + fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { + let mut err = type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0615, + "attempted to take value of method `{field}` on type `{expr_t}`", + ); + err.span_label(field.span, "method, not a field"); + let expr_is_call = + if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id)) + { + expr.hir_id == callee.hir_id + } else { + false + }; + let expr_snippet = + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default(); + let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')'); + let after_open = expr.span.lo() + rustc_span::BytePos(1); + let before_close = expr.span.hi() - rustc_span::BytePos(1); + + if expr_is_call && is_wrapped { + err.multipart_suggestion( + "remove wrapping parentheses to call the method", + vec![ + (expr.span.with_hi(after_open), String::new()), + (expr.span.with_lo(before_close), String::new()), + ], + Applicability::MachineApplicable, + ); + } else if !self.expr_in_place(expr.hir_id) { + // Suggest call parentheses inside the wrapping parentheses + let span = if is_wrapped { + expr.span.with_lo(after_open).with_hi(before_close) + } else { + expr.span + }; + self.suggest_method_call( + &mut err, + "use parentheses to call the method", + field, + expr_t, + expr, + Some(span), + ); + } else if let ty::RawPtr(ty_and_mut) = expr_t.kind() + && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() + && let ExprKind::Field(base_expr, _) = expr.kind + && adt_def.variants().len() == 1 + && adt_def + .variants() + .iter() + .next() + .unwrap() + .fields + .iter() + .any(|f| f.ident(self.tcx) == field) + { + err.multipart_suggestion( + "to access the field, dereference first", + vec![ + (base_expr.span.shrink_to_lo(), "(*".to_string()), + (base_expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + err.help("methods are immutable and cannot be assigned to"); + } + + err.emit(); + } + + fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let generic_param = generics.type_param(¶m, self.tcx); + if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { + return; + } + let param_def_id = generic_param.def_id; + let param_hir_id = match param_def_id.as_local() { + Some(x) => self.tcx.hir().local_def_id_to_hir_id(x), + None => return, + }; + let param_span = self.tcx.hir().span(param_hir_id); + let param_name = self.tcx.hir().ty_param_name(param_def_id.expect_local()); + + err.span_label(param_span, &format!("type parameter '{param_name}' declared here")); + } + + fn suggest_fields_on_recordish( + &self, + err: &mut Diagnostic, + def: ty::AdtDef<'tcx>, + field: Ident, + access_span: Span, + ) { + if let Some(suggested_field_name) = + self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) + { + err.span_suggestion( + field.span, + "a field with a similar name exists", + suggested_field_name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(field.span, "unknown field"); + let struct_variant_def = def.non_enum_variant(); + let field_names = self.available_field_names(struct_variant_def, access_span); + if !field_names.is_empty() { + err.note(&format!( + "available fields are: {}", + self.name_series_display(field_names), + )); + } + } + } + + fn maybe_suggest_array_indexing( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + base: &hir::Expr<'_>, + field: Ident, + len: ty::Const<'tcx>, + ) { + if let (Some(len), Ok(user_index)) = + (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) + && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) + { + let help = "instead of using tuple indexing, use array indexing"; + let suggestion = format!("{base}[{field}]"); + let applicability = if len < user_index { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion(expr.span, help, suggestion, applicability); + } + } + + fn suggest_first_deref_field( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + base: &hir::Expr<'_>, + field: Ident, + ) { + if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { + let msg = format!("`{base}` is a raw pointer; try dereferencing it"); + let suggestion = format!("(*{base}).{field}"); + err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); + } + } + + fn no_such_field_err( + &self, + field: Ident, + expr_t: Ty<'tcx>, + id: HirId, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let span = field.span; + debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); + + let mut err = type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0609, + "no field `{field}` on type `{expr_t}`", + ); + + // try to add a suggestion in case the field is a nested field of a field of the Adt + let mod_id = self.tcx.parent_module(id).to_def_id(); + if let Some((fields, substs)) = + self.get_field_candidates_considering_privacy(span, expr_t, mod_id) + { + let candidate_fields: Vec<_> = fields + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|candidate_field, _| candidate_field.ident(self.tcx()) == field, + candidate_field, + substs, + vec![], + mod_id, + ) + }) + .map(|mut field_path| { + field_path.pop(); + field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::<Vec<String>>() + .join(".") + }) + .collect::<Vec<_>>(); + + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + field.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a field of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); + } + } + err + } + + pub(crate) fn get_field_candidates_considering_privacy( + &self, + span: Span, + base_ty: Ty<'tcx>, + mod_id: DefId, + ) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> { + debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); + + for (base_t, _) in self.autoderef(span, base_ty) { + match base_t.kind() { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + let tcx = self.tcx; + let fields = &base_def.non_enum_variant().fields; + // Some struct, e.g. some that impl `Deref`, have all private fields + // because you're expected to deref them to access the _real_ fields. + // This, for example, will help us suggest accessing a field through a `Box<T>`. + if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { + continue; + } + return Some(( + fields + .iter() + .filter(move |field| field.vis.is_accessible_from(mod_id, tcx)) + // For compile-time reasons put a limit on number of fields we search + .take(100), + substs, + )); + } + _ => {} + } + } + None + } + + /// This method is called after we have encountered a missing field error to recursively + /// search for the field + pub(crate) fn check_for_nested_field_satisfying( + &self, + span: Span, + matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, + candidate_field: &ty::FieldDef, + subst: SubstsRef<'tcx>, + mut field_path: Vec<Ident>, + mod_id: DefId, + ) -> Option<Vec<Ident>> { + debug!( + "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", + span, candidate_field, field_path + ); + + if field_path.len() > 3 { + // For compile-time reasons and to avoid infinite recursion we only check for fields + // up to a depth of three + None + } else { + field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0()); + let field_ty = candidate_field.ty(self.tcx, subst); + if matches(candidate_field, field_ty) { + return Some(field_path); + } else if let Some((nested_fields, subst)) = + self.get_field_candidates_considering_privacy(span, field_ty, mod_id) + { + // recursively search fields of `candidate_field` if it's a ty::Adt + for field in nested_fields { + if let Some(field_path) = self.check_for_nested_field_satisfying( + span, + matches, + field, + subst, + field_path.clone(), + mod_id, + ) { + return Some(field_path); + } + } + } + None + } + } + + fn check_expr_index( + &self, + base: &'tcx hir::Expr<'tcx>, + idx: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let base_t = self.check_expr(&base); + let idx_t = self.check_expr(&idx); + + if base_t.references_error() { + base_t + } else if idx_t.references_error() { + idx_t + } else { + let base_t = self.structurally_resolved_type(base.span, base_t); + match self.lookup_indexing(expr, base, base_t, idx, idx_t) { + 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.point_at_index_if_possible(errors, idx.span) + }); + element_ty + } + None => { + let mut err = type_error_struct!( + self.tcx.sess, + expr.span, + base_t, + E0608, + "cannot index into a value of type `{base_t}`", + ); + // Try to give some advice about indexing tuples. + if let ty::Tuple(..) = base_t.kind() { + let mut needs_note = true; + // If the index is an integer, we can show the actual + // fixed expression: + if let ExprKind::Lit(ref lit) = idx.kind { + if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { + let snip = self.tcx.sess.source_map().span_to_snippet(base.span); + if let Ok(snip) = snip { + err.span_suggestion( + expr.span, + "to access tuple elements, use", + format!("{snip}.{i}"), + Applicability::MachineApplicable, + ); + needs_note = false; + } + } + } + if needs_note { + err.help( + "to access tuple elements, use tuple indexing \ + syntax (e.g., `tuple.0`)", + ); + } + } + err.emit(); + self.tcx.ty_error() + } + } + } + } + + fn point_at_index_if_possible( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + span: Span, + ) { + for error in errors { + match error.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(predicate) + if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { + } + _ => continue, + } + error.obligation.cause.span = span; + } + } + + fn check_expr_yield( + &self, + value: &'tcx hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + src: &'tcx hir::YieldSource, + ) -> Ty<'tcx> { + match self.resume_yield_tys { + Some((resume_ty, yield_ty)) => { + self.check_expr_coercable_to_type(&value, yield_ty, None); + + resume_ty + } + // Given that this `yield` expression was generated as a result of lowering a `.await`, + // we know that the yield type must be `()`; however, the context won't contain this + // information. Hence, we check the source of the yield expression here and check its + // value's type against `()` (this check should always hold). + None if src.is_await() => { + self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); + self.tcx.mk_unit() + } + _ => { + self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); + // Avoid expressions without types during writeback (#78653). + self.check_expr(value); + self.tcx.mk_unit() + } + } + } + + fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { + let needs = if is_input { Needs::None } else { Needs::MutPlace }; + let ty = self.check_expr_with_needs(expr, needs); + self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); + + if !is_input && !expr.is_syntactic_place_expr() { + let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); + err.span_label(expr.span, "cannot assign to this expression"); + err.emit(); + } + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type candidate list in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.structurally_resolved_type(expr.span, ty); + match *ty.kind() { + ty::FnDef(..) => { + let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); + self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); + } + ty::Ref(_, base_ty, mutbl) => { + let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); + self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); + } + _ => {} + } + } + } + + fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { + for (op, _op_sp) in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } => { + self.check_expr_asm_operand(expr, true); + } + hir::InlineAsmOperand::Out { expr: Some(expr), .. } + | hir::InlineAsmOperand::InOut { expr, .. } => { + self.check_expr_asm_operand(expr, false); + } + hir::InlineAsmOperand::Out { expr: None, .. } => {} + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + self.check_expr_asm_operand(out_expr, false); + } + } + // `AnonConst`s have their own body and is type-checked separately. + // As they don't flow into the type system we don't need them to + // be well-formed. + hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} + hir::InlineAsmOperand::SymStatic { .. } => {} + } + } + if asm.options.contains(ast::InlineAsmOptions::NORETURN) { + self.tcx.types.never + } else { + self.tcx.mk_unit() + } + } +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 74a5b6e42..fce2a5888 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -89,15 +89,6 @@ enum ConsumeMode { Move, } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum MutateMode { - Init, - /// Example: `x = y` - JustWrite, - /// Example: `x += y` - WriteAndRead, -} - /// The ExprUseVisitor type /// /// This is the code that actually walks the tree. @@ -134,7 +125,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// - `typeck_results` --- typeck results for the code being analyzed pub fn new( delegate: &'a mut (dyn Delegate<'tcx> + 'a), - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, body_owner: LocalDefId, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -233,8 +224,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_exprs(args); } - hir::ExprKind::MethodCall(.., args, _) => { + hir::ExprKind::MethodCall(.., receiver, args, _) => { // callee.m(args) + self.consume_expr(receiver); self.consume_exprs(args); } @@ -497,7 +489,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); if let Some(els) = els { - // borrowing because we need to test the descriminant + // borrowing because we need to test the discriminant self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); self.walk_block(els) } @@ -582,7 +574,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { for adjustment in adjustments { debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); match adjustment.kind { - adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { + adjustment::Adjust::NeverToAny + | adjustment::Adjust::Pointer(_) + | adjustment::Adjust::DynStar => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. self.delegate_consume(&place_with_id, place_with_id.hir_id); diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 4059b3403..747ecb036 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,4 +1,4 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_data_structures::{ fx::{FxHashMap, FxHashSet}, graph::WithSuccessors, @@ -72,7 +72,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // // - Unconstrained ints are replaced with `i32`. // - // - Unconstrained floats are replaced with with `f64`. + // - Unconstrained floats are replaced with `f64`. // // - Non-numerics may get replaced with `()` or `!`, depending on // how they were categorized by `calculate_diverging_fallback` diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 3a8093345..6a1cffe3e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,12 +1,7 @@ -use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, -}; -use crate::check::callee::{self, DeferredCallResolution}; -use crate::check::method::{self, MethodCallee, SelfSource}; -use crate::check::rvalue_scopes; -use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; - +use crate::callee::{self, DeferredCallResolution}; +use crate::method::{self, MethodCallee, SelfSource}; +use crate::rvalue_scopes; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -15,26 +10,28 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::astconv::{ + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, IsMethodCall, PathSeg, +}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{ - self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, -}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, ToPredicate, Ty, UserType, }; +use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; 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::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; @@ -60,17 +57,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( + let msg = format!("unreachable {}", kind); + self.tcx().struct_span_lint_hir( + lint::builtin::UNREACHABLE_CODE, + id, + span, + &msg, + |lint| { + lint.span_label(span, &msg).span_label( orig_span, custom_note .unwrap_or("any code following this expression is unreachable"), ) - .emit(); - }) + }, + ) } } } @@ -83,21 +83,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) } - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")] + #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( &self, mut ty: Ty<'tcx>, mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) -> Ty<'tcx> { // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!("no inference var, nothing needs doing"); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(ty); - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!(?ty); return ty; } @@ -107,10 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // indirect dependencies that don't seem worth tracking // precisely. self.select_obligations_where_possible(false, mutate_fulfillment_errors); - ty = self.resolve_vars_if_possible(ty); - - debug!(?ty); - ty + self.resolve_vars_if_possible(ty) } pub(in super::super) fn record_deferred_call_resolution( @@ -412,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_pred: None, + output_ty: None, }, ), self.param_env, @@ -492,21 +489,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), - hir::ArrayLen::Body(anon_const) => self.to_const(anon_const), + 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); + self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); + self.normalize_associated_types_in(span, c) + } } } - pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::WellFormed(None), - ); - c - } - pub fn const_arg_to_const( &self, ast_c: &hir::AnonConst, @@ -565,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub fn register_wf_obligation( &self, - arg: subst::GenericArg<'tcx>, + arg: ty::GenericArg<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -610,18 +602,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { self.select_obligations_where_possible(false, |_| {}); - crate::check::generator_interior::resolve_interior( - self, def_id, body_id, interior, kind, - ); + crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } #[instrument(skip(self), level = "debug")] pub(in super::super) fn select_all_obligations_or_error(&self) { - let errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); + let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); if !errors.is_empty() { - self.report_fulfillment_errors(&errors, self.inh.body_id, false); + self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); } } @@ -634,7 +625,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); - self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); + self.adjust_fulfillment_errors_for_expr_obligation(&mut result); + self.err_ctxt().report_fulfillment_errors( + &result, + self.inh.body_id, + fallback_has_occurred, + ); } } @@ -831,23 +827,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = item_ty.subst(self.tcx, substs); self.write_resolution(hir_id, Ok((def_kind, def_id))); - self.add_required_obligations_with_code( - span, - def_id, - &substs, - match lang_item { - hir::LangItem::IntoFutureIntoFuture => { - ObligationCauseCode::AwaitableExpr(expr_hir_id) - } - hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { - ObligationCauseCode::ForLoopIterator - } - hir::LangItem::TryTraitFromOutput - | hir::LangItem::TryTraitFromResidual - | hir::LangItem::TryTraitBranch => ObligationCauseCode::QuestionMark, - _ => traits::ItemObligation(def_id), - }, - ); + + let code = match lang_item { + hir::LangItem::IntoFutureIntoFuture => { + Some(ObligationCauseCode::AwaitableExpr(expr_hir_id)) + } + hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { + Some(ObligationCauseCode::ForLoopIterator) + } + hir::LangItem::TryTraitFromOutput + | hir::LangItem::TryTraitFromResidual + | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark), + _ => None, + }; + if let Some(code) = code { + self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone()); + } else { + self.add_required_obligations_for_hir(span, def_id, substs, hir_id); + } + (Res::Def(def_kind, def_id), ty) } @@ -986,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind { + if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { if self .typeck_results .borrow() @@ -1273,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut self, param: &ty::GenericParamDef, arg: &GenericArg<'_>, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into() @@ -1297,10 +1295,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn inferred_kind( &mut self, - substs: Option<&[subst::GenericArg<'tcx>]>, + substs: Option<&[ty::GenericArg<'tcx>]>, param: &ty::GenericParamDef, infer_args: bool, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); match param.kind { GenericParamDefKind::Lifetime => { @@ -1359,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // First, store the "user substs" for later. self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - self.add_required_obligations(span, def_id, &substs); + self.add_required_obligations_for_hir(span, def_id, &substs, hir_id); // Substitute the values for the type parameters into the type of // the referenced item. @@ -1396,35 +1394,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Add all the obligations that are required, substituting and normalized appropriately. - pub(crate) fn add_required_obligations( + pub(crate) fn add_required_obligations_for_hir( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, + substs: SubstsRef<'tcx>, + hir_id: hir::HirId, ) { - self.add_required_obligations_with_code( - span, - def_id, - substs, - traits::ItemObligation(def_id), - ) + self.add_required_obligations_with_code(span, def_id, substs, |idx, span| { + if span.is_dummy() { + ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx) + } else { + ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx) + } + }) } - #[tracing::instrument(level = "debug", skip(self, span, def_id, substs))] + #[instrument(level = "debug", skip(self, code, span, substs))] fn add_required_obligations_with_code( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, - code: ObligationCauseCode<'tcx>, + substs: SubstsRef<'tcx>, + code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>, ) { + let param_env = self.param_env; + + let remap = match self.tcx.def_kind(def_id) { + // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when + // `Self: Trait` is satisfied because it does not matter whether the impl is `const`. + // Therefore we have to remap the param env here to be non-const. + hir::def::DefKind::AssocConst => true, + hir::def::DefKind::AssocFn + if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait => + { + // N.B.: All callsites to this function involve checking a path expression. + // + // When instantiating a trait method as a function item, it does not actually matter whether + // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as + // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would + // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a + // `const fn` pointer. + // + // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy + // `~const FnOnce` or can be coerced to `const fn` pointer. + true + } + _ => false, + }; let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); - for obligation in traits::predicates_for_generics( - traits::ObligationCause::new(span, self.body_id, code), - self.param_env, + for mut obligation in traits::predicates_for_generics( + |idx, predicate_span| { + traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) + }, + param_env, bounds, ) { + if remap { + obligation = obligation.without_const(self.tcx); + } self.register_predicate(obligation); } } @@ -1438,7 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } else { if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + self.err_ctxt() + .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) .emit(); } let err = self.tcx.ty_error(); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs index 7602f2550..fc83994ca 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs @@ -130,14 +130,17 @@ impl<'tcx> ArgMatrix<'tcx> { let ai = &self.expected_indices; let ii = &self.provided_indices; + // Issue: 100478, when we end the iteration, + // `next_unmatched_idx` will point to the index of the first unmatched + let mut next_unmatched_idx = 0; for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over inputs are considered missing + // If we eliminate the last row, any left-over arguments are considered missing if i >= mat.len() { - return Some(Issue::Missing(i)); + return Some(Issue::Missing(next_unmatched_idx)); } - // If we eliminate the last column, any left-over arguments are extra + // If we eliminate the last column, any left-over inputs are extra if mat[i].len() == 0 { - return Some(Issue::Extra(i)); + return Some(Issue::Extra(next_unmatched_idx)); } // Make sure we don't pass the bounds of our matrix @@ -145,6 +148,7 @@ impl<'tcx> ArgMatrix<'tcx> { let is_input = i < ii.len(); if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { // This is a satisfied input, so move along + next_unmatched_idx += 1; continue; } @@ -163,7 +167,7 @@ impl<'tcx> ArgMatrix<'tcx> { if is_input { for j in 0..ai.len() { // If we find at least one argument that could satisfy this input - // this argument isn't useless + // this input isn't useless if matches!(mat[i][j], Compatibility::Compatible) { useless = false; break; @@ -232,8 +236,8 @@ impl<'tcx> ArgMatrix<'tcx> { if matches!(c, Compatibility::Compatible) { Some(i) } else { None } }) .collect(); - if compat.len() != 1 { - // this could go into multiple slots, don't bother exploring both + if compat.len() < 1 { + // try to find a cycle even when this could go into multiple slots, see #101097 is_cycle = false; break; } @@ -309,7 +313,8 @@ impl<'tcx> ArgMatrix<'tcx> { } while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - match self.find_issue() { + let res = self.find_issue(); + match res { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); let input_idx = self.provided_indices[idx]; @@ -364,7 +369,9 @@ impl<'tcx> ArgMatrix<'tcx> { None => { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs - for (inp, arg) in self.eliminate_satisfied() { + let eliminated = self.eliminate_satisfied(); + assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); + for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs new file mode 100644 index 000000000..8e0fcb56c --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -0,0 +1,2236 @@ +use crate::coercion::CoerceMany; +use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; +use crate::gather_locals::Declaration; +use crate::method::MethodCallee; +use crate::Expectation::*; +use crate::TupleArgumentsFlag::*; +use crate::{ + struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, + TupleArgumentsFlag, +}; +use rustc_ast as ast; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ExprKind, Node, QPath}; +use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; +use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::structured_errors::StructuredDiagnostic; +use rustc_index::vec::IndexVec; +use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferOk; +use rustc_infer::infer::TypeTrace; +use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; +use rustc_session::Session; +use rustc_span::symbol::Ident; +use rustc_span::{self, sym, Span}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; + +use std::iter; +use std::mem; +use std::ops::ControlFlow; +use std::slice; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(in super::super) fn check_casts(&mut self) { + // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors + // when writing to `self.param_env`. + let mut deferred_cast_checks = mem::take(&mut *self.deferred_cast_checks.borrow_mut()); + + debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); + for cast in deferred_cast_checks.drain(..) { + let prev_env = self.param_env; + self.param_env = self.param_env.with_constness(cast.constness); + + cast.check(self); + + self.param_env = prev_env; + } + + *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; + } + + pub(in super::super) fn check_transmutes(&self) { + let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); + debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); + for (from, to, hir_id) in deferred_transmute_checks.drain(..) { + self.check_transmute(from, to, hir_id); + } + } + + pub(in super::super) fn check_asms(&self) { + let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut(); + debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); + for (asm, hir_id) in deferred_asm_checks.drain(..) { + let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); + let get_operand_ty = |expr| { + 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) + } + }; + InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty) + .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); + } + } + + pub(in super::super) fn check_method_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + method: Result<MethodCallee<'tcx>, ()>, + args_no_rcvr: &'tcx [hir::Expr<'tcx>], + tuple_arguments: TupleArgumentsFlag, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let has_error = match method { + Ok(method) => method.substs.references_error() || method.sig.references_error(), + Err(_) => true, + }; + if has_error { + let err_inputs = self.err_args(args_no_rcvr.len()); + + let err_inputs = match tuple_arguments { + DontTupleArguments => err_inputs, + TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], + }; + + self.check_argument_types( + sp, + expr, + &err_inputs, + None, + args_no_rcvr, + false, + tuple_arguments, + method.ok().map(|method| method.def_id), + ); + return self.tcx.ty_error(); + } + + let method = method.unwrap(); + // HACK(eddyb) ignore self in the definition (see above). + let expected_input_tys = self.expected_inputs_for_expected_output( + sp, + expected, + method.sig.output(), + &method.sig.inputs()[1..], + ); + self.check_argument_types( + sp, + expr, + &method.sig.inputs()[1..], + expected_input_tys, + args_no_rcvr, + method.sig.c_variadic, + tuple_arguments, + Some(method.def_id), + ); + method.sig.output() + } + + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(in super::super) fn check_argument_types( + &self, + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + formal_input_tys: &[Ty<'tcx>], + // More specific expected types, after unifying with caller output types + expected_input_tys: Option<Vec<Ty<'tcx>>>, + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // Whether the function is variadic, for example when imported from C + c_variadic: bool, + // Whether the arguments have been bundled in a tuple (ex: closures) + tuple_arguments: TupleArgumentsFlag, + // The DefId for the function being called, for better error messages + fn_def_id: Option<DefId>, + ) { + let tcx = self.tcx; + + // Conceptually, we've got some number of expected inputs, and some number of provided arguments + // and we can form a grid of whether each argument could satisfy a given input: + // in1 | in2 | in3 | ... + // arg1 ? | | | + // arg2 | ? | | + // arg3 | | ? | + // ... + // Initially, we just check the diagonal, because in the case of correct code + // these are the only checks that matter + // However, in the unhappy path, we'll fill in this whole grid to attempt to provide + // better error messages about invalid method calls. + + // All the input types from the fn signature must outlive the call + // so as to validate implied bounds. + for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) { + self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); + } + + let mut err_code = "E0061"; + + // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here + let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { + let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); + match tuple_type.kind() { + // We expected a tuple and got a tuple + ty::Tuple(arg_types) => { + // Argument length differs + if arg_types.len() != provided_args.len() { + err_code = "E0057"; + } + let expected_input_tys = match expected_input_tys { + Some(expected_input_tys) => match expected_input_tys.get(0) { + Some(ty) => match ty.kind() { + ty::Tuple(tys) => Some(tys.iter().collect()), + _ => None, + }, + None => None, + }, + None => None, + }; + (arg_types.iter().collect(), expected_input_tys) + } + _ => { + // Otherwise, there's a mismatch, so clear out what we're expecting, and set + // our input types to err_args so we don't blow up the error messages + struct_span_err!( + tcx.sess, + call_span, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + (self.err_args(provided_args.len()), None) + } + } + } else { + (formal_input_tys.to_vec(), expected_input_tys) + }; + + // If there are no external expectations at the call site, just use the types from the function defn + let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { + assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + expected_input_tys + } else { + formal_input_tys.clone() + }; + + let minimum_input_count = expected_input_tys.len(); + let provided_arg_count = provided_args.len(); + + let is_const_eval_select = matches!(fn_def_id, Some(def_id) if + self.tcx.def_kind(def_id) == hir::def::DefKind::Fn + && self.tcx.is_intrinsic(def_id) + && self.tcx.item_name(def_id) == sym::const_eval_select); + + // We introduce a helper function to demand that a given argument satisfy a given input + // This is more complicated than just checking type equality, as arguments could be coerced + // This version writes those types back so further type checking uses the narrowed types + let demand_compatible = |idx| { + let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; + let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; + let provided_arg = &provided_args[idx]; + + debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); + + // We're on the happy path here, so we'll do a more involved check and write back types + // To check compatibility, we'll do 3 things: + // 1. Unify the provided argument with the expected type + let expectation = Expectation::rvalue_hint(self, expected_input_ty); + + let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); + + // Cause selection errors caused by resolving a single argument to point at the + // argument and not the call. This lets us customize the span pointed to in the + // fulfillment error to be more accurate. + let coerced_ty = self.resolve_vars_with_obligations(coerced_ty); + + let coerce_error = self + .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None) + .err(); + + if coerce_error.is_some() { + return Compatibility::Incompatible(coerce_error); + } + + // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that + // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed + // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here. + // + // This check is here because there is currently no way to express a trait bound for `FnDef` types only. + if is_const_eval_select && (1..=2).contains(&idx) { + if let ty::FnDef(def_id, _) = checked_ty.kind() { + if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a `const fn`") + .help("consult the documentation on `const_eval_select` for more information") + .emit(); + } + } else { + self.tcx + .sess + .struct_span_err(provided_arg.span, "this argument must be a function item") + .note(format!("expected a function item, found {checked_ty}")) + .help( + "consult the documentation on `const_eval_select` for more information", + ) + .emit(); + } + } + + // 3. Check if the formal type is a supertype of the checked one + // and register any such obligations for future type checks + let supertype_error = self + .at(&self.misc(provided_arg.span), self.param_env) + .sup(formal_input_ty, coerced_ty); + let subtyping_error = match supertype_error { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations); + None + } + Err(err) => Some(err), + }; + + // If neither check failed, the types are compatible + match subtyping_error { + None => Compatibility::Compatible, + Some(_) => Compatibility::Incompatible(subtyping_error), + } + }; + + // To start, we only care "along the diagonal", where we expect every + // provided arg to be in the right spot + let mut compatibility_diagonal = + vec![Compatibility::Incompatible(None); provided_args.len()]; + + // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path + // if the wrong number of arguments were supplied, we CAN'T be satisfied, + // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function + // otherwise, they need to be identical, because rust doesn't currently support variadic functions + let mut call_appears_satisfied = if c_variadic { + provided_arg_count >= minimum_input_count + } else { + provided_arg_count == minimum_input_count + }; + + // Check the arguments. + // We do this in a pretty awful way: first we type-check any arguments + // that are not closures, then we type-check the closures. This is so + // that we have more information about the types of arguments when we + // type-check the functions. This isn't really the right way to do this. + for check_closures in [false, true] { + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.select_obligations_where_possible(false, |_| {}) + } + + // Check each argument, to satisfy the input it was provided for + // Visually, we're traveling down the diagonal of the compatibility matrix + for (idx, arg) in provided_args.iter().enumerate() { + // Warn only for the first loop (the "no closures" one). + // Closure arguments themselves can't be diverging, but + // a previous argument can, e.g., `foo(panic!(), || {})`. + if !check_closures { + self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); + } + + // For C-variadic functions, we don't have a declared type for all of + // the arguments hence we only do our usual type checking with + // the arguments who's types we do know. However, we *can* check + // for unreachable expressions (see above). + // FIXME: unreachable warning current isn't emitted + if idx >= minimum_input_count { + continue; + } + + let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); + if is_closure != check_closures { + continue; + } + + let compatible = demand_compatible(idx); + let is_compatible = matches!(compatible, Compatibility::Compatible); + compatibility_diagonal[idx] = compatible; + + if !is_compatible { + call_appears_satisfied = false; + } + } + } + + if c_variadic && provided_arg_count < minimum_input_count { + err_code = "E0060"; + } + + for arg in provided_args.iter().skip(minimum_input_count) { + // Make sure we've checked this expr at least once. + let arg_ty = self.check_expr(&arg); + + // If the function is c-style variadic, we skipped a bunch of arguments + // so we need to check those, and write out the types + // Ideally this would be folded into the above, for uniform style + // but c-variadic is already a corner case + if c_variadic { + fn variadic_error<'tcx>( + sess: &'tcx Session, + span: Span, + ty: Ty<'tcx>, + cast_ty: &str, + ) { + use rustc_hir_analysis::structured_errors::MissingCastForVariadicArg; + + MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); + } + + // There are a few types which get autopromoted when passed via varargs + // in C but we just error out instead and require explicit casts. + let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); + match arg_ty.kind() { + ty::Float(ty::FloatTy::F32) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); + } + ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); + } + ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); + } + ty::FnDef(..) => { + let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); + let ptr_ty = self.resolve_vars_if_possible(ptr_ty); + variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + } + _ => {} + } + } + } + + if !call_appears_satisfied { + let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); + let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { + minimum_input_count + } else { + provided_arg_count + })); + debug_assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "expected formal_input_tys to be the same size as expected_input_tys" + ); + let formal_and_expected_inputs = IndexVec::from_iter( + formal_input_tys + .iter() + .copied() + .zip(expected_input_tys.iter().copied()) + .map(|vars| self.resolve_vars_if_possible(vars)), + ); + + self.report_arg_errors( + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + ); + } + } + + fn report_arg_errors( + &self, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, + c_variadic: bool, + err_code: &str, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &hir::Expr<'tcx>, + ) { + // Next, let's construct the error + let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind { + hir::ExprKind::Call( + hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, + _, + ) => { + if let Res::Def(DefKind::Ctor(of, _), _) = + self.typeck_results.borrow().qpath_res(qpath, *hir_id) + { + (call_span, *span, Some(of), false) + } else { + (call_span, *span, None, false) + } + } + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false), + hir::ExprKind::MethodCall(path_segment, _, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) + } else { + ident_span + }; + // methods are never ctors + (*span, ident_span, None, true) + } + k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), + }; + let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); + let call_name = match ctor_of { + Some(CtorOf::Struct) => "struct", + Some(CtorOf::Variant) => "enum variant", + None => "function", + }; + + // Don't print if it has error types or is just plain `_` + fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { + tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) + } + + self.set_tainted_by_errors(); + let tcx = self.tcx; + + // Get the argument span in the context of the call span so that + // suggestions and labels are (more) correct when an arg is a + // macro invocation. + let normalize_span = |span: Span| -> Span { + let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(error_span) { span } else { normalized_span } + }; + + // Precompute the provided types and spans, since that's all we typically need for below + let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args + .iter() + .map(|expr| { + let ty = self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(*expr) + .unwrap_or_else(|| tcx.ty_error()); + (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) + }) + .collect(); + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, receiver, ..) => { + if let Some((DefKind::AssocFn, def_id)) = + self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.fn_has_self_parameter + { + Some(*receiver) + } else { + None + } + } + _ => None, + }; + let callee_ty = callee_expr + .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); + + // A "softer" version of the `demand_compatible`, which checks types without persisting them, + // and treats error types differently + // This will allow us to "probe" for other argument orders that would likely have been correct + let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { + if provided_idx.as_usize() == expected_idx.as_usize() { + return compatibility_diagonal[provided_idx].clone(); + } + + let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; + // If either is an error type, we defy the usual convention and consider them to *not* be + // coercible. This prevents our error message heuristic from trying to pass errors into + // every argument. + if (formal_input_ty, expected_input_ty).references_error() { + return Compatibility::Incompatible(None); + } + + let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; + + let expectation = Expectation::rvalue_hint(self, expected_input_ty); + let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); + let can_coerce = self.can_coerce(arg_ty, coerced_ty); + if !can_coerce { + return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( + ty::error::ExpectedFound::new(true, coerced_ty, arg_ty), + ))); + } + + // Using probe here, since we don't want this subtyping to affect inference. + let subtyping_error = self.probe(|_| { + self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err() + }); + + // Same as above: if either the coerce type or the checked type is an error type, + // consider them *not* compatible. + let references_error = (coerced_ty, arg_ty).references_error(); + match (references_error, subtyping_error) { + (false, None) => Compatibility::Compatible, + (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), + } + }; + + // 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 + // - An input is missing, which isn't satisfied by *any* of the other arguments + // - Some number of arguments have been provided in the wrong order + // - A type is straight up invalid + + // First, let's find the errors + let (mut errors, matched_inputs) = + ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) + .find_errors(); + + // First, check if we just need to wrap some arguments in a tuple. + if let Some((mismatch_idx, terr)) = + compatibility_diagonal.iter().enumerate().find_map(|(i, c)| { + if let Compatibility::Incompatible(Some(terr)) = c { + Some((i, *terr)) + } else { + None + } + }) + { + // Is the first bad expected argument a tuple? + // Do we have as many extra provided arguments as the tuple's length? + // If so, we might have just forgotten to wrap some args in a tuple. + if let Some(ty::Tuple(tys)) = + formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() + && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() + { + // Wrap up the N provided arguments starting at this position in a tuple. + let provided_as_tuple = tcx.mk_tup( + provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()), + ); + + let mut satisfied = true; + // Check if the newly wrapped tuple + rest of the arguments are compatible. + for ((_, expected_ty), provided_ty) in std::iter::zip( + formal_and_expected_inputs.iter().skip(mismatch_idx), + [provided_as_tuple].into_iter().chain( + provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()), + ), + ) { + if !self.can_coerce(provided_ty, *expected_ty) { + satisfied = false; + break; + } + } + + // If they're compatible, suggest wrapping in an arg, and we're done! + // Take some care with spans, so we don't suggest wrapping a macro's + // innards in parenthesis, for example. + if satisfied + && let Some((_, lo)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx)) + && let Some((_, hi)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1)) + { + let mut err; + if tys.len() == 1 { + // 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, + provided_arg_tys[mismatch_idx.into()].0, + ), + terr, + ); + err.span_label( + full_call_span, + format!("arguments to this {} are incorrect", call_name), + ); + } else { + err = tcx.sess.struct_span_err_with_code( + full_call_span, + &format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count( + formal_and_expected_inputs.len(), + "argument" + ), + potentially_plural_count(provided_args.len(), "argument"), + pluralize!("was", provided_args.len()) + ), + DiagnosticId::Error(err_code.to_owned()), + ); + err.multipart_suggestion_verbose( + "wrap these arguments in parentheses to construct a tuple", + vec![ + (lo.shrink_to_lo(), "(".to_string()), + (hi.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + }; + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + Some(mismatch_idx), + is_method, + ); + err.emit(); + return; + } + } + } + + // Okay, so here's where it gets complicated in regards to what errors + // we emit and how. + // There are 3 different "types" of errors we might encounter. + // 1) Missing/extra/swapped arguments + // 2) Valid but incorrect arguments + // 3) Invalid arguments + // - Currently I think this only comes up with `CyclicTy` + // + // We first need to go through, remove those from (3) and emit those + // as their own error, particularly since they're error code and + // message is special. From what I can tell, we *must* emit these + // here (vs somewhere prior to this function) since the arguments + // become invalid *because* of how they get used in the function. + // It is what it is. + + if errors.is_empty() { + if cfg!(debug_assertions) { + span_bug!(error_span, "expected errors from argument matrix"); + } else { + tcx.sess + .struct_span_err( + error_span, + "argument type mismatch was detected, \ + but rustc had trouble determining where", + ) + .note( + "we would appreciate a bug report: \ + https://github.com/rust-lang/rust/issues/new", + ) + .emit(); + } + return; + } + + 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); + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { + self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); + return true; + } + false + }); + + // We're done if we found errors, but we already emitted them. + if errors.is_empty() { + return; + } + + // Okay, now that we've emitted the special errors separately, we + // are only left missing/extra/swapped and mismatched arguments, both + // can be collated pretty easily if needed. + + // Next special case: if there is only one "Incompatible" error, just emit that + if let [ + Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), + ] = &errors[..] + { + 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 mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); + self.emit_coerce_suggestions( + &mut err, + &provided_args[*provided_idx], + provided_ty, + Expectation::rvalue_hint(self, expected_ty) + .only_has_type(self) + .unwrap_or(formal_ty), + None, + None, + ); + err.span_label( + full_call_span, + format!("arguments to this {} are incorrect", call_name), + ); + // Call out where the function is defined + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + Some(expected_idx.as_usize()), + is_method, + ); + err.emit(); + return; + } + + let mut err = if formal_and_expected_inputs.len() == provided_args.len() { + struct_span_err!( + tcx.sess, + full_call_span, + E0308, + "arguments to this {} are incorrect", + call_name, + ) + } else { + tcx.sess.struct_span_err_with_code( + full_call_span, + &format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count(formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(provided_args.len(), "argument"), + pluralize!("was", provided_args.len()) + ), + DiagnosticId::Error(err_code.to_owned()), + ) + }; + + // As we encounter issues, keep track of what we want to provide for the suggestion + let mut labels = vec![]; + // If there is a single error, we give a specific suggestion; otherwise, we change to + // "did you mean" with the suggested function call + enum SuggestionText { + None, + Provide(bool), + Remove(bool), + Swap, + Reorder, + DidYouMean, + } + let mut suggestion_text = SuggestionText::None; + + let mut errors = errors.into_iter().peekable(); + while let Some(error) = errors.next() { + match error { + Error::Invalid(provided_idx, expected_idx, compatibility) => { + 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); + if let Some(e) = error { + self.err_ctxt().note_type_err( + &mut err, + &trace.cause, + None, + Some(trace.values), + e, + false, + true, + ); + } + } + + self.emit_coerce_suggestions( + &mut err, + &provided_args[provided_idx], + provided_ty, + Expectation::rvalue_hint(self, expected_ty) + .only_has_type(self) + .unwrap_or(formal_ty), + None, + None, + ); + } + Error::Extra(arg_idx) => { + let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + // FIXME: not suggestable, use something else + format!(" of type `{}`", provided_ty) + } else { + "".to_string() + }; + labels + .push((provided_span, format!("argument{} unexpected", provided_ty_name))); + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + } + Error::Missing(expected_idx) => { + // If there are multiple missing arguments adjacent to each other, + // then we can provide a single error. + + let mut missing_idxs = vec![expected_idx]; + while let Some(e) = errors.next_if(|e| { + matches!(e, Error::Missing(next_expected_idx) + if *next_expected_idx == *missing_idxs.last().unwrap() + 1) + }) { + match e { + Error::Missing(expected_idx) => missing_idxs.push(expected_idx), + _ => unreachable!(), + } + } + + // NOTE: Because we might be re-arranging arguments, might have extra + // arguments, etc. it's hard to *really* know where we should provide + // this error label, so as a heuristic, we point to the provided arg, or + // to the call if the missing inputs pass the provided args. + match &missing_idxs[..] { + &[expected_idx] => { + let (_, input_ty) = formal_and_expected_inputs[expected_idx]; + let span = if let Some((_, arg_span)) = + provided_arg_tys.get(expected_idx.to_provided_idx()) + { + *arg_span + } else { + args_span + }; + let rendered = if !has_error_or_infer([input_ty]) { + format!(" of type `{}`", input_ty) + } else { + "".to_string() + }; + labels.push((span, format!("an argument{} is missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Provide(false), + SuggestionText::Provide(_) => SuggestionText::Provide(true), + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx] => { + let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; + let span = if let (Some((_, first_span)), Some((_, second_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(second_idx.to_provided_idx()), + ) { + first_span.to(*second_span) + } else { + args_span + }; + let rendered = + if !has_error_or_infer([first_expected_ty, second_expected_ty]) { + format!( + " of type `{}` and `{}`", + first_expected_ty, second_expected_ty + ) + } else { + "".to_string() + }; + labels.push((span, format!("two arguments{} are missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx, third_idx] => { + let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; + let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; + let span = if let (Some((_, first_span)), Some((_, third_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(third_idx.to_provided_idx()), + ) { + first_span.to(*third_span) + } else { + args_span + }; + let rendered = if !has_error_or_infer([ + first_expected_ty, + second_expected_ty, + third_expected_ty, + ]) { + format!( + " of type `{}`, `{}`, and `{}`", + first_expected_ty, second_expected_ty, third_expected_ty + ) + } else { + "".to_string() + }; + labels.push((span, format!("three arguments{} are missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + missing_idxs => { + let first_idx = *missing_idxs.first().unwrap(); + let last_idx = *missing_idxs.last().unwrap(); + // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. + // It's hard to *really* know where we should provide this error label, so this is a + // decent heuristic + let span = if let (Some((_, first_span)), Some((_, last_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(last_idx.to_provided_idx()), + ) { + first_span.to(*last_span) + } else { + args_span + }; + labels.push((span, format!("multiple arguments are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + } + } + Error::Swap( + first_provided_idx, + second_provided_idx, + first_expected_idx, + second_expected_idx, + ) => { + let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; + let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; + let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { + format!(", found `{}`", first_provided_ty) + } else { + String::new() + }; + labels.push(( + first_span, + format!("expected `{}`{}", first_expected_ty, first_provided_ty_name), + )); + + let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; + let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { + format!(", found `{}`", second_provided_ty) + } else { + String::new() + }; + labels.push(( + second_span, + format!("expected `{}`{}", second_expected_ty, second_provided_ty_name), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Swap, + _ => SuggestionText::DidYouMean, + }; + } + Error::Permutation(args) => { + for (dst_arg, dest_input) in args { + let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; + let (provided_ty, provided_span) = provided_arg_tys[dest_input]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + format!(", found `{}`", provided_ty) + } else { + String::new() + }; + labels.push(( + provided_span, + format!("expected `{}`{}", expected_ty, provided_ty_name), + )); + } + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Reorder, + _ => SuggestionText::DidYouMean, + }; + } + } + } + + // If we have less than 5 things to say, it would be useful to call out exactly what's wrong + if labels.len() <= 5 { + for (span, label) in labels { + err.span_label(span, label); + } + } + + // Call out where the function is defined + self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method); + + // And add a suggestion block for all of the parameters + let suggestion_text = match suggestion_text { + SuggestionText::None => None, + SuggestionText::Provide(plural) => { + Some(format!("provide the argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Remove(plural) => { + Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Swap => Some("swap these arguments".to_string()), + SuggestionText::Reorder => Some("reorder these arguments".to_string()), + SuggestionText::DidYouMean => Some("did you mean".to_string()), + }; + if let Some(suggestion_text) = suggestion_text { + let source_map = self.sess().source_map(); + let (mut suggestion, suggestion_span) = + if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) { + ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi())) + } else { + ( + format!( + "{}(", + source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| { + fn_def_id.map_or("".to_string(), |fn_def_id| { + tcx.item_name(fn_def_id).to_string() + }) + }) + ), + error_span, + ) + }; + let mut needs_comma = false; + for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { + if needs_comma { + suggestion += ", "; + } else { + needs_comma = true; + } + let suggestion_text = if let Some(provided_idx) = provided_idx + && let (_, provided_span) = provided_arg_tys[*provided_idx] + && let Ok(arg_text) = source_map.span_to_snippet(provided_span) + { + arg_text + } else { + // Propose a placeholder of the correct type + let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; + if expected_ty.is_unit() { + "()".to_string() + } else if expected_ty.is_suggestable(tcx, false) { + format!("/* {} */", expected_ty) + } else { + "/* value */".to_string() + } + }; + suggestion += &suggestion_text; + } + suggestion += ")"; + err.span_suggestion_verbose( + suggestion_span, + &suggestion_text, + suggestion, + Applicability::HasPlaceholders, + ); + } + + err.emit(); + } + + // AST fragment checking + pub(in super::super) fn check_lit( + &self, + lit: &hir::Lit, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + + match lit.node { + ast::LitKind::Str(..) => tcx.mk_static_str(), + ast::LitKind::ByteStr(ref v) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) + } + ast::LitKind::Byte(_) => tcx.types.u8, + ast::LitKind::Char(_) => tcx.types.char, + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)), + ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Int(_) | ty::Uint(_) => Some(ty), + ty::Char => Some(tcx.types.u8), + ty::RawPtr(..) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_int_var()) + } + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => { + tcx.mk_mach_float(ty::float_ty(t)) + } + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Float(_) => Some(ty), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_float_var()) + } + ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::Err => tcx.ty_error(), + } + } + + pub fn check_struct_path( + &self, + qpath: &QPath<'_>, + hir_id: hir::HirId, + ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + let path_span = qpath.span(); + let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let variant = match def { + Res::Err => { + self.set_tainted_by_errors(); + return None; + } + Res::Def(DefKind::Variant, _) => match ty.kind() { + ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)), + _ => bug!("unexpected type: {:?}", ty), + }, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } => match ty.kind() { + ty::Adt(adt, substs) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did(), substs)) + } + _ => None, + }, + _ => bug!("unexpected definition: {:?}", def), + }; + + if let Some((variant, did, substs)) = variant { + debug!("check_struct_path: did={:?} substs={:?}", did, substs); + self.write_user_type_annotation_from_substs(hir_id, did, substs, None); + + // Check bounds on type arguments used in the path. + self.add_required_obligations_for_hir(path_span, did, substs, hir_id); + + Some((variant, ty)) + } else { + match ty.kind() { + ty::Error(_) => { + // E0071 might be caused by a spelling error, which will have + // already caused an error message and probably a suggestion + // elsewhere. Refrain from emitting more unhelpful errors here + // (issue #88844). + } + _ => { + struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(); + } + } + None + } + } + + pub fn check_decl_initializer( + &self, + hir_id: hir::HirId, + pat: &'tcx hir::Pat<'tcx>, + init: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed + // for #42640 (default match binding modes). + // + // See #44848. + let ref_bindings = pat.contains_explicit_ref_binding(); + + let local_ty = self.local_ty(init.span, hir_id).revealed_ty; + if let Some(m) = ref_bindings { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the place it is referencing, and not some + // supertype thereof. + let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + self.demand_eqtype(init.span, local_ty, init_ty); + init_ty + } else { + self.check_expr_coercable_to_type(init, local_ty, None) + } + } + + pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) { + // Determine and write the type which we'll check the pattern against. + let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty; + self.write_ty(decl.hir_id, decl_ty); + + // Type check the initializer. + if let Some(ref init) = decl.init { + let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init); + self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty); + } + + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (decl.ty, decl.init) { + (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => { + (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + } // No explicit type; so use the scrutinee. + _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + }; + + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); + let pat_ty = self.node_ty(decl.pat.hir_id); + self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); + + if let Some(blk) = decl.els { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_block_with_expected(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Some(mut err) = + self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); + } + self.diverges.set(previous_diverges); + } + } + + /// Type check a `let` statement. + pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + self.check_decl(local.into()); + } + + pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) { + // Don't do all the complex logic below for `DeclItem`. + match stmt.kind { + hir::StmtKind::Item(..) => return, + hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + } + + self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); + + // 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) => { + self.check_decl_local(l); + } + // Ignore for now. + hir::StmtKind::Item(_) => {} + hir::StmtKind::Expr(ref expr) => { + // Check with expected type of `()`. + self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { + if expr.can_have_side_effects() { + self.suggest_semicolon_at_end(expr.span, err); + } + }); + } + hir::StmtKind::Semi(ref expr) => { + // All of this is equivalent to calling `check_expr`, but it is inlined out here + // in order to capture the fact that this `match` is the last statement in its + // function. This is done for better suggestions to remove the `;`. + let expectation = match expr.kind { + hir::ExprKind::Match(..) if is_last => IsLast(stmt.span), + _ => NoExpectation, + }; + self.check_expr_with_expectation(expr, expectation); + } + } + + // 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>) { + let unit = self.tcx.mk_unit(); + let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); + } + } + + pub(in super::super) fn check_block_with_expected( + &self, + 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 + // a `try { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let tail_expr = blk.expr.as_ref(); + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + let tail_expr: &[&hir::Expr<'_>] = match tail_expr { + Some(e) => slice::from_ref(e), + None => &[], + }; + CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + }; + + let prev_diverges = self.diverges.get(); + let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; + + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for (pos, s) in blk.stmts.iter().enumerate() { + self.check_stmt(s, blk.stmts.len() - 1 == pos); + } + + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(blk.hir_id); + let coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + let span = self.get_expr_coercion_span(tail_expr); + let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); + let ty_for_diagnostic = coerce.merged_ty(); + // We use coerce_inner here because we want to augment the error + // suggesting to wrap the block in square brackets if it might've + // been mistaken array syntax + coerce.coerce_inner( + self, + &cause, + Some(tail_expr), + tail_expr_ty, + Some(&mut |diag: &mut Diagnostic| { + self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic); + }), + false, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.get().is_always() { + // #50009 -- Do not point at the entire fn block span, point at the return type + // span, as it is the cause of the requirement, and + // `consider_hint_about_removing_semicolon` will point at the last expression + // if it were a relevant part of the error. This improves usability in editors + // that highlight errors inline. + let mut sp = blk.span; + let mut fn_span = None; + if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + let ret_sp = decl.output.span(); + if let Some(block_sp) = self.parent_item_span(blk.hir_id) { + // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the + // output would otherwise be incorrect and even misleading. Make sure + // the span we're aiming at correspond to a `fn` body. + if block_sp == blk.span { + sp = ret_sp; + fn_span = Some(ident.span); + } + } + } + coerce.coerce_forced_unit( + self, + &self.misc(sp), + &mut |err| { + if let Some(expected_ty) = expected.only_has_type(self) { + if !self.consider_removing_semicolon(blk, expected_ty, err) { + self.err_ctxt().consider_returning_binding( + blk, + expected_ty, + err, + ); + } + if expected_ty == self.tcx.types.bool { + // If this is caused by a missing `let` in a `while let`, + // silence this redundant error, as we already emit E0070. + + // Our block must be a `assign desugar local; assignment` + if let Some(hir::Node::Block(hir::Block { + stmts: + [ + hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { + source: + hir::LocalSource::AssignDesugar(_), + .. + }), + .. + }, + hir::Stmt { + kind: + hir::StmtKind::Expr(hir::Expr { + kind: hir::ExprKind::Assign(..), + .. + }), + .. + }, + ], + .. + })) = self.tcx.hir().find(blk.hir_id) + { + self.comes_from_while_condition(blk.hir_id, |_| { + err.downgrade_to_delayed_bug(); + }) + } + } + } + if let Some(fn_span) = fn_span { + err.span_label( + fn_span, + "implicitly returns `()` as its body has no tail or `return` \ + expression", + ); + } + }, + false, + ); + } + } + }); + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + 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() + } + + self.write_ty(blk.hir_id, ty); + + self.ps.set(prev); + ty + } + + fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { + let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id).def_id); + match node { + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { + let body = self.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = &body.value.kind { + return Some(block.span); + } + } + _ => {} + } + None + } + + /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. + fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { + let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); + self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + } + + /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail + /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors + /// when given code like the following: + /// ```text + /// if false { return 0i32; } else { 1u32 } + /// // ^^^^ point at this instead of the whole `if` expression + /// ``` + fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { + let check_in_progress = |elem: &hir::Expr<'_>| { + self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( + |_| match elem.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), + _ => elem.span, + }, + ) + }; + + if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { + if let Some(rslt) = check_in_progress(el) { + return rslt; + } + } + + if let hir::ExprKind::Match(_, arms, _) = expr.kind { + let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); + if let Some(span) = iter.next() { + if iter.next().is_none() { + return span; + } + } + } + + expr.span + } + + fn overwrite_local_ty_if_err( + &self, + hir_id: hir::HirId, + pat: &'tcx hir::Pat<'tcx>, + decl_ty: Ty<'tcx>, + ty: Ty<'tcx>, + ) { + if ty.references_error() { + // Override the types everywhere with `err()` to avoid knock on errors. + self.write_ty(hir_id, ty); + self.write_ty(pat.hir_id, ty); + let local_ty = LocalTy { decl_ty, revealed_ty: ty }; + self.locals.borrow_mut().insert(hir_id, local_ty); + self.locals.borrow_mut().insert(pat.hir_id, local_ty); + } + } + + // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. + // The newly resolved definition is written into `type_dependent_defs`. + fn finish_resolving_struct_path( + &self, + qpath: &QPath<'_>, + path_span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + match *qpath { + QPath::Resolved(ref maybe_qself, ref path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); + let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true); + (path.res, ty) + } + QPath::TypeRelative(ref qself, ref segment) => { + let ty = self.to_ty(qself); + + let result = <dyn AstConv<'_>>::associated_path_to_ty( + self, hir_id, path_span, ty, qself, segment, true, + ); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let result = result.map(|(_, kind, def_id)| (kind, def_id)); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + + (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) + } + QPath::LangItem(lang_item, span, id) => { + self.resolve_lang_item_path(lang_item, span, hir_id, id) + } + } + } + + /// Given a vector of fulfillment errors, try to adjust the spans of the + /// errors to more accurately point at the cause of the failure. + /// + /// This applies to calls, methods, and struct expressions. This will also + /// try to deduplicate errors that are due to the same cause but might + /// have been created with different [`ObligationCause`][traits::ObligationCause]s. + pub(super) fn adjust_fulfillment_errors_for_expr_obligation( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + ) { + // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that + // other errors that have the same span and predicate can also get fixed, + // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. + // This is important since if we adjust one span but not the other, then + // we will have "duplicated" the error on the UI side. + let mut remap_cause = FxHashSet::default(); + let mut not_adjusted = vec![]; + + for error in errors { + let before_span = error.obligation.cause.span; + if self.adjust_fulfillment_error_for_expr_obligation(error) + || before_span != error.obligation.cause.span + { + // Store both the predicate and the predicate *without constness* + // since sometimes we instantiate and check both of these in a + // method call, for example. + remap_cause.insert(( + before_span, + error.obligation.predicate, + error.obligation.cause.clone(), + )); + remap_cause.insert(( + before_span, + error.obligation.predicate.without_const(self.tcx), + error.obligation.cause.clone(), + )); + } else { + // If it failed to be adjusted once around, it may be adjusted + // via the "remap cause" mapping the second time... + not_adjusted.push(error); + } + } + + for error in not_adjusted { + for (span, predicate, cause) in &remap_cause { + if *predicate == error.obligation.predicate + && span.contains(error.obligation.cause.span) + { + error.obligation.cause = cause.clone(); + continue; + } + } + } + } + + fn adjust_fulfillment_error_for_expr_obligation( + &self, + error: &mut traits::FulfillmentError<'tcx>, + ) -> bool { + let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx)) + = *error.obligation.cause.code().peel_derives() else { return false; }; + 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::List::empty(), + }; + + let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { + predicate_substs.types().find_map(|ty| { + ty.walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + && matches(param_ty) + { + Some(arg) + } else { + None + } + }) + }) + }; + + // Prefer generics that are local to the fn item, since these are likely + // to be the cause of the unsatisfied predicate. + let mut param_to_point_at = find_param_matching(&|param_ty| { + self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id + }); + // Fall back to generic that isn't local to the fn item. This will come + // from a trait or impl, for example. + let mut fallback_param_to_point_at = find_param_matching(&|param_ty| { + self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id + && param_ty.name != rustc_span::symbol::kw::SelfUpper + }); + // Finally, the `Self` parameter is possibly the reason that the predicate + // is unsatisfied. This is less likely to be true for methods, because + // method probe means that we already kinda check that the predicates due + // to the `Self` type are true. + let mut self_param_to_point_at = + find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper); + + // Finally, for ambiguity-related errors, we actually want to look + // for a parameter that is the source of the inference type left + // over in this predicate. + if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code { + fallback_param_to_point_at = None; + self_param_to_point_at = None; + param_to_point_at = + self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); + } + + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + + match &expr.kind { + hir::ExprKind::Path(qpath) => { + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), + hir_id: call_hir_id, + span: call_span, + .. + }) = hir.get(hir.get_parent_node(expr.hir_id)) + && callee.hir_id == expr.hir_id + { + if self.closure_span_overlaps_error(error, *call_span) { + return false; + } + + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_arg_if_possible( + error, + def_id, + param, + *call_hir_id, + callee.span, + None, + args, + ) + { + return true; + } + } + } + // Notably, we only point to params that are local to the + // item we're checking, since those are the ones we are able + // to look in the final `hir::PathSegment` for. Everything else + // would require a deeper search into the `qpath` than I think + // is worthwhile. + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; + } + } + hir::ExprKind::MethodCall(segment, receiver, args, ..) => { + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_arg_if_possible( + error, + def_id, + param, + hir_id, + segment.ident.span, + Some(receiver), + args, + ) { + return true; + } + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) + { + return true; + } + } + hir::ExprKind::Struct(qpath, fields, ..) => { + if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = + self.typeck_results.borrow().qpath_res(qpath, hir_id) + { + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + { + if let Some(param) = param + && self.point_at_field_if_possible( + error, + def_id, + param, + variant_def_id, + fields, + ) + { + return true; + } + } + } + if let Some(param_to_point_at) = param_to_point_at + && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) + { + return true; + } + } + _ => {} + } + + false + } + + fn closure_span_overlaps_error( + &self, + error: &traits::FulfillmentError<'tcx>, + span: Span, + ) -> bool { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), + ) = error.code + && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() + && span.overlaps(self.tcx.def_span(*def_id)) + { + true + } else { + false + } + } + + fn point_at_arg_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + call_hir_id: hir::HirId, + callee_span: Span, + receiver: Option<&'tcx hir::Expr<'tcx>>, + args: &'tcx [hir::Expr<'tcx>], + ) -> bool { + let sig = self.tcx.fn_sig(def_id).skip_binder(); + let args_referencing_param: Vec<_> = sig + .inputs() + .iter() + .enumerate() + .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at)) + .collect(); + // If there's one field that references the given generic, great! + if let [(idx, _)] = args_referencing_param.as_slice() + && let Some(arg) = receiver + .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) { + error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span); + error.obligation.cause.map_code(|parent_code| { + ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id: arg.hir_id, + call_hir_id, + parent_code, + } + }); + return true; + } else if args_referencing_param.len() > 0 { + // If more than one argument applies, then point to the callee span at least... + // We have chance to fix this up further in `point_at_generics_if_possible` + error.obligation.cause.span = callee_span; + } + + false + } + + fn point_at_field_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + variant_def_id: DefId, + expr_fields: &[hir::ExprField<'tcx>], + ) -> bool { + let def = self.tcx.adt_def(def_id); + + let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id); + let fields_referencing_param: Vec<_> = def + .variant_with_id(variant_def_id) + .fields + .iter() + .filter(|field| { + let field_ty = field.ty(self.tcx, identity_substs); + find_param_in_ty(field_ty, param_to_point_at) + }) + .collect(); + + if let [field] = fields_referencing_param.as_slice() { + for expr_field in expr_fields { + // Look for the ExprField that matches the field, using the + // same rules that check_expr_struct uses for macro hygiene. + if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx) + { + error.obligation.cause.span = expr_field + .expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(expr_field.span); + return true; + } + } + } + + false + } + + fn point_at_path_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param: ty::GenericArg<'tcx>, + qpath: &QPath<'tcx>, + ) -> bool { + match qpath { + hir::QPath::Resolved(_, path) => { + if let Some(segment) = path.segments.last() + && self.point_at_generic_if_possible(error, def_id, param, segment) + { + return true; + } + } + hir::QPath::TypeRelative(_, segment) => { + if self.point_at_generic_if_possible(error, def_id, param, segment) { + return true; + } + } + _ => {} + } + + false + } + + fn point_at_generic_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + segment: &hir::PathSegment<'tcx>, + ) -> bool { + let own_substs = self + .tcx + .generics_of(def_id) + .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id)); + let Some((index, _)) = own_substs + .iter() + .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) + .enumerate() + .find(|(_, arg)| **arg == param_to_point_at) else { return false }; + let Some(arg) = segment + .args() + .args + .iter() + .filter(|arg| matches!(arg, hir::GenericArg::Type(_))) + .nth(index) else { return false; }; + error.obligation.cause.span = arg + .span() + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(arg.span()); + true + } + + fn find_ambiguous_parameter_in<T: TypeVisitable<'tcx>>( + &self, + item_def_id: DefId, + t: T, + ) -> Option<ty::GenericArg<'tcx>> { + struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); + impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> { + type BreakTy = ty::GenericArg<'tcx>; + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> { + if let Some(origin) = self.0.type_var_origin(ty) + && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = + origin.kind + && let generics = self.0.tcx.generics_of(self.1) + && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) + && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1) + .get(index as usize) + { + ControlFlow::Break(*subst) + } else { + ty.super_visit_with(self) + } + } + } + t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value() + } + + fn label_fn_like( + &self, + err: &mut Diagnostic, + callable_def_id: Option<DefId>, + callee_ty: Option<Ty<'tcx>>, + // A specific argument should be labeled, instead of all of them + expected_idx: Option<usize>, + is_method: bool, + ) { + let Some(mut def_id) = callable_def_id else { + return; + }; + + if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // Possibly points at either impl or trait item, so try to get it + // to point to trait item, then get the parent. + // This parent might be an impl in the case of an inherent function, + // but the next check will fail. + && 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(callee_ty) = callee_ty + { + let callee_ty = callee_ty.peel_refs(); + match *callee_ty.kind() { + ty::Param(param) => { + let param = + self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); + if param.kind.is_synthetic() { + // if it's `impl Fn() -> ..` then just fall down to the def-id based logic + def_id = param.def_id; + } else { + // Otherwise, find the predicate that makes this generic callable, + // and point at that. + let instantiated = self + .tcx + .explicit_predicates_of(self.body_id.owner) + .instantiate_identity(self.tcx); + // FIXME(compiler-errors): This could be problematic if something has two + // fn-like predicates with different args, but callable types really never + // do that, so it's OK. + for (predicate, span) in + std::iter::zip(instantiated.predicates, instantiated.spans) + { + if let ty::PredicateKind::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() + { + err.span_note(span, "callable defined here"); + return; + } + } + } + } + ty::Opaque(new_def_id, _) + | ty::Closure(new_def_id, _) + | ty::FnDef(new_def_id, _) => { + def_id = new_def_id; + } + _ => { + // Look for a user-provided impl of a `Fn` trait, and point to it. + let new_def_id = self.probe(|_| { + let trait_ref = ty::TraitRef::new( + call_kind.to_def_id(self.tcx), + self.tcx.mk_substs( + [ + ty::GenericArg::from(callee_ty), + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: rustc_span::DUMMY_SP, + }) + .into(), + ] + .into_iter(), + ), + ); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }), + ); + match SelectionContext::new(&self).select(&obligation) { + Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { + Some(impl_source.impl_def_id) + } + _ => None, + } + }); + if let Some(new_def_id) = new_def_id { + def_id = new_def_id; + } else { + return; + } + } + } + } + + if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() { + let mut spans: MultiSpan = def_span.into(); + + let params = self + .tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .flat_map(|id| self.tcx.hir().body(id).params) + .skip(if is_method { 1 } else { 0 }); + + for (_, param) in params + .into_iter() + .enumerate() + .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) + { + spans.push_span_label(param.span, ""); + } + + let def_kind = self.tcx.def_kind(def_id); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) + && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind + { + let param = expected_idx + .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx)); + let (kind, span) = if let Some(param) = param { + ("closure parameter", param.span) + } else { + ("closure", self.tcx.def_span(def_id)) + }; + err.span_note(span, &format!("{} defined here", kind)); + } else { + let def_kind = self.tcx.def_kind(def_id); + err.span_note( + self.tcx.def_span(def_id), + &format!("{} defined here", def_kind.descr(def_id)), + ); + } + } +} + +fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool { + let mut walk = ty.walk(); + while let Some(arg) = walk.next() { + if arg == param_to_point_at { + return true; + } else if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(..) = ty.kind() + { + // This logic may seem a bit strange, but typically when + // we have a projection type in a function signature, the + // argument that's being passed into that signature is + // not actually constraining that projection's substs in + // a meaningful way. So we skip it, and see improvements + // in some UI tests. + walk.skip_current_subtree(); + } + } + false +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs new file mode 100644 index 000000000..0c600daf4 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -0,0 +1,312 @@ +mod _impl; +mod arg_matrix; +mod checks; +mod suggestions; + +pub use _impl::*; +pub use suggestions::*; + +use crate::coercion::DynamicCoerceMany; +use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir_analysis::astconv::AstConv; +use rustc_infer::infer; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::visit::TypeVisitable; +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 std::cell::{Cell, RefCell}; +use std::ops::Deref; + +/// The `FnCtxt` stores type-checking context needed to type-check bodies of +/// functions, closures, and `const`s, including performing type inference +/// with [`InferCtxt`]. +/// +/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures* +/// and thus does not perform type inference. +/// +/// See [`ItemCtxt`]'s docs for more. +/// +/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt +/// [`InferCtxt`]: infer::InferCtxt +pub struct FnCtxt<'a, 'tcx> { + pub(super) body_id: hir::HirId, + + /// The parameter environment used for proving trait obligations + /// in this function. This can change when we descend into + /// closures (as they bring new things into scope), hence it is + /// not part of `Inherited` (as of the time of this writing, + /// closures do not yet change the environment, but they will + /// eventually). + pub(super) param_env: ty::ParamEnv<'tcx>, + + /// Number of errors that had been reported when we started + /// checking this function. On exit, if we find that *more* errors + /// have been reported, we will skip regionck and other work that + /// expects the types within the function to be consistent. + // FIXME(matthewjasper) This should not exist, and it's not correct + // if type checking is run in parallel. + err_count_on_creation: usize, + + /// If `Some`, this stores coercion information for returned + /// expressions. If `None`, this is in a context where return is + /// inappropriate, such as a const expression. + /// + /// This is a `RefCell<DynamicCoerceMany>`, which means that we + /// can track all the return expressions and then use them to + /// compute a useful coercion from the set, similar to a match + /// expression or other branching context. You can use methods + /// like `expected_ty` to access the declared return type (if + /// 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 + /// indicates whether prior parts of the containing expression may + /// have diverged. It is then typically set to `Maybe` (and the + /// old value remembered) for processing the subparts of the + /// current expression. As each subpart is processed, they may set + /// the flag to `Always`, etc. Finally, at the end, we take the + /// result and "union" it with the original value, so that when we + /// return the flag indicates if any subpart of the parent + /// expression (up to and including this part) has diverged. So, + /// if you read it after evaluating a subexpression `X`, the value + /// you get indicates whether any subexpression that was + /// evaluating up to and including `X` diverged. + /// + /// We currently use this flag only for diagnostic purposes: + /// + /// - To warn about unreachable code: if, after processing a + /// sub-expression but before we have applied the effects of the + /// current node, we see that the flag is set to `Always`, we + /// can issue a warning. This corresponds to something like + /// `foo(return)`; we warn on the `foo()` expression. (We then + /// update the flag to `WarnedAlways` to suppress duplicate + /// reports.) Similarly, if we traverse to a fresh statement (or + /// tail expression) from an `Always` setting, we will issue a + /// warning. This corresponds to something like `{return; + /// foo();}` or `{return; 22}`, where we would warn on the + /// `foo()` or `22`. + /// + /// An expression represents dead code if, after checking it, + /// 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, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn new( + inh: &'a Inherited<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> FnCtxt<'a, 'tcx> { + FnCtxt { + body_id, + 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, + } + } + + pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { + ObligationCause::new(span, self.body_id, code) + } + + pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { + self.cause(span, ObligationCauseCode::MiscObligation) + } + + pub fn sess(&self) -> &Session { + &self.tcx.sess + } + + /// Creates an `TypeErrCtxt` with a reference to the in-progress + /// `TypeckResults` which is used for diagnostics. + /// Use [`InferCtxt::err_ctxt`] to start one without a `TypeckResults`. + /// + /// [`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()) } + } + + pub fn errors_reported_since_creation(&self) -> bool { + self.tcx.sess.err_count() > self.err_count_on_creation + } +} + +impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { + type Target = Inherited<'tcx>; + fn deref(&self) -> &Self::Target { + &self.inh + } +} + +impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option<DefId> { + None + } + + fn get_type_parameter_bounds( + &self, + _: Span, + def_id: DefId, + _: Ident, + ) -> ty::GenericPredicates<'tcx> { + let tcx = self.tcx; + let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + ty::GenericPredicates { + parent: None, + 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) => { + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + Some((predicate, span)) + } + _ => None, + } + }), + ), + } + } + + fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span), + }; + Some(self.next_region_var(v)) + } + + fn allow_ty_infer(&self) -> bool { + true + } + + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { + return ty; + } + unreachable!() + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }) + } + } + + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> Const<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { + return ct; + } + unreachable!() + } else { + self.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, + ) + } + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + let trait_ref = self.replace_bound_vars_with_fresh_vars( + span, + infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), + poly_trait_ref, + ); + + let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( + self, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + self.tcx().mk_projection(item_def_id, item_substs) + } + + fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_bound_vars() { + ty // FIXME: normalization and escaping regions + } else { + self.normalize_associated_types_in(span, ty) + } + } + + fn set_tainted_by_errors(&self) { + self.infcx.set_tainted_by_errors() + } + + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { + self.write_ty(hir_id, ty) + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs new file mode 100644 index 000000000..4db9c56f9 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -0,0 +1,1250 @@ +use super::FnCtxt; + +use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind}; +use rustc_hir::lang_items::LangItem; +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::traits::{self, StatementAsExpression}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Binder, 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 _; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { + err.span_suggestion_short( + span.shrink_to_hi(), + "consider using a semicolon here", + ";", + Applicability::MachineApplicable, + ); + } + + /// On implicit return expressions with mismatched types, provides the following suggestions: + /// + /// - Points out the method's return type as the reason for the expected type. + /// - Possible missing semicolon. + /// - Possible missing return type if the return type is the default, and not `fn main()`. + pub fn suggest_mismatched_types_on_tail( + &self, + err: &mut Diagnostic, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + blk_id: hir::HirId, + ) -> bool { + let expr = expr.peel_drop_temps(); + self.suggest_missing_semicolon(err, expr, expected, false); + let mut pointing_at_return_type = false; + if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { + let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); + pointing_at_return_type = self.suggest_missing_return_type( + err, + &fn_decl, + expected, + found, + can_suggest, + fn_id, + ); + self.suggest_missing_break_or_return_expr( + err, expr, &fn_decl, expected, found, blk_id, fn_id, + ); + } + pointing_at_return_type + } + + /// When encountering an fn-like type, try accessing the output of the type + /// and suggesting calling it if it satisfies a predicate (i.e. if the + /// output has a method or a field): + /// ```compile_fail,E0308 + /// fn foo(x: usize) -> usize { x } + /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` + /// ``` + pub(crate) fn suggest_fn_call( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, + ) -> bool { + let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + if can_satisfy(output) { + let (sugg_call, mut applicability) = match inputs.len() { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::<Vec<_>>() + .join(", "), + Applicability::HasPlaceholders, + ), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), + }; + + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), + kind => format!("call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("call this {name}"), + }; + + let sugg = match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] + } + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] + } + _ => { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ] + } + }; + + err.multipart_suggestion_verbose( + format!("use parentheses to {msg}"), + sugg, + applicability, + ); + return true; + } + false + } + + /// Extracts information about a callable type for diagnostics. This is a + /// heuristic -- it doesn't necessarily mean that a type is always callable, + /// because the callable type must also be well-formed to be called. + pub(in super::super) fn extract_callable_info( + &self, + expr: &Expr<'_>, + found: Ty<'tcx>, + ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { + // Autoderef is useful here because sometimes we box callables, etc. + let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { + match *found.kind() { + ty::FnPtr(fn_sig) => + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())), + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..]))) + } + 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() + && 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() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + 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() + && 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] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + } + }) else { return None; }; + + let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); + let inputs = inputs + .skip_binder() + .iter() + .map(|ty| { + self.replace_bound_vars_with_fresh_vars( + expr.span, + infer::FnCall, + inputs.rebind(*ty), + ) + }) + .collect(); + + // We don't want to register any extra obligations, which should be + // 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); + + if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } + } + + pub fn suggest_two_fn_call( + &self, + err: &mut Diagnostic, + lhs_expr: &'tcx hir::Expr<'tcx>, + lhs_ty: Ty<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + rhs_ty: Ty<'tcx>, + can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, + ) -> bool { + let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) + else { return false; }; + let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) + else { return false; }; + + if can_satisfy(lhs_output_ty, rhs_output_ty) { + let mut sugg = vec![]; + let mut applicability = Applicability::MachineApplicable; + + for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { + let (sugg_call, this_applicability) = match inputs.len() { + 0 => ("".to_string(), Applicability::MachineApplicable), + 1..=4 => ( + inputs + .iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::<Vec<_>>() + .join(", "), + Applicability::HasPlaceholders, + ), + _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), + }; + + applicability = applicability.max(this_applicability); + + match expr.kind { + hir::ExprKind::Call(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Lit(..) => { + sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); + } + hir::ExprKind::Closure { .. } => { + // Might be `{ expr } || { bool }` + applicability = Applicability::MaybeIncorrect; + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } + _ => { + sugg.extend([ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(")({sugg_call})")), + ]); + } + } + } + + err.multipart_suggestion_verbose( + format!("use parentheses to call these"), + sugg, + applicability, + ); + + true + } else { + false + } + } + + pub fn suggest_deref_ref_or_into( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) -> bool { + let expr = expr.peel_blocks(); + if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = + self.check_ref(expr, found, expected) + { + if verbose { + err.span_suggestion_verbose(sp, &msg, suggestion, applicability); + } else { + err.span_suggestion(sp, &msg, suggestion, applicability); + } + 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 ", + _ => return true, + }; + let mut tuple_indexes = Vec::new(); + let mut expr_id = expr.hir_id; + for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { + match node { + Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { + tuple_indexes.push( + subs.iter() + .enumerate() + .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) + .unwrap() + .0, + ); + expr_id = parent_id; + } + Node::Local(local) => { + if let Some(mut ty) = local.ty { + while let Some(index) = tuple_indexes.pop() { + match ty.kind { + TyKind::Tup(tys) => ty = &tys[index], + _ => return true, + } + } + let annotation_span = ty.span; + err.span_suggestion( + annotation_span.with_hi(annotation_span.lo()), + format!("alternatively, consider changing the type annotation"), + suggest_annotation, + Applicability::MaybeIncorrect, + ); + } + break; + } + _ => break, + } + } + } + return true; + } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + return true; + } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + && let ty::FnDef(def_id, ..) = &found.kind() + && let Some(sp) = self.tcx.hir().span_if_local(*def_id) + { + err.span_label(sp, format!("{found} defined here")); + return true; + } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + return true; + } else { + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if !methods.is_empty() { + let mut suggestions = methods.iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) + } + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. + { + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] + } else { + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] + }; + let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert( + 0, + (expr.span.shrink_to_lo(), format!("{}: ", name)), + ); + } + Some(sugg) + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + return true; + } + } else if let ty::Adt(found_adt, found_substs) = found.kind() + && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) + && let ty::Adt(expected_adt, expected_substs) = expected.kind() + && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) + && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() + && inner_ty.is_str() + { + let ty = found_substs.type_at(0); + let mut peeled = ty; + let mut ref_cnt = 0; + while let ty::Ref(_, inner, _) = peeled.kind() { + peeled = *inner; + ref_cnt += 1; + } + if let ty::Adt(adt, _) = peeled.kind() + && self.tcx.is_diagnostic_item(sym::String, adt.did()) + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + "try converting the passed type into a `&str`", + format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), + Applicability::MaybeIncorrect, + ); + return true; + } + } + } + + false + } + + /// When encountering the expected boxed value allocated in the stack, suggest allocating it + /// in the heap by calling `Box::new()`. + pub(in super::super) fn suggest_boxing_when_appropriate( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return false; + } + if !expected.is_box() || found.is_box() { + return false; + } + let boxed_found = self.tcx.mk_box(found); + if self.can_coerce(boxed_found, expected) { + err.multipart_suggestion( + "store this in the heap by calling `Box::new`", + vec![ + (expr.span.shrink_to_lo(), "Box::new(".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + err.note( + "for more on the distinction between the stack and the heap, read \ + https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html, and \ + https://doc.rust-lang.org/std/boxed/index.html", + ); + true + } else { + false + } + } + + /// When encountering a closure that captures variables, where a FnPtr is expected, + /// suggest a non-capturing closure + pub(in super::super) fn suggest_no_capture_closure( + &self, + err: &mut Diagnostic, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { + if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { + // Report upto four upvars being captured to reduce the amount error messages + // reported back to the user. + let spans_and_labels = upvars + .iter() + .take(4) + .map(|(var_hir_id, upvar)| { + let var_name = self.tcx.hir().name(*var_hir_id).to_string(); + let msg = format!("`{}` captured here", var_name); + (upvar.span, msg) + }) + .collect::<Vec<_>>(); + + let mut multi_span: MultiSpan = + spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); + for (sp, label) in spans_and_labels { + multi_span.push_span_label(sp, label); + } + err.span_note( + multi_span, + "closures can only be coerced to `fn` types if they do not capture any variables" + ); + return true; + } + } + false + } + + /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. + #[instrument(skip(self, err))] + pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + // Handle #68197. + + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return false; + } + let pin_did = self.tcx.lang_items().pin_type(); + // This guards the `unwrap` and `mk_box` below. + if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { + return false; + } + let box_found = self.tcx.mk_box(found); + let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap(); + let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap(); + match expected.kind() { + ty::Adt(def, _) if Some(def.did()) == pin_did => { + if self.can_coerce(pin_box_found, expected) { + debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + } + _ => { + err.multipart_suggestion( + "you need to pin and box this expression", + vec![ + (expr.span.shrink_to_lo(), "Box::pin(".to_string()), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + } + true + } else if self.can_coerce(pin_found, expected) { + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + true + } + _ => false, + } + } else { + false + } + } + ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { + // Check if the parent expression is a call to Pin::new. If it + // is and we were expecting a Box, ergo Pin<Box<expected>>, we + // can suggest Box::pin. + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { + return false; + }; + match fn_name.kind { + ExprKind::Path(QPath::TypeRelative( + hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), + .. + }, + method, + )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => { + err.span_suggestion( + fn_name.span, + "use `Box::pin` to pin and box this expression", + "Box::pin", + Applicability::MachineApplicable, + ); + true + } + _ => false, + } + } + _ => false, + } + } + + /// A common error is to forget to add a semicolon at the end of a block, e.g., + /// + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return expression in a block would make sense on its own as a + /// statement and the return type has been left as default or has been specified as `()`. If so, + /// it suggests adding a semicolon. + /// + /// If the expression is the expression of a closure without block (`|| expr`), a + /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. + pub fn suggest_missing_semicolon( + &self, + err: &mut Diagnostic, + expression: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + needs_block: bool, + ) { + if expected.is_unit() { + // `BlockTailExpression` only relevant if the tail expr would be + // useful on its own. + match expression.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::If(..) + | ExprKind::Match(..) + | ExprKind::Block(..) + if expression.can_have_side_effects() + // If the expression is from an external macro, then do not suggest + // adding a semicolon, because there's nowhere to put it. + // See issue #81943. + && !in_external_macro(self.tcx.sess, expression.span) => + { + if needs_block { + err.multipart_suggestion( + "consider using a semicolon here", + vec![ + (expression.span.shrink_to_lo(), "{ ".to_owned()), + (expression.span.shrink_to_hi(), "; }".to_owned()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion( + expression.span.shrink_to_hi(), + "consider using a semicolon here", + ";", + Applicability::MachineApplicable, + ); + } + } + _ => (), + } + } + } + + /// A possible error is to forget to add a return type that is needed: + /// + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return type is left as default, the method is not part of an + /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return + /// type. + pub(in super::super) fn suggest_missing_return_type( + &self, + err: &mut Diagnostic, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + can_suggest: bool, + fn_id: hir::HirId, + ) -> bool { + let found = + self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); + // Only suggest changing the return type for methods that + // haven't set a return type at all (and aren't `fn main()` or an impl). + match &fn_decl.output { + &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { + // `fn main()` must return `()`, do not suggest changing return type + err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); + return true; + } + &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { + if found.is_suggestable(self.tcx, false) { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + return true; + } else if let ty::Closure(_, substs) = found.kind() + // FIXME(compiler-errors): Get better at printing binders... + && let closure = substs.as_closure() + && closure.sig().is_suggestable(self.tcx, false) + { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); + return true; + } else { + // FIXME: if `found` could be `impl Iterator` we should suggest that. + err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); + return true + } + } + &hir::FnRetTy::Return(ref ty) => { + // Only point to return type if the expected type is the return type, as if they + // are not, the expectation must have been caused by something else. + debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); + let span = ty.span; + let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); + debug!("suggest_missing_return_type: return type {:?}", ty); + 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.tcx.erase_late_bound_regions(ty); + if self.can_coerce(expected, ty) { + err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); + self.try_suggest_return_impl_trait(err, expected, ty, fn_id); + return true; + } + } + _ => {} + } + false + } + + /// check whether the return type is a generic type with a trait bound + /// only suggest this if the generic param is not present in the arguments + /// if this is true, hint them towards changing the return type to `impl Trait` + /// ```compile_fail,E0308 + /// fn cant_name_it<T: Fn() -> u32>() -> T { + /// || 3 + /// } + /// ``` + fn try_suggest_return_impl_trait( + &self, + err: &mut Diagnostic, + expected: Ty<'tcx>, + found: Ty<'tcx>, + fn_id: hir::HirId, + ) { + // Only apply the suggestion if: + // - the return type is a generic parameter + // - the generic param is not used as a fn param + // - the generic param has at least one bound + // - the generic param doesn't appear in any other bounds where it's not the Self type + // Suggest: + // - Changing the return type to be `impl <all bounds>` + + debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); + + let ty::Param(expected_ty_as_param) = expected.kind() else { return }; + + let fn_node = self.tcx.hir().find(fn_id); + + let Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Fn( + hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, + hir::Generics { params, predicates, .. }, + _body_id, + ), + .. + })) = fn_node else { return }; + + if params.get(expected_ty_as_param.index as usize).is_none() { + return; + }; + + // get all where BoundPredicates here, because they are used in to cases below + let where_predicates = predicates + .iter() + .filter_map(|p| match p { + WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bounds, + bounded_ty, + .. + }) => { + // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) + let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty); + Some((ty, bounds)) + } + _ => None, + }) + .map(|(ty, bounds)| match ty.kind() { + ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), + // check whether there is any predicate that contains our `T`, like `Option<T>: Send` + _ => match ty.contains(expected) { + true => Err(()), + false => Ok(None), + }, + }) + .collect::<Result<Vec<_>, _>>(); + + let Ok(where_predicates) = where_predicates else { return }; + + // now get all predicates in the same types as the where bounds, so we can chain them + let predicates_from_where = + where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); + + // extract all bounds from the source code using their spans + let all_matching_bounds_strs = predicates_from_where + .filter_map(|bound| match bound { + GenericBound::Trait(_, _) => { + self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() + } + _ => None, + }) + .collect::<Vec<String>>(); + + if all_matching_bounds_strs.len() == 0 { + return; + } + + let all_bounds_str = all_matching_bounds_strs.join(" + "); + + let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { + let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param); + matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) + }); + + if ty_param_used_in_fn_params { + return; + } + + err.span_suggestion( + fn_return.span(), + "consider using an impl return type", + format!("impl {}", all_bounds_str), + Applicability::MaybeIncorrect, + ); + } + + pub(in super::super) fn suggest_missing_break_or_return_expr( + &self, + err: &mut Diagnostic, + expr: &'tcx hir::Expr<'tcx>, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + id: hir::HirId, + fn_id: hir::HirId, + ) { + if !expected.is_unit() { + return; + } + let found = self.resolve_vars_with_obligations(found); + + let in_loop = self.is_loop(id) + || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id)); + + let in_local_statement = self.is_local_statement(id) + || self + .tcx + .hir() + .parent_iter(id) + .any(|(parent_id, _)| self.is_local_statement(parent_id)); + + if in_loop && in_local_statement { + err.multipart_suggestion( + "you might have meant to break the loop with this value", + vec![ + (expr.span.shrink_to_lo(), "break ".to_string()), + (expr.span.shrink_to_hi(), ";".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return; + } + + if let hir::FnRetTy::Return(ty) = fn_decl.output { + 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::NotAsync => ty, + }; + if self.can_coerce(found, ty) { + err.multipart_suggestion( + "you might have meant to return this value", + vec![ + (expr.span.shrink_to_lo(), "return ".to_string()), + (expr.span.shrink_to_hi(), ";".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + } + } + + pub(in super::super) fn suggest_missing_parentheses( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ) -> bool { + let sp = self.tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + true + } else { + false + } + } + + /// Given an expression type mismatch, peel any `&` expressions until we get to + /// a block expression, and then suggest replacing the braces with square braces + /// if it was possibly mistaken array syntax. + pub(crate) fn suggest_block_to_brackets_peeling_refs( + &self, + diag: &mut Diagnostic, + mut expr: &hir::Expr<'_>, + mut expr_ty: Ty<'tcx>, + mut expected_ty: Ty<'tcx>, + ) -> bool { + loop { + match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { + ( + hir::ExprKind::AddrOf(_, _, inner_expr), + ty::Ref(_, inner_expr_ty, _), + ty::Ref(_, inner_expected_ty, _), + ) => { + expr = *inner_expr; + expr_ty = *inner_expr_ty; + expected_ty = *inner_expected_ty; + } + (hir::ExprKind::Block(blk, _), _, _) => { + self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); + break true; + } + _ => break false, + } + } + } + + pub(crate) fn suggest_copied_or_cloned( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; + let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; + if adt_def != expected_adt_def { + return false; + } + + let mut suggest_copied_or_cloned = || { + let expr_inner_ty = substs.type_at(0); + let expected_inner_ty = expected_substs.type_at(0); + if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() + && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok() + { + let def_path = self.tcx.def_path_str(adt_def.did()); + if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{def_path}::copied` to copy the value inside the `{def_path}`" + ), + ".copied()", + Applicability::MachineApplicable, + ); + return true; + } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() + && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( + self, + self.param_env, + *ty, + clone_did, + expr.span + ) + { + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{def_path}::cloned` to clone the value inside the `{def_path}`" + ), + ".cloned()", + Applicability::MachineApplicable, + ); + return true; + } + } + false + }; + + if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) + && adt_def.did() == result_did + // Check that the error types are equal + && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() + { + return suggest_copied_or_cloned(); + } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) + && adt_def.did() == option_did + { + return suggest_copied_or_cloned(); + } + + false + } + + pub(crate) fn suggest_into( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + let expr = expr.peel_blocks(); + + // We have better suggestions for scalar interconversions... + if expr_ty.is_scalar() && expected_ty.is_scalar() { + return false; + } + + // Don't suggest turning a block into another type (e.g. `{}.into()`) + if matches!(expr.kind, hir::ExprKind::Block(..)) { + return false; + } + + // We'll later suggest `.as_ref` when noting the type error, + // so skip if we will suggest that instead. + if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() { + return false; + } + + if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) + && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + 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), + )) + { + let sugg = if expr.precedence().order() >= PREC_POSTFIX { + vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] + } else { + vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] + }; + diag.multipart_suggestion( + format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), + sugg, + Applicability::MaybeIncorrect + ); + return true; + } + + false + } + + /// 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( + &self, + diag: &mut Diagnostic, + blk: &hir::Block<'_>, + blk_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) { + if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { + if self.can_coerce(blk_ty, *elem_ty) + && blk.stmts.is_empty() + && blk.rules == hir::BlockCheckMode::DefaultBlock + { + let source_map = self.tcx.sess.source_map(); + if let Ok(snippet) = source_map.span_to_snippet(blk.span) { + if snippet.starts_with('{') && snippet.ends_with('}') { + diag.multipart_suggestion_verbose( + "to create an array, use square brackets instead of curly braces", + vec![ + ( + blk.span + .shrink_to_lo() + .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), + "[".to_string(), + ), + ( + blk.span + .shrink_to_hi() + .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), + "]".to_string(), + ), + ], + Applicability::MachineApplicable, + ); + } + } + } + } + } + + fn is_loop(&self, id: hir::HirId) -> bool { + let node = self.tcx.hir().get(id); + matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) + } + + fn is_local_statement(&self, id: hir::HirId) -> bool { + let node = self.tcx.hir().get(id); + matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. })) + } + + /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`, + /// which is a side-effect of autoref. + pub(crate) fn note_type_is_not_clone( + &self, + diag: &mut Diagnostic, + expected_ty: Ty<'tcx>, + found_ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + ) { + let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; + let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; + let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; + let results = self.typeck_results.borrow(); + // First, look for a `Clone::clone` call + if segment.ident.name == sym::clone + && results.type_dependent_def_id(expr.hir_id).map_or( + false, + |did| { + let assoc_item = self.tcx.associated_item(did); + assoc_item.container == ty::AssocItemContainer::TraitContainer + && assoc_item.container_id(self.tcx) == clone_trait_did + }, + ) + // If that clone call hasn't already dereferenced the self type (i.e. don't give this + // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). + && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) + // Check that we're in fact trying to clone into the expected type + && self.can_coerce(*pointee_ty, expected_ty) + // And the expected type doesn't implement `Clone` + && !self.predicate_must_hold_considering_regions(&traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env: self.param_env, + recursion_depth: 0, + predicate: ty::Binder::dummy(ty::TraitRef { + def_id: clone_trait_did, + substs: self.tcx.mk_substs([expected_ty.into()].iter()), + }) + .without_const() + .to_predicate(self.tcx), + }) + { + diag.span_note( + callee_expr.span, + &format!( + "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" + ), + ); + } + } + + /// A common error is to add an extra semicolon: + /// + /// ```compile_fail,E0308 + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + pub(crate) fn consider_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { + if let StatementAsExpression::NeedsBoxing = boxed { + err.span_suggestion_verbose( + span_semi, + "consider removing this semicolon and boxing the expression", + "", + Applicability::HasPlaceholders, + ); + } else { + err.span_suggestion_short( + span_semi, + "remove this semicolon to return this value", + "", + Applicability::MachineApplicable, + ); + } + true + } else { + false + } + } +} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 8f34a970f..9a096f24f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -1,9 +1,10 @@ -use crate::check::{FnCtxt, LocalTy, UserType}; +use crate::{FnCtxt, LocalTy}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; +use rustc_middle::ty::UserType; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index a2c23db16..122ad7009 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { } /// For an expression with an uninhabited return type (e.g. a function that returns !), - /// this adds a self edge to to the CFG to model the fact that the function does not + /// this adds a self edge to the CFG to model the fact that the function does not /// return. fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { let ty = self.typeck_results.expr_ty(expr); @@ -256,6 +256,8 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::TypeBinding(..) | hir::Node::TraitRef(..) | hir::Node::Pat(..) + | hir::Node::PatField(..) + | hir::Node::ExprField(..) | hir::Node::Arm(..) | hir::Node::Local(..) | hir::Node::Ctor(..) @@ -432,7 +434,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { self.handle_uninhabited_return(expr); } - ExprKind::MethodCall(_, exprs, _) => { + ExprKind::MethodCall(_, receiver, exprs, _) => { + self.visit_expr(receiver); for expr in exprs { self.visit_expr(expr); } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs index 139d17d2e..139d17d2e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs index c0a0bfe8e..c0a0bfe8e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs 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 new file mode 100644 index 000000000..4f3bdfbe7 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -0,0 +1,309 @@ +//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped +//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the +//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used. +//! +//! There are three phases to this analysis: +//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed. +//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized, +//! and also build a control flow graph. +//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through +//! the CFG and find the exact points where we know a value is definitely dropped. +//! +//! The end result is a data structure that maps the post-order index of each node in the HIR tree +//! to a set of values that are known to be dropped at that location. + +use self::cfg_build::build_control_flow_graph; +use self::record_consumed_borrow::find_consumed_and_borrowed; +use crate::FnCtxt; +use hir::def_id::DefId; +use hir::{Body, HirId, HirIdMap, Node}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; +use rustc_middle::ty; +use std::collections::BTreeMap; +use std::fmt::Debug; + +mod cfg_build; +mod cfg_propagate; +mod cfg_visualize; +mod record_consumed_borrow; + +pub fn compute_drop_ranges<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + def_id: DefId, + body: &'tcx Body<'tcx>, +) -> DropRanges { + if fcx.sess().opts.unstable_opts.drop_tracking { + let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); + + 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, + typeck_results, + consumed_borrowed_places, + body, + num_exprs, + ); + + drop_ranges.propagate_to_fixpoint(); + + debug!("borrowed_temporaries = {borrowed_temporaries:?}"); + DropRanges { + tracked_value_map: drop_ranges.tracked_value_map, + nodes: drop_ranges.nodes, + borrowed_temporaries: Some(borrowed_temporaries), + } + } else { + // If drop range tracking is not enabled, skip all the analysis and produce an + // empty set of DropRanges. + DropRanges { + tracked_value_map: FxHashMap::default(), + nodes: IndexVec::new(), + borrowed_temporaries: None, + } + } +} + +/// Applies `f` to consumable node in the HIR subtree pointed to by `place`. +/// +/// This includes the place itself, and if the place is a reference to a local +/// variable then `f` is also called on the HIR node for that variable as well. +/// +/// For example, if `place` points to `foo()`, then `f` is called once for the +/// result of `foo`. On the other hand, if `place` points to `x` then `f` will +/// be called both on the `ExprKind::Path` node that represents the expression +/// as well as the HirId of the local `x` itself. +fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) { + f(place); + let node = hir.find(place.hir_id()); + if let Some(Node::Expr(expr)) = node { + match expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) => { + f(TrackedValue::Variable(*hir_id)); + } + _ => (), + } + } +} + +rustc_index::newtype_index! { + pub struct PostOrderId { + DEBUG_FORMAT = "id({})", + } +} + +rustc_index::newtype_index! { + pub struct TrackedValueIndex { + DEBUG_FORMAT = "hidx({})", + } +} + +/// Identifies a value whose drop state we need to track. +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +enum TrackedValue { + /// Represents a named variable, such as a let binding, parameter, or upvar. + /// + /// The HirId points to the variable's definition site. + Variable(HirId), + /// A value produced as a result of an expression. + /// + /// The HirId points to the expression that returns this value. + Temporary(HirId), +} + +impl Debug for TrackedValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ty::tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + write!(f, "{}", tcx.hir().node_to_string(self.hir_id())) + } else { + match self { + Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id), + Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id), + } + } + }) + } +} + +impl TrackedValue { + fn hir_id(&self) -> HirId { + match self { + TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id, + } + } + + fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self { + match place_with_id.place.base { + PlaceBase::Rvalue | PlaceBase::StaticItem => { + TrackedValue::Temporary(place_with_id.hir_id) + } + PlaceBase::Local(hir_id) + | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => { + TrackedValue::Variable(hir_id) + } + } + } +} + +/// Represents a reason why we might not be able to convert a HirId or Place +/// into a tracked value. +#[derive(Debug)] +enum TrackedValueConversionError { + /// Place projects are not currently supported. + /// + /// The reasoning around these is kind of subtle, so we choose to be more + /// conservative around these for now. There is no reason in theory we + /// cannot support these, we just have not implemented it yet. + PlaceProjectionsNotSupported, +} + +impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue { + type Error = TrackedValueConversionError; + + fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> { + if !place_with_id.place.projections.is_empty() { + debug!( + "TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.", + place_with_id + ); + return Err(TrackedValueConversionError::PlaceProjectionsNotSupported); + } + + Ok(TrackedValue::from_place_with_projections_allowed(place_with_id)) + } +} + +pub struct DropRanges { + tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, + nodes: IndexVec<PostOrderId, NodeInfo>, + borrowed_temporaries: Option<FxHashSet<HirId>>, +} + +impl DropRanges { + pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool { + self.tracked_value_map + .get(&TrackedValue::Temporary(hir_id)) + .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) + .cloned() + .map_or(false, |tracked_value_id| { + self.expect_node(location.into()).drop_state.contains(tracked_value_id) + }) + } + + pub fn is_borrowed_temporary(&self, expr: &hir::Expr<'_>) -> bool { + if let Some(b) = &self.borrowed_temporaries { b.contains(&expr.hir_id) } else { true } + } + + /// Returns a reference to the NodeInfo for a node, panicking if it does not exist + fn expect_node(&self, id: PostOrderId) -> &NodeInfo { + &self.nodes[id] + } +} + +/// Tracks information needed to compute drop ranges. +struct DropRangesBuilder { + /// The core of DropRangesBuilder is a set of nodes, which each represent + /// one expression. We primarily refer to them by their index in a + /// post-order traversal of the HIR tree, since this is what + /// generator_interior uses to talk about yield positions. + /// + /// This IndexVec keeps the relevant details for each node. See the + /// NodeInfo struct for more details, but this information includes things + /// such as the set of control-flow successors, which variables are dropped + /// or reinitialized, and whether each variable has been inferred to be + /// known-dropped or potentially reinitialized at each point. + nodes: IndexVec<PostOrderId, NodeInfo>, + /// We refer to values whose drop state we are tracking by the HirId of + /// where they are defined. Within a NodeInfo, however, we store the + /// drop-state in a bit vector indexed by a HirIdIndex + /// (see NodeInfo::drop_state). The hir_id_map field stores the mapping + /// from HirIds to the HirIdIndex that is used to represent that value in + /// bitvector. + tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, + + /// When building the control flow graph, we don't always know the + /// post-order index of the target node at the point we encounter it. + /// For example, this happens with break and continue. In those cases, + /// we store a pair of the PostOrderId of the source and the HirId + /// of the target. Once we have gathered all of these edges, we make a + /// pass over the set of deferred edges (see process_deferred_edges in + /// cfg_build.rs), look up the PostOrderId for the target (since now the + /// post-order index for all nodes is known), and add missing control flow + /// edges. + deferred_edges: Vec<(PostOrderId, HirId)>, + /// This maps HirIds of expressions to their post-order index. It is + /// used in process_deferred_edges to correctly add back-edges. + post_order_map: HirIdMap<PostOrderId>, +} + +impl Debug for DropRangesBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DropRanges") + .field("hir_id_map", &self.tracked_value_map) + .field("post_order_maps", &self.post_order_map) + .field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>()) + .finish() + } +} + +/// DropRanges keeps track of what values are definitely dropped at each point in the code. +/// +/// Values of interest are defined by the hir_id of their place. Locations in code are identified +/// by their index in the post-order traversal. At its core, DropRanges maps +/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely +/// dropped at the point of the node identified by post_order_id. +impl DropRangesBuilder { + /// Returns the number of values (hir_ids) that are tracked + fn num_values(&self) -> usize { + self.tracked_value_map.len() + } + + fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo { + let size = self.num_values(); + self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)); + &mut self.nodes[id] + } + + fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) { + trace!("adding control edge from {:?} to {:?}", from, to); + self.node_mut(from).successors.push(to); + } +} + +#[derive(Debug)] +struct NodeInfo { + /// IDs of nodes that can follow this one in the control flow + /// + /// If the vec is empty, then control proceeds to the next node. + successors: Vec<PostOrderId>, + + /// List of hir_ids that are dropped by this node. + drops: Vec<TrackedValueIndex>, + + /// List of hir_ids that are reinitialized by this node. + reinits: Vec<TrackedValueIndex>, + + /// Set of values that are definitely dropped at this point. + drop_state: BitSet<TrackedValueIndex>, +} + +impl NodeInfo { + fn new(num_values: usize) -> Self { + Self { + successors: vec![], + drops: vec![], + reinits: vec![], + drop_state: BitSet::new_filled(num_values), + } + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index ded0888c3..bfe95852a 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -1,13 +1,16 @@ use super::TrackedValue; use crate::{ - check::FnCtxt, expr_use_visitor::{self, ExprUseVisitor}, + FnCtxt, }; use hir::{def_id::DefId, Body, HirId, HirIdMap}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_middle::{ + hir::place::{PlaceBase, Projection, ProjectionKind}, + ty::TypeVisitable, +}; pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, @@ -159,8 +162,8 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { bk: rustc_middle::ty::BorrowKind, ) { debug!( - "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \ - borrow_kind={bk:?}" + "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \ + borrow_kind={bk:#?}" ); self.borrow_place(place_with_id); @@ -198,7 +201,13 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { // If the type being assigned needs dropped, then the mutation counts as a borrow // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. - if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) { + let ty = self.tcx.erase_regions(assignee_place.place.base_ty); + if ty.needs_infer() { + self.tcx.sess.delay_span_bug( + self.tcx.hir().span(assignee_place.hir_id), + &format!("inference variables in {ty}"), + ); + } else if ty.needs_drop(self.tcx, self.param_env) { self.places .borrowed .insert(TrackedValue::from_place_with_projections_allowed(assignee_place)); diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs new file mode 100644 index 000000000..b7dd599cd --- /dev/null +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -0,0 +1,647 @@ +//! This calculates the types which has storage which lives across a suspension point in a +//! generator from the perspective of typeck. The actual types used at runtime +//! is calculated in `rustc_mir_transform::generator` and may be a subset of the +//! types computed here. + +use self::drop_ranges::DropRanges; +use super::FnCtxt; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_errors::{pluralize, DelayDm}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; +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_middle::middle::region::{self, Scope, ScopeData, YieldData}; +use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +mod drop_ranges; + +struct InteriorVisitor<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + region_scope_tree: &'a region::ScopeTree, + types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, + rvalue_scopes: &'a RvalueScopes, + expr_count: usize, + kind: hir::GeneratorKind, + prev_unresolved_span: Option<Span>, + linted_values: HirIdSet, + drop_ranges: DropRanges, +} + +impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { + fn record( + &mut self, + ty: Ty<'tcx>, + hir_id: HirId, + scope: Option<region::Scope>, + expr: Option<&'tcx Expr<'tcx>>, + source_span: Span, + ) { + use rustc_span::DUMMY_SP; + + let ty = self.fcx.resolve_vars_if_possible(ty); + + debug!( + "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}", + ty, hir_id, scope, expr, source_span, self.expr_count, + ); + + let live_across_yield = scope + .map(|s| { + self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| { + // If we are recording an expression that is the last yield + // in the scope, or that has a postorder CFG index larger + // than the one of all of the yields, then its value can't + // be storage-live (and therefore live) at any of the yields. + // + // See the mega-comment at `yield_in_scope` for a proof. + + yield_data + .iter() + .find(|yield_data| { + debug!( + "comparing counts yield: {} self: {}, source_span = {:?}", + yield_data.expr_and_pat_count, self.expr_count, source_span + ); + + if self.fcx.sess().opts.unstable_opts.drop_tracking + && self + .drop_ranges + .is_dropped_at(hir_id, yield_data.expr_and_pat_count) + { + debug!("value is dropped at yield point; not recording"); + return false; + } + + // If it is a borrowing happening in the guard, + // it needs to be recorded regardless because they + // do live across this yield point. + yield_data.expr_and_pat_count >= self.expr_count + }) + .cloned() + }) + }) + .unwrap_or_else(|| { + Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() }) + }); + + if let Some(yield_data) = live_across_yield { + debug!( + "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", + expr, scope, ty, self.expr_count, yield_data.span + ); + + if let Some((unresolved_type, unresolved_type_span)) = + self.fcx.unresolved_type_vars(&ty) + { + // If unresolved type isn't a ty_var then unresolved_type_span is None + let span = self + .prev_unresolved_span + .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); + + // 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() + { + self.fcx + .tcx + .sess + .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); + } else { + let note = format!( + "the type is part of the {} because of this {}", + self.kind, yield_data.source + ); + + self.fcx + .need_type_info_err_in_generator(self.kind, span, unresolved_type) + .span_note(yield_data.span, &*note) + .emit(); + } + } else { + // Insert the type into the ordered set. + let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); + + if !self.linted_values.contains(&hir_id) { + check_must_not_suspend_ty( + self.fcx, + ty, + hir_id, + SuspendCheckData { + expr, + source_span, + yield_span: yield_data.span, + plural_len: 1, + ..Default::default() + }, + ); + self.linted_values.insert(hir_id); + } + + self.types.insert(ty::GeneratorInteriorTypeCause { + span: source_span, + ty, + scope_span, + yield_span: yield_data.span, + expr: expr.map(|e| e.hir_id), + }); + } + } else { + debug!( + "no type in expr = {:?}, count = {:?}, span = {:?}", + expr, + self.expr_count, + expr.map(|e| e.span) + ); + if let Some((unresolved_type, unresolved_type_span)) = + self.fcx.unresolved_type_vars(&ty) + { + debug!( + "remained unresolved_type = {:?}, unresolved_type_span: {:?}", + unresolved_type, unresolved_type_span + ); + self.prev_unresolved_span = unresolved_type_span; + } + } + } +} + +pub fn resolve_interior<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + def_id: DefId, + body_id: hir::BodyId, + interior: Ty<'tcx>, + kind: hir::GeneratorKind, +) { + let body = fcx.tcx.hir().body(body_id); + let typeck_results = fcx.inh.typeck_results.borrow(); + let mut visitor = InteriorVisitor { + fcx, + types: FxIndexSet::default(), + region_scope_tree: fcx.tcx.region_scope_tree(def_id), + rvalue_scopes: &typeck_results.rvalue_scopes, + expr_count: 0, + kind, + prev_unresolved_span: None, + linted_values: <_>::default(), + drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), + }; + intravisit::walk_body(&mut visitor, body); + + // Check that we visited the same amount of expressions as the RegionResolutionVisitor + let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap(); + assert_eq!(region_expr_count, visitor.expr_count); + + // The types are already kept in insertion order. + let types = visitor.types; + + // The types in the generator interior contain lifetimes local to the generator itself, + // which should not be exposed outside of the generator. Therefore, we replace these + // lifetimes with existentially-bound lifetimes, which reflect the exact value of the + // lifetimes not being known by users. + // + // These lifetimes are used in auto trait impl checking (for example, + // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), + // so knowledge of the exact relationships between them isn't particularly important. + + debug!("types in generator {:?}, span = {:?}", types, body.value.span); + + let mut counter = 0; + 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; + Some(cause) + } else { + None + } + }) + .collect(); + + // 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 witness = + fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); + + drop(typeck_results); + // Store the generator types and spans into the typeck results for this generator. + fcx.inh.typeck_results.borrow_mut().generator_interior_types = + ty::Binder::bind_with_vars(type_causes, bound_vars); + + debug!( + "types in generator after region replacement {:?}, span = {:?}", + witness, body.value.span + ); + + // Unify the type variable inside the generator with the new witness + match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { + Ok(ok) => fcx.register_infer_ok_obligations(ok), + _ => bug!("failed to relate {interior} and {witness}"), + } +} + +// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in +// librustc_middle/middle/region.rs since `expr_count` is compared against the results +// there. +impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { + fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { + let Arm { guard, pat, body, .. } = arm; + self.visit_pat(pat); + if let Some(ref g) = guard { + { + // If there is a guard, we need to count all variables bound in the pattern as + // borrowed for the entire guard body, regardless of whether they are accessed. + // We do this by walking the pattern bindings and recording `&T` for any `x: T` + // that is bound. + + struct ArmPatCollector<'a, 'b, 'tcx> { + interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>, + scope: Scope, + } + + impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> { + fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { + intravisit::walk_pat(self, pat); + if let PatKind::Binding(_, id, ident, ..) = pat.kind { + let ty = + self.interior_visitor.fcx.typeck_results.borrow().node_type(id); + let tcx = self.interior_visitor.fcx.tcx; + let ty = tcx.mk_ref( + // Use `ReErased` as `resolve_interior` is going to replace all the + // regions anyway. + tcx.mk_region(ty::ReErased), + ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, + ); + self.interior_visitor.record( + ty, + id, + Some(self.scope), + None, + ident.span, + ); + } + } + } + + ArmPatCollector { + interior_visitor: self, + scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node }, + } + .visit_pat(pat); + } + + match g { + Guard::If(ref e) => { + self.visit_expr(e); + } + Guard::IfLet(ref l) => { + self.visit_let_expr(l); + } + } + } + self.visit_expr(body); + } + + fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { + intravisit::walk_pat(self, pat); + + self.expr_count += 1; + + if let PatKind::Binding(..) = pat.kind { + let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); + let ty = self.fcx.typeck_results.borrow().pat_ty(pat); + self.record(ty, pat.hir_id, Some(scope), None, pat.span); + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::Call(callee, args) => match &callee.kind { + ExprKind::Path(qpath) => { + let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id); + match res { + // Direct calls never need to keep the callee `ty::FnDef` + // ZST in a temporary, so skip its type, just in case it + // can significantly complicate the generator type. + Res::Def( + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), + _, + ) => { + // NOTE(eddyb) this assumes a path expression has + // no nested expressions to keep track of. + self.expr_count += 1; + + // Record the rest of the call expression normally. + for arg in *args { + self.visit_expr(arg); + } + } + _ => intravisit::walk_expr(self, expr), + } + } + _ => intravisit::walk_expr(self, expr), + }, + _ => intravisit::walk_expr(self, expr), + } + + self.expr_count += 1; + + debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); + + let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); + + // Typically, the value produced by an expression is consumed by its parent in some way, + // so we only have to check if the parent contains a yield (note that the parent may, for + // example, store the value into a local variable, but then we already consider local + // variables to be live across their scope). + // + // However, in the case of temporary values, we are going to store the value into a + // temporary on the stack that is live for the current temporary scope and then return a + // reference to it. That value may be live across the entire temporary scope. + // + // There's another subtlety: if the type has an observable drop, it must be dropped after + // the yield, even if it's not borrowed or referenced after the yield. Ideally this would + // *only* happen for types with observable drop, not all types which wrap them, but that + // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in + // src/test/ui/generator/drop-tracking-parent-expression.rs. + let scope = if self.drop_ranges.is_borrowed_temporary(expr) + || ty.map_or(true, |ty| { + // Avoid ICEs in needs_drop. + let ty = self.fcx.resolve_vars_if_possible(ty); + let ty = self.fcx.tcx.erase_regions(ty); + if ty.needs_infer() { + self.fcx + .tcx + .sess + .delay_span_bug(expr.span, &format!("inference variables in {ty}")); + true + } else { + ty.needs_drop(self.fcx.tcx, self.fcx.param_env) + } + }) { + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) + } else { + let parent_expr = self + .fcx + .tcx + .hir() + .parent_iter(expr.hir_id) + .find(|(_, node)| matches!(node, hir::Node::Expr(_))) + .map(|(id, _)| id); + debug!("parent_expr: {:?}", parent_expr); + match parent_expr { + Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), + None => { + self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) + } + } + }; + + // If there are adjustments, then record the final type -- + // this is the actual value that is being produced. + if let Some(adjusted_ty) = ty { + self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); + } + + // Also record the unadjusted type (which is the only type if + // there are no adjustments). The reason for this is that the + // unadjusted value is sometimes a "temporary" that would wind + // up in a MIR temporary. + // + // As an example, consider an expression like `vec![].push(x)`. + // Here, the `vec![]` would wind up MIR stored into a + // temporary variable `t` which we can borrow to invoke + // `<Vec<_>>::push(&mut t, x)`. + // + // Note that an expression can have many adjustments, and we + // are just ignoring those intermediate types. This is because + // those intermediate values are always linearly "consumed" by + // the other adjustments, and hence would never be directly + // captured in the MIR. + // + // (Note that this partly relies on the fact that the `Deref` + // traits always return references, which means their content + // can be reborrowed without needing to spill to a temporary. + // If this were not the case, then we could conceivably have + // to create intermediate temporaries.) + // + // The type table might not have information for this expression + // if it is in a malformed scope. (#66387) + if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { + self.record(ty, expr.hir_id, scope, Some(expr), expr.span); + } else { + self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); + } + } +} + +#[derive(Default)] +struct SuspendCheckData<'a, 'tcx> { + expr: Option<&'tcx Expr<'tcx>>, + source_span: Span, + yield_span: Span, + descr_pre: &'a str, + descr_post: &'a str, + plural_len: usize, +} + +// Returns whether it emitted a diagnostic or not +// Note that this fn and the proceeding one are based on the code +// for creating must_use diagnostics +// +// Note that this technique was chosen over things like a `Suspend` marker trait +// as it is simpler and has precedent in the compiler +fn check_must_not_suspend_ty<'tcx>( + fcx: &FnCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + hir_id: HirId, + data: SuspendCheckData<'_, 'tcx>, +) -> bool { + if ty.is_unit() + // FIXME: should this check `is_ty_uninhabited_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) + { + return false; + } + + let plural_suffix = pluralize!(data.plural_len); + + debug!("Checking must_not_suspend for {}", ty); + + match *ty.kind() { + ty::Adt(..) if ty.is_box() => { + let boxed_ty = ty.boxed_ty(); + let descr_pre = &format!("{}boxed ", data.descr_pre); + check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data }) + } + ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data), + // FIXME: support adding the attribute to TAITs + ty::Opaque(def, _) => { + 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) = + predicate.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; + let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix); + if check_must_not_suspend_def( + fcx.tcx, + def_id, + hir_id, + SuspendCheckData { descr_pre, ..data }, + ) { + has_emitted = true; + break; + } + } + } + has_emitted + } + ty::Dynamic(binder, _, _) => { + let mut has_emitted = false; + for predicate in binder.iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + let def_id = trait_ref.def_id; + let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post); + if check_must_not_suspend_def( + fcx.tcx, + def_id, + hir_id, + SuspendCheckData { descr_post, ..data }, + ) { + has_emitted = true; + break; + } + } + } + has_emitted + } + ty::Tuple(fields) => { + let mut has_emitted = false; + let comps = match data.expr.map(|e| &e.kind) { + Some(hir::ExprKind::Tup(comps)) => { + debug_assert_eq!(comps.len(), fields.len()); + Some(comps) + } + _ => None, + }; + for (i, ty) in fields.iter().enumerate() { + let descr_post = &format!(" in tuple element {i}"); + let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span); + if check_must_not_suspend_ty( + fcx, + ty, + hir_id, + SuspendCheckData { + descr_post, + expr: comps.and_then(|comps| comps.get(i)), + source_span: span, + ..data + }, + ) { + has_emitted = true; + } + } + has_emitted + } + ty::Array(ty, len) => { + let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); + check_must_not_suspend_ty( + fcx, + ty, + hir_id, + SuspendCheckData { + descr_pre, + plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize + + 1, + ..data + }, + ) + } + // If drop tracking is enabled, we want to look through references, since the referrent + // may not be considered live across the await point. + ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => { + let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); + check_must_not_suspend_ty(fcx, ty, hir_id, SuspendCheckData { descr_pre, ..data }) + } + _ => false, + } +} + +fn check_must_not_suspend_def( + tcx: TyCtxt<'_>, + def_id: DefId, + hir_id: HirId, + data: SuspendCheckData<'_, '_>, +) -> bool { + if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::MUST_NOT_SUSPEND, + hir_id, + data.source_span, + DelayDm(|| { + format!( + "{}`{}`{} held across a suspend point, but should not be", + data.descr_pre, + tcx.def_path_str(def_id), + data.descr_post, + ) + }), + |lint| { + // add span pointing to the offending yield/await + lint.span_label(data.yield_span, "the value is held across this suspend point"); + + // Add optional reason note + if let Some(note) = attr.value_str() { + // FIXME(guswynn): consider formatting this better + lint.span_note(data.source_span, note.as_str()); + } + + // Add some quick suggestions on what to do + // FIXME: can `drop` work as a suggestion here as well? + lint.span_help( + data.source_span, + "consider using a block (`{ ... }`) \ + to shrink the value's scope, ending before the suspend point", + ); + + lint + }, + ); + + true + } else { + false + } +} diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs new file mode 100644 index 000000000..0fb7651b3 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -0,0 +1,213 @@ +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 std::cell::RefCell; +use std::ops::Deref; + +/// Closures defined within the function. For example: +/// ```ignore (illustrative) +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// ``` +/// Here, the function `foo()` and the closure passed to +/// `bar()` will each have their own `FnCtxt`, but they will +/// share the inherited fields. +pub struct Inherited<'tcx> { + pub(super) infcx: InferCtxt<'tcx>, + + pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>, + + pub(super) locals: RefCell<HirIdMap<super::LocalTy<'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. + 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. + pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>, + + pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, + + pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, hir::HirId)>>, + + pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>, + + pub(super) deferred_generator_interiors: + RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, + + pub(super) body_id: Option<hir::BodyId>, + + /// Whenever we introduce an adjustment from `!` into a type variable, + /// we record that type variable here. This is later used to inform + /// fallback. See the `fallback` module for details. + pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>, +} + +impl<'tcx> Deref for Inherited<'tcx> { + type Target = InferCtxt<'tcx>; + fn deref(&self) -> &Self::Target { + &self.infcx + } +} + +/// A temporary returned by `Inherited::build(...)`. This is necessary +/// for multiple `InferCtxt` to share the same `typeck_results` +/// without using `Rc` or something similar. +pub struct InheritedBuilder<'tcx> { + infcx: infer::InferCtxtBuilder<'tcx>, + def_id: LocalDefId, + typeck_results: RefCell<ty::TypeckResults<'tcx>>, +} + +impl<'tcx> Inherited<'tcx> { + pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; + + InheritedBuilder { + 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 + }) + })), + def_id, + typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), + } + } +} + +impl<'tcx> InheritedBuilder<'tcx> { + pub fn enter<F, R>(mut self, f: F) -> R + where + F: FnOnce(&Inherited<'tcx>) -> R, + { + let def_id = self.def_id; + f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results)) + } +} + +impl<'tcx> Inherited<'tcx> { + fn new( + infcx: InferCtxt<'tcx>, + def_id: LocalDefId, + typeck_results: RefCell<ty::TypeckResults<'tcx>>, + ) -> Self { + let tcx = infcx.tcx; + let body_id = tcx.hir().maybe_body_owned_by(def_id); + + Inherited { + typeck_results, + infcx, + fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)), + locals: RefCell::new(Default::default()), + deferred_sized_obligations: RefCell::new(Vec::new()), + deferred_call_resolutions: RefCell::new(Default::default()), + deferred_cast_checks: RefCell::new(Vec::new()), + deferred_transmute_checks: RefCell::new(Vec::new()), + deferred_asm_checks: RefCell::new(Vec::new()), + deferred_generator_interiors: RefCell::new(Vec::new()), + diverging_type_vars: RefCell::new(Default::default()), + body_id, + } + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { + if obligation.has_escaping_bound_vars() { + span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); + } + self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); + } + + pub(super) fn register_predicates<I>(&self, obligations: I) + where + I: IntoIterator<Item = traits::PredicateObligation<'tcx>>, + { + for obligation in obligations { + self.register_predicate(obligation); + } + } + + pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { + 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 new file mode 100644 index 000000000..9812d96fc --- /dev/null +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -0,0 +1,108 @@ +use hir::HirId; +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_target::abi::{Pointer, VariantIdx}; + +use super::FnCtxt; + +/// If the type is `Option<T>`, it will return `T`, otherwise +/// the type itself. Works on most `Option`-like types. +fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty::Adt(def, substs) = *ty.kind() else { return ty }; + + if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() { + let data_idx; + + let one = VariantIdx::new(1); + let zero = VariantIdx::new(0); + + if def.variant(zero).fields.is_empty() { + data_idx = one; + } else if def.variant(one).fields.is_empty() { + data_idx = zero; + } else { + return ty; + } + + if def.variant(data_idx).fields.len() == 1 { + return def.variant(data_idx).fields[0].ty(tcx, substs); + } + } + + ty +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { + let tcx = self.tcx; + let span = tcx.hir().span(hir_id); + let normalize = |ty| { + let ty = self.resolve_vars_if_possible(ty); + self.tcx.normalize_erasing_regions(self.param_env, ty) + }; + let from = normalize(from); + let to = normalize(to); + trace!(?from, ?to); + + // Transmutes that are only changing lifetimes are always ok. + if from == to { + return; + } + + let skel = |ty| SizeSkeleton::compute(ty, tcx, self.param_env); + let sk_from = skel(from); + let sk_to = skel(to); + trace!(?sk_from, ?sk_to); + + // Check for same size using the skeletons. + if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { + if sk_from.same_size(sk_to) { + return; + } + + // Special-case transmuting from `typeof(function)` and + // `Option<typeof(function)>` to present a clearer error. + let from = unpack_option_like(tcx, from); + if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&tcx) { + struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type") + .note(&format!("source type: {from}")) + .note(&format!("target type: {to}")) + .help("cast with `as` to a pointer instead") + .emit(); + return; + } + } + + // Try to display a sensible error with as much information as possible. + let skeleton_string = |ty: Ty<'tcx>, sk| match sk { + Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), + Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), + Err(LayoutError::Unknown(bad)) => { + if bad == ty { + "this type does not have a fixed size".to_owned() + } else { + format!("size can vary because of {bad}") + } + } + Err(err) => err.to_string(), + }; + + let mut err = struct_span_err!( + tcx.sess, + span, + E0512, + "cannot transmute between types of different sizes, \ + or dependently-sized types" + ); + if from == to { + err.note(&format!("`{from}` does not have a fixed size")); + } else { + err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) + .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); + } + err.emit(); + } +} diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs new file mode 100644 index 000000000..959c54866 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -0,0 +1,507 @@ +#![feature(if_let_guard)] +#![feature(let_chains)] +#![feature(try_blocks)] +#![feature(never_type)] +#![feature(min_specialization)] +#![feature(control_flow_enum)] +#![feature(drain_filter)] +#![allow(rustc::potential_query_instability)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate tracing; + +#[macro_use] +extern crate rustc_middle; + +mod _match; +mod autoderef; +mod callee; +// Used by clippy; +pub mod cast; +mod check; +mod closure; +mod coercion; +mod demand; +mod diverges; +mod errors; +mod expectation; +mod expr; +// Used by clippy; +pub mod expr_use_visitor; +mod fallback; +mod fn_ctxt; +mod gather_locals; +mod generator_interior; +mod inherited; +mod intrinsicck; +mod mem_categorization; +mod method; +mod op; +mod pat; +mod place_op; +mod rvalue_scopes; +mod upvar; +mod writeback; + +pub use diverges::Diverges; +pub use expectation::Expectation; +pub use fn_ctxt::*; +pub use inherited::{Inherited, InheritedBuilder}; + +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_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{HirIdMap, Node}; +use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::check::check_abi; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::traits; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::config; +use rustc_session::Session; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::Span; + +#[macro_export] +macro_rules! type_error_struct { + ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ + let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*); + + if $typ.references_error() { + err.downgrade_to_delayed_bug(); + } + + err + }) +} + +/// The type of a local binding, including the revealed type for anon types. +#[derive(Copy, Clone, Debug)] +pub struct LocalTy<'tcx> { + decl_ty: Ty<'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`. +/// +/// If this function returns `Some`, then `typeck_results(def_id)` will +/// succeed; if it returns `None`, then `typeck_results(def_id)` may or +/// may not succeed. In some cases where this function returns `None` +/// (notably closures), `typeck_results(def_id)` would wind up +/// redirecting to the owning function. +fn primary_body_of( + tcx: TyCtxt<'_>, + id: hir::HirId, +) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { + Some((body, Some(ty), None)) + } + hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), + _ => None, + }, + Node::TraitItem(item) => match item.kind { + hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + Some((body, None, Some(sig))) + } + _ => None, + }, + Node::ImplItem(item) => match item.kind { + hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), + hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), + _ => None, + }, + Node::AnonConst(constant) => Some((constant.body, None, None)), + _ => None, + } +} + +fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.has_typeck_results(typeck_root_def_id); + } + + if let Some(def_id) = def_id.as_local() { + let id = tcx.hir().local_def_id_to_hir_id(def_id); + primary_body_of(tcx, id).is_some() + } else { + false + } +} + +fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDefId> { + &*tcx.typeck(def_id).used_trait_imports +} + +fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { + tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); +} + +fn typeck_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + (did, param_did): (LocalDefId, DefId), +) -> &ty::TypeckResults<'tcx> { + let fallback = move || tcx.type_of(param_did); + typeck_with_fallback(tcx, did, fallback) +} + +fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + if let Some(param_did) = tcx.opt_const_param_of(def_id) { + tcx.typeck_const_arg((def_id, param_did)) + } else { + let fallback = move || tcx.type_of(def_id.to_def_id()); + typeck_with_fallback(tcx, def_id, fallback) + } +} + +/// Used only to get `TypeckResults` for type inference during error recovery. +/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. +fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { + let fallback = move || { + let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); + tcx.ty_error_with_message(span, "diagnostic only typeck table used") + }; + typeck_with_fallback(tcx, def_id, fallback) +} + +fn typeck_with_fallback<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + fallback: impl Fn() -> Ty<'tcx> + 'tcx, +) -> &'tcx ty::TypeckResults<'tcx> { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + if typeck_root_def_id != def_id { + return tcx.typeck(typeck_root_def_id); + } + + let id = tcx.hir().local_def_id_to_hir_id(def_id); + let span = tcx.hir().span(id); + + // Figure out what primary body this item has. + let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { + span_bug!(span, "can't type-check body of {:?}", def_id); + }); + let body = tcx.hir().body(body_id); + + 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 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) + }; + + check_abi(tcx, id, span, fn_sig.abi()); + + // 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 + } 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)), + _ => None, + }) + .unwrap_or_else(|| match tcx.hir().get(id) { + Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::ConstBlock(ref anon_const), + .. + }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }), + Node::Ty(&hir::Ty { + kind: hir::TyKind::Typeof(ref anon_const), .. + }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }), + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) + | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { + let operand_ty = asm + .operands + .iter() + .filter_map(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } + if anon_const.hir_id == id => + { + // Inline assembly constants must be integers. + Some(fcx.next_int_var()) + } + hir::InlineAsmOperand::SymFn { anon_const } + if anon_const.hir_id == id => + { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + })) + } + _ => None, + }) + .next(); + operand_ty.unwrap_or_else(fallback) + } + _ => fallback(), + }, + _ => fallback(), + }); + + let expected_type = fcx.normalize_associated_types_in(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). + GatherLocalsVisitor::new(&fcx).visit_body(body); + + 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(); + + // 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, |_| {}); + + // Closure and generator analysis may run after fallback + // because they don't constrain other type variables. + // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) + let prev_constness = fcx.param_env.constness(); + fcx.param_env = fcx.param_env.without_const(); + fcx.closure_analyze(body); + fcx.param_env = fcx.param_env.with_constness(prev_constness); + assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + // Before the generator analysis, temporary scopes shall be marked to provide more + // precise information on types to be captured. + fcx.resolve_rvalue_scopes(def_id.to_def_id()); + fcx.resolve_generator_interiors(def_id.to_def_id()); + + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + let ty = fcx.normalize_ty(span, ty); + fcx.require_type_is_sized(ty, span, code); + } + + fcx.select_all_obligations_or_error(); + + if !fcx.infcx.is_tainted_by_errors() { + fcx.check_transmutes(); + } + + fcx.check_asms(); + + fcx.infcx.skip_region_resolution(); + + fcx.resolve_type_vars_in_body(body) + }); + + // Consistency check our TypeckResults instance can hold all ItemLocalIds + // it will need to hold. + assert_eq!(typeck_results.hir_owner, id.owner); + + typeck_results +} + +/// When `check_fn` is invoked on a generator (i.e., a body that +/// includes yield), it returns back some information about the yield +/// points. +struct GeneratorTypes<'tcx> { + /// Type of generator argument / values returned by `yield`. + resume_ty: Ty<'tcx>, + + /// Type of value that is yielded. + yield_ty: Ty<'tcx>, + + /// Types that are captured (see `GeneratorInterior` for more). + interior: Ty<'tcx>, + + /// Indicates if the generator is movable or static (immovable). + movability: hir::Movability, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Needs { + MutPlace, + None, +} + +impl Needs { + fn maybe_mut_place(m: hir::Mutability) -> Self { + match m { + hir::Mutability::Mut => Needs::MutPlace, + hir::Mutability::Not => Needs::None, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum PlaceOp { + Deref, + Index, +} + +pub struct BreakableCtxt<'tcx> { + may_break: bool, + + // this is `null` for loops where break with a value is illegal, + // such as `while`, `for`, and `while let` + coerce: Option<DynamicCoerceMany<'tcx>>, +} + +pub struct EnclosingBreakables<'tcx> { + stack: Vec<BreakableCtxt<'tcx>>, + by_id: HirIdMap<usize>, +} + +impl<'tcx> EnclosingBreakables<'tcx> { + fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { + self.opt_find_breakable(target_id).unwrap_or_else(|| { + bug!("could not find enclosing breakable with id {}", target_id); + }) + } + + fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { + match self.by_id.get(&target_id) { + Some(ix) => Some(&mut self.stack[*ix]), + None => None, + } + } +} + +fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { + struct_span_err!( + tcx.sess, + span, + E0533, + "expected unit struct, unit variant or constant, found {} `{}`", + res.descr(), + rustc_hir_pretty::qpath_to_string(qpath), + ) + .emit(); +} + +/// Controls whether the arguments are tupled. This is used for the call +/// operator. +/// +/// Tupling means that all call-side arguments are packed into a tuple and +/// passed as a single parameter. For example, if tupling is enabled, this +/// function: +/// ``` +/// fn f(x: (isize, isize)) {} +/// ``` +/// Can be called as: +/// ```ignore UNSOLVED (can this be done in user code?) +/// # fn f(x: (isize, isize)) {} +/// f(1, 2); +/// ``` +/// Instead of: +/// ``` +/// # fn f(x: (isize, isize)) {} +/// f((1, 2)); +/// ``` +#[derive(Clone, Eq, PartialEq)] +enum TupleArgumentsFlag { + DontTupleArguments, + TupleArguments, +} + +fn fatally_break_rust(sess: &Session) { + let handler = sess.diagnostic(); + handler.span_bug_no_panic( + MultiSpan::new(), + "It looks like you're trying to break rust; would you like some ICE?", + ); + handler.note_without_error("the compiler expectedly panicked. this is a feature."); + handler.note_without_error( + "we would appreciate a joke overview: \ + https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", + ); + handler.note_without_error(&format!( + "rustc {} running on {}", + option_env!("CFG_VERSION").unwrap_or("unknown_version"), + config::host_triple(), + )); +} + +fn has_expected_num_generic_args<'tcx>( + tcx: TyCtxt<'tcx>, + trait_did: Option<DefId>, + expected: usize, +) -> bool { + trait_did.map_or(true, |trait_did| { + let generics = tcx.generics_of(trait_did); + generics.count() == expected + if generics.has_self { 1 } else { 0 } + }) +} + +pub fn provide(providers: &mut Providers) { + method::provide(providers); + *providers = Providers { + typeck_item_bodies, + typeck_const_arg, + typeck, + diagnostic_only_typeck, + has_typeck_results, + used_trait_imports, + ..*providers + }; +} diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index ced919f66..362f1c343 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -92,7 +92,7 @@ impl HirNode for hir::Pat<'_> { #[derive(Clone)] pub(crate) struct MemCategorizationContext<'a, 'tcx> { pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, @@ -103,7 +103,7 @@ pub(crate) type McResult<T> = Result<T, ()>; impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Creates a `MemCategorizationContext`. pub(crate) fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -184,7 +184,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// modes #42640) may look like `Some(x)` but in fact have /// implicit deref patterns attached (e.g., it is really /// `&Some(x)`). In that case, we return the "outermost" type - /// (e.g., `&Option<T>). + /// (e.g., `&Option<T>`). pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { // Check for implicit `&` types wrapping the pattern; note // that these are never attached to binding patterns, so @@ -265,6 +265,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) } + #[instrument(level = "debug", skip(self, previous))] fn cat_expr_adjusted_with<F>( &self, expr: &hir::Expr<'_>, @@ -274,7 +275,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>, { - debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); let target = self.resolve_vars_if_possible(adjustment.target); match adjustment.kind { adjustment::Adjust::Deref(overloaded) => { @@ -292,13 +292,15 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { + | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::DynStar => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) } } } + #[instrument(level = "debug", skip(self))] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, @@ -387,6 +389,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self, span))] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -394,8 +397,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { expr_ty: Ty<'tcx>, res: Res, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); - match res { Res::Def( DefKind::Ctor(..) @@ -475,13 +476,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ret } + #[instrument(level = "debug", skip(self))] fn cat_overloaded_place( &self, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); - // Reconstruct the output assuming it's a reference with the // same region and mutability as the receiver. This holds for // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. @@ -497,13 +497,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } + #[instrument(level = "debug", skip(self, node))] fn cat_deref( &self, node: &impl HirNode, base_place: PlaceWithHirId<'tcx>, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_deref: base_place={:?}", base_place); - let base_curr_ty = base_place.place.ty(); let deref_ty = match base_curr_ty.builtin_deref(true) { Some(mt) => mt.ty, @@ -562,7 +561,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfCtor(..) - | Res::SelfTy { .. } => { + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } => { // Structs and Unions have only have one variant. Ok(VariantIdx::new(0)) } diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 2c89b63ae..be4ea9986 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,16 +1,16 @@ use super::{probe, MethodCallee}; -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use crate::check::{callee, FnCtxt}; +use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{self, Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; @@ -491,7 +491,19 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // so we just call `predicates_for_generics` directly to avoid redoing work. // `self.add_required_obligations(self.span, def_id, &all_substs);` for obligation in traits::predicates_for_generics( - traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)), + |idx, span| { + let code = if span.is_dummy() { + ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx) + } else { + ObligationCauseCode::ExprBindingObligation( + def_id, + span, + self.call_expr.hir_id, + idx, + ) + }; + traits::ObligationCause::new(self.span, self.body_id, code) + }, self.param_env, method_predicates, ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs new file mode 100644 index 000000000..a1278edef --- /dev/null +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -0,0 +1,625 @@ +//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html + +mod confirm; +mod prelude2021; +pub mod probe; +mod suggest; + +pub use self::suggest::SelfSource; +pub use self::MethodError::*; + +use crate::{Expectation, FnCtxt}; +use rustc_data_structures::sync::Lrc; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace}; +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_span::symbol::Ident; +use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; + +use self::probe::{IsSuggestion, ProbeScope}; + +pub fn provide(providers: &mut ty::query::Providers) { + probe::provide(providers); +} + +#[derive(Clone, Copy, Debug)] +pub struct MethodCallee<'tcx> { + /// Impl method ID, for inherent methods, or trait method ID, otherwise. + pub def_id: DefId, + pub substs: SubstsRef<'tcx>, + + /// Instantiated method signature, i.e., it has been + /// substituted, normalized, and has had late-bound + /// lifetimes replaced with inference variables. + pub sig: ty::FnSig<'tcx>, +} + +#[derive(Debug)] +pub enum MethodError<'tcx> { + // Did not find an applicable method, but we did find various near-misses that may work. + NoMatch(NoMatchData<'tcx>), + + // Multiple methods might apply. + Ambiguity(Vec<CandidateSource>), + + // Found an applicable method, but it is not visible. The third argument contains a list of + // 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. + IllegalSizedBound(Vec<DefId>, bool, Span), + + // Found a match, but the return type is wrong + BadReturnType, +} + +// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which +// could lead to matches if satisfied, and a list of not-in-scope traits which may work. +#[derive(Debug)] +pub struct NoMatchData<'tcx> { + pub static_candidates: Vec<CandidateSource>, + pub unsatisfied_predicates: + Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, + pub out_of_scope_traits: Vec<DefId>, + pub lev_candidate: Option<ty::AssocItem>, + pub mode: probe::Mode, +} + +// A pared down enum describing just the places from which a method +// candidate can arise. Used for error reporting only. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CandidateSource { + Impl(DefId), + Trait(DefId /* trait id */), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Determines whether the type `self_ty` supports a method name `method_name` or not. + #[instrument(level = "debug", skip(self))] + pub fn method_exists( + &self, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr_id: hir::HirId, + allow_private: bool, + ) -> bool { + let mode = probe::Mode::MethodCall; + match self.probe_for_name( + method_name.span, + mode, + method_name, + IsSuggestion(false), + self_ty, + call_expr_id, + ProbeScope::TraitsInScope, + ) { + Ok(..) => true, + Err(NoMatch(..)) => false, + Err(Ambiguity(..)) => true, + Err(PrivateMatch(..)) => allow_private, + Err(IllegalSizedBound(..)) => true, + Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), + } + } + + /// Adds a suggestion to call the given method to the provided diagnostic. + #[instrument(level = "debug", skip(self, err, call_expr))] + pub(crate) fn suggest_method_call( + &self, + err: &mut Diagnostic, + msg: &str, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &hir::Expr<'_>, + span: Option<Span>, + ) { + let params = self + .probe_for_name( + method_name.span, + probe::Mode::MethodCall, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + ProbeScope::TraitsInScope, + ) + .map(|pick| { + let sig = self.tcx.fn_sig(pick.item.def_id); + sig.inputs().skip_binder().len().saturating_sub(1) + }) + .unwrap_or(0); + + // Account for `foo.bar<T>`; + let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi(); + let (suggestion, applicability) = ( + format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")), + if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, + ); + + err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); + } + + /// Performs method lookup. If lookup is successful, it will return the callee + /// and store an appropriate adjustment for the self-expr. In some cases it may + /// report an error (e.g., invoking the `drop` method). + /// + /// # Arguments + /// + /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) + /// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`) + /// * `span`: the span for the method call + /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`) + /// * `self_expr`: the self expression (`foo`) + /// * `args`: the expressions of the arguments (`a, b + 1, ...`) + #[instrument(level = "debug", skip(self))] + pub fn lookup_method( + &self, + self_ty: Ty<'tcx>, + segment: &hir::PathSegment<'_>, + span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'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.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); + + for import_id in &pick.import_ids { + debug!("used_trait_import: {:?}", import_id); + Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports) + .unwrap() + .insert(*import_id); + } + + self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None); + + let result = + self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); + debug!("result = {:?}", result); + + if let Some(span) = result.illegal_sized_bound { + let mut needs_mut = false; + if let ty::Ref(region, t_type, mutability) = self_ty.kind() { + let trait_type = self + .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 => { + needs_mut = true; + } + _ => {} + } + } + + // 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)); + + return Err(IllegalSizedBound(candidates, needs_mut, span)); + } + + Ok(result.callee) + } + + #[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, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + scope, + ) + } + + pub(super) fn obligation_for_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) + { + // Construct a trait-reference `self_ty : Trait<input_tys>` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_types) = opt_input_types { + return input_types[param.index as usize - 1].into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + ( + traits::Obligation::misc( + span, + self.body_id, + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + + pub(super) fn obligation_for_op_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option<Ty<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) + { + // Construct a trait-reference `self_ty : Trait<input_tys>` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_type) = opt_input_type { + return input_type.into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); + + ( + traits::Obligation::new( + traits::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, + }, + ), + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + + /// `lookup_method_in_trait` is used for overloaded operators. + /// It does a very narrow slice of what the normal probe/confirm path does. + /// In particular, it doesn't really do any probing: it simply constructs + /// an obligation for a particular trait with the given self type and checks + /// whether that trait is implemented. + #[instrument(level = "debug", skip(self, span))] + pub(super) fn lookup_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + let (obligation, substs) = + self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + None, + false, + ) + } + + pub(super) fn lookup_op_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option<Ty<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + let (obligation, substs) = self.obligation_for_op_method( + span, + trait_def_id, + self_ty, + opt_input_type, + opt_input_expr, + expected, + ); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + opt_input_expr, + true, + ) + } + + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + fn construct_obligation_for_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + obligation: traits::PredicateObligation<'tcx>, + substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + is_op: bool, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + debug!(?obligation); + + // Now we want to know if this can be matched + if !self.predicate_may_hold(&obligation) { + debug!("--> Cannot match obligation"); + // Cannot be matched, no such method resolution is possible. + return None; + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = self.tcx; + let Some(method_item) = self.associated_value(trait_def_id, m_name) else { + tcx.sess.delay_span_bug( + span, + "operator trait does not have corresponding operator method", + ); + return None; + }; + let def_id = method_item.def_id; + let generics = tcx.generics_of(def_id); + assert_eq!(generics.params.len(), 0); + + debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); + let mut obligations = vec![]; + + // Instantiate late-bound regions and substitute the trait + // parameters into the method type to get the actual method type. + // + // N.B., instantiate late-bound regions first so that + // `instantiate_type_scheme` can normalize associated types that + // may reference those regions. + let fn_sig = tcx.bound_fn_sig(def_id); + 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) + } else { + self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + }; + let fn_sig = { + obligations.extend(o); + value + }; + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + // + // Note that as the method comes from a trait, it should not have + // 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 bounds = { + obligations.extend(o); + value + }; + + 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(), + self.param_env, + bounds, + )); + + // Also add an obligation for the method type being well-formed. + let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig)); + debug!( + "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", + method_ty, obligation + ); + obligations.push(traits::Obligation::new( + cause, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), + )); + + let callee = MethodCallee { def_id, substs, sig: fn_sig }; + + debug!("callee = {:?}", callee); + + Some(InferOk { obligations, value: callee }) + } + + /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If + /// lookup is successful, it will return the type of definition and the [`DefId`] of the found + /// function definition. + /// + /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls + /// + /// # Arguments + /// + /// Given a function call like `Foo::bar::<T1,...Tn>(...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`) + /// * `method_name`: the identifier of the function within the container type (`bar`) + /// * `self_ty`: the type to search within (`Foo`) + /// * `self_ty_span` the span for the type being searched within (span of `Foo`) + /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call + #[instrument(level = "debug", skip(self), ret)] + pub fn resolve_fully_qualified_call( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + self_ty_span: Span, + expr_id: hir::HirId, + ) -> Result<(DefKind, DefId), MethodError<'tcx>> { + let tcx = self.tcx; + + // Check if we have an enum variant. + if let ty::Adt(adt_def, _) = self_ty.kind() { + if adt_def.is_enum() { + let variant_def = adt_def + .variants() + .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, + )); + } + } + } + + let pick = self.probe_for_name( + span, + probe::Mode::Path, + method_name, + IsSuggestion(false), + self_ty, + expr_id, + ProbeScope::TraitsInScope, + )?; + + self.lint_fully_qualified_call_from_2018( + span, + method_name, + self_ty, + self_ty_span, + expr_id, + &pick, + ); + + debug!(?pick); + { + let mut typeck_results = self.typeck_results.borrow_mut(); + let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); + for import_id in pick.import_ids { + debug!(used_trait_import=?import_id); + used_trait_imports.insert(import_id); + } + } + + let def_kind = pick.item.kind.as_def_kind(); + tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); + Ok((def_kind, pick.item.def_id)) + } + + /// Finds item with name `item_name` defined in impl/trait `def_id` + /// and return it, or `None`, if no such item was defined there. + pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> { + self.tcx + .associated_items(def_id) + .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) + .copied() + } +} diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 7c68d9304..3c98a2aa3 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -1,3 +1,7 @@ +use crate::{ + method::probe::{self, Pick}, + FnCtxt, +}; use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; @@ -12,11 +16,6 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use crate::check::{ - method::probe::{self, Pick}, - FnCtxt, -}; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_dot_call_from_2018( &self, @@ -82,14 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = self_expr.span; - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { @@ -133,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } else { @@ -143,6 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( @@ -151,16 +147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick.item.container_id(self.tcx), ); - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); if precise { let args = args .iter() - .skip(1) .map(|arg| { let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); format!( @@ -203,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } @@ -258,15 +248,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.params.len() <= trait_generics.has_self as usize { + self.tcx.struct_span_lint_hir( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + ), + |lint| { + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(self.tcx); + let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = self.tcx.generics_of(container_id); + + let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize + { trait_path } else { let counts = trait_generics.own_counts(); @@ -283,44 +281,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - let mut lint = lint.build(&format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.params.is_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::<Vec<_>>() - .join(", ") - ); + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let Adt(def, _) = self_ty.kind() { + let generics = self.tcx.generics_of(def.did()); + if !generics.params.is_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain( + std::iter::repeat("_").take(counts.types + counts.consts) + ) + .collect::<Vec<_>>() + .join(", ") + ); + } } } - } - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - - lint.emit(); - }); + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index efe15fec7..28aa2302f 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,14 +3,12 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::check::FnCtxt; use crate::errors::MethodCallOnUnknownType; -use crate::hir::def::DefKind; -use crate::hir::def_id::DefId; - +use crate::FnCtxt; 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}; @@ -19,10 +17,11 @@ 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::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; +use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, @@ -253,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self, candidate_filter))] pub fn probe_for_return_type( &self, span: Span, @@ -261,11 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Ty<'tcx>, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec<ty::AssocItem> { - debug!( - "probe(self_ty={:?}, return_type={}, scope_expr_id={})", - self_ty, return_type, scope_expr_id - ); let method_names = self .probe_op( span, @@ -276,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, scope_expr_id, ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), + |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) .unwrap_or_default(); method_names @@ -299,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, span: Span, @@ -310,10 +306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope_expr_id: hir::HirId, scope: ProbeScope, ) -> PickResult<'tcx> { - debug!( - "probe(self_ty={:?}, item_name={}, scope_expr_id={})", - self_ty, item_name, scope_expr_id - ); self.probe_op( span, mode, @@ -417,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.build("type annotations needed").emit(); - }, + "type annotations needed", + |lint| lint, ); } } else { @@ -481,69 +472,65 @@ fn method_autoderef_steps<'tcx>( ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { - 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 reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars.clone(), - ty, - ), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - - let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), - }), - ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - - steps.push(CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - infcx.tcx.mk_slice(*elem_ty), - ), - autoderefs: dereferences, - // this could be from an unsafe deref if we had - // a *mut/const [T; N] - from_unsafe_deref: reached_raw_pointer, - unsize: true, - }); - - None + 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 reached_raw_pointer = false; + let mut steps: Vec<_> = autoderef + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + }; + if let ty::RawPtr(_) = ty.kind() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; } - _ => None, - }; - - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + step + }) + .collect(); + + let final_ty = autoderef.final_ty(true); + let opt_bad_ty = match final_ty.kind() { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Array(elem_ty, _) => { + let dereferences = steps.len() - 1; + + steps.push(CandidateStep { + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + infcx.tcx.mk_slice(*elem_ty), + ), + autoderefs: dereferences, + // this could be from an unsafe deref if we had + // a *mut/const [T; N] + from_unsafe_deref: reached_raw_pointer, + unsize: true, + }); - MethodAutoderefStepsResult { - steps: tcx.arena.alloc_from_iter(steps), - opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + None } - }) + _ => None, + }; + + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + + MethodAutoderefStepsResult { + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), + reached_recursion_limit: autoderef.reached_recursion_limit(), + } } impl<'a, 'tcx> ProbeContext<'a, 'tcx> { @@ -980,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn candidate_method_names(&self) -> Vec<Ident> { + fn candidate_method_names( + &self, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, + ) -> Vec<Ident> { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates .iter() .chain(&self.extension_candidates) + .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { self.matches_return_type(&candidate.item, None, return_ty) @@ -1366,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { stable_pick: &Pick<'_>, unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { + let def_kind = stable_pick.item.kind.as_def_kind(); self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, self.scope_expr_id, self.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), + ), |lint| { - let def_kind = stable_pick.item.kind.as_def_kind(); - let mut diag = lint.build(&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), - )); match (stable_pick.item.kind, stable_pick.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing // the code from `report_method_error()`. - diag.help(&format!( + lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", self.tcx.def_path_str(stable_pick.item.def_id), @@ -1391,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { let def_id = stable_pick.item.container_id(self.tcx); - diag.span_suggestion( + lint.span_suggestion( self.span, "use the fully qualified path to the associated const", format!( @@ -1407,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } if self.tcx.sess.is_nightly_build() { for (candidate, feature) in unstable_candidates { - diag.help(&format!( + lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, self.tcx.def_path_str(candidate.item.def_id), @@ -1415,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - diag.emit(); + lint }, ); } @@ -1514,8 +1505,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); // Convert the bounds into obligations. - let impl_obligations = - traits::predicates_for_generics(cause, self.param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + move |_, _| cause.clone(), + self.param_env, + impl_bounds, + ); let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) @@ -1700,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - let method_names = pcx.candidate_method_names(); + let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; let applicable_close_candidates: Vec<ty::AssocItem> = method_names .iter() diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c92b93cbc..6c21ed902 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,7 +1,9 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. -use crate::check::FnCtxt; +use crate::errors; +use crate::FnCtxt; +use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -16,12 +18,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, 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::InferCtxtExt as _; +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, @@ -30,8 +32,8 @@ use rustc_trait_selection::traits::{ use std::cmp::Ordering; use std::iter; -use super::probe::{Mode, ProbeScope}; -use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; +use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; +use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -95,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -104,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, - mut sources: Vec<CandidateSource>, + sources: &mut Vec<CandidateSource>, sugg_span: Span| { sources.sort(); sources.dedup(); @@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: static_sources, + static_candidates: mut static_sources, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -270,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_constraining_numerical_ty( + if self.suggest_wrapping_range_with_parens( + tcx, actual, source, span, item_name, &ty_str, + ) || self.suggest_constraining_numerical_ty( tcx, actual, source, span, item_kind, item_name, &ty_str, ) { return None; @@ -363,44 +367,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if self.is_fn_ty(rcvr_ty, span) { - if let SelfSource::MethodCall(expr) = source { - let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - if let Some(local_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields.len(), of)) - } else { - None - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { - let parent_def_id = tcx.parent(*def_id); - Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) - } else { - None - } - } - } else { - None - }; - - // If the function is a tuple constructor, we recommend that they call it - if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields, &mut err); - } else { - // General case - err.span_label( - expr.span, - "this is a function, perhaps you wish to call it", - ); - } - } + if let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { + let call_expr = self + .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, + ProbeScope::AllTraits, + ); + probe.is_ok() + }); } let mut custom_span_label = false; @@ -441,9 +422,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help(&format!("try with `{}::{}`", ty_str, item_name,)); } - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } let mut bound_spans = vec![]; @@ -560,7 +541,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.push((self.tcx.def_span(def.did()), msg)) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _) => { + ty::Dynamic(preds, _, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => bound_spans @@ -877,8 +858,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Avoid crashing. return (None, None); } - let OnUnimplementedNote { message, label, .. } = - self.on_unimplemented_note(trait_ref, &obligation); + let OnUnimplementedNote { message, label, .. } = self + .err_ctxt() + .on_unimplemented_note(trait_ref, &obligation); (message, label) }) .unwrap_or((None, None)) @@ -904,7 +886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { + 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() { @@ -1000,9 +982,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - self.check_for_field_method(&mut err, source, span, actual, item_name); + // Don't suggest (for example) `expr.field.method()` if `expr.method()` + // doesn't exist due to unsatisfied predicates. + if unsatisfied_predicates.is_empty() { + self.check_for_field_method(&mut err, source, span, actual, item_name); + } - self.check_for_unwrap_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, span, actual, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -1017,10 +1003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|args| args.len()), + args.map(|(_, args)| args.len() + 1), source, out_of_scope_traits, &unsatisfied_predicates, + &static_sources, unsatisfied_bounds, ); } @@ -1062,23 +1049,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() { let def_kind = lev_candidate.kind.as_def_kind(); - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); + // Methods are defined within the context of a struct and their first parameter is always self, + // which represents the instance of the struct the method is being called on + // Associated functions don’t take self as a parameter and + // they are not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { + err.span_suggestion( + span, + &format!("there is a method with a similar name",), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } } } + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + return Some(err); } - MethodError::Ambiguity(sources) => { + MethodError::Ambiguity(mut sources) => { let mut err = struct_span_err!( self.sess(), item_name.span, @@ -1087,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, sources, sugg_span); + report_candidates(span, &mut err, &mut sources, sugg_span); err.emit(); } @@ -1150,7 +1152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, expr: &hir::Expr<'_>, item_name: Ident, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, ) -> bool { let tcx = self.tcx; let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { @@ -1165,7 +1167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }); if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module(self.body_id).to_def_id(); + let scope = tcx.parent_module(self.body_id); let is_accessible = field.vis.is_accessible_from(scope, tcx); if is_accessible { @@ -1204,6 +1206,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// Suggest possible range with adding parentheses, for example: + /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. + fn suggest_wrapping_range_with_parens( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'tcx>, + span: Span, + item_name: Ident, + ty_str: &str, + ) -> bool { + if let SelfSource::MethodCall(expr) = source { + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, + }; + + if !span_included { + continue; + } + + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + 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), + range_ty, + expr.hir_id, + ProbeScope::AllTraits, + ); + if pick.is_ok() { + let range_span = parent_expr.span.with_hi(expr.span.hi()); + tcx.sess.emit_err(errors::MissingParentheseInRange { + span, + ty_str: ty_str.to_string(), + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), + }); + return true; + } + } + } + } + false + } + fn suggest_constraining_numerical_ty( &self, tcx: TyCtxt<'tcx>, @@ -1266,7 +1351,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a floating point literal that ends with '.', // get rid of it to stop this from becoming a member access. let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( lit.span, &format!( @@ -1282,7 +1366,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // local binding if let hir::def::Res::Local(hir_id) = path.res { let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = @@ -1292,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { concrete_type, ); - match (filename, parent_node, snippet) { + match (filename, parent_node) { ( FileName::Real(_), Node::Local(hir::Local { @@ -1300,14 +1383,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty, .. }), - Ok(ref snippet), ) => { + let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi()); err.span_suggestion( // account for `let x: _ = 42;` - // ^^^^ - span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + // ^^^ + type_span, &msg, - format!("{}: {}", snippet, concrete_type), + format!(": {concrete_type}"), Applicability::MaybeIncorrect, ); } @@ -1327,55 +1410,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_for_field_method( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, item_name: Ident, ) { if let SelfSource::MethodCall(expr) = source - && let Some((fields, substs)) = self.get_field_candidates(span, actual) + && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() + && let Some((fields, substs)) = + self.get_field_candidates_considering_privacy(span, actual, mod_id) { let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - for candidate_field in fields.iter() { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::AllTraits, - ) - .is_ok() - }, - candidate_field, - substs, - vec![], - self.tcx.parent_module(expr.hir_id).to_def_id(), - ) { - let field_path_str = field_path + + let lang_items = self.tcx.lang_items(); + let never_mention_traits = [ + lang_items.clone_trait(), + lang_items.deref_trait(), + lang_items.deref_mut_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + self.tcx.get_diagnostic_item(sym::AsMut), + self.tcx.get_diagnostic_item(sym::Borrow), + self.tcx.get_diagnostic_item(sym::BorrowMut), + ]; + let candidate_fields: Vec<_> = fields + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .map_or(false, |pick| { + !never_mention_traits + .iter() + .flatten() + .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) + }) + }, + candidate_field, + substs, + vec![], + mod_id, + ) + }) + .map(|field_path| { + field_path .iter() .map(|id| id.name.to_ident_string()) .collect::<Vec<String>>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); - - err.span_suggestion_verbose( - item_name.span.shrink_to_lo(), - "one of the expressions' fields has a method of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } + .join(".") + }) + .collect(); + + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + item_name.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a method of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); } } } - fn check_for_unwrap_self( + fn check_for_inner_self( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, @@ -1386,81 +1496,168 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; - if !kind.is_enum() { - return; - } + match kind.adt_kind() { + ty::AdtKind::Enum => { + let matching_variants: Vec<_> = kind + .variants() + .iter() + .flat_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if self.resolve_vars_if_possible(field_ty).is_ty_var() { + return None; + } - let matching_variants: Vec<_> = kind - .variants() - .iter() - .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; - let field_ty = field.ty(tcx, substs); + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .ok() + .map(|pick| (variant, field, pick)) + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; - // Skip `_`, since that'll just lead to ambiguity. - if self.resolve_vars_if_possible(field_ty).is_ty_var() { - return None; + match &matching_variants[..] { + [(_, field, pick)] => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + let (article, kind, variant, question) = + if tcx.is_diagnostic_item(sym::Result, kind.did()) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if tcx.is_diagnostic_item(sym::Option, kind.did()) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), + "?", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), + ".expect(\"REASON\")", + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} } - - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - .ok() - .map(|pick| (variant, field, pick)) - }) - .collect(); - - let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false } - }; + // Target wrapper types - types that wrap or pretend to wrap another type, + // perhaps this inner type is meant to be called? + ty::AdtKind::Struct | ty::AdtKind::Union => { + 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; }; - match &matching_variants[..] { - [(_, field, pick)] => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - let (article, kind, variant, question) = - if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { - ("a", "Result", "Err", ret_ty_matches(sym::Result)) - } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { - ("an", "Option", "None", ret_ty_matches(sym::Option)) - } else { - return; + let name = self.ty_to_value_string(actual); + let inner_id = kind.did(); + let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) = + pick.autoref_or_ptr_adjustment + { + Some(mutbl) + } else { + None + }; + + if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { + err.help("use `with` or `try_with` to access thread local storage"); + } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { + err.help(format!( + "if this `{name}` has been initialized, \ + use one of the `assume_init` methods to access the inner value" + )); + } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) { + let (suggestion, borrow_kind, panic_if) = match mutable { + Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"), + Some(Mutability::Mut) => { + (".borrow_mut()", "mutably borrow", "any borrows exist") + } + None => return, }; - if question { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "use the `?` operator to extract the `{self_ty}` value, propagating \ - {article} `{kind}::{variant}` value to the caller" + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + panicking if {panic_if}" ), - "?", - Applicability::MachineApplicable, + suggestion, + Applicability::MaybeIncorrect, ); - } else { + } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ - panicking if the value is {article} `{kind}::{variant}`" + "use `.lock().unwrap()` to borrow the `{ty}`, \ + blocking the current thread until it can be acquired" ), - ".expect(\"REASON\")", - Applicability::HasPlaceholders, + ".lock().unwrap()", + Applicability::MaybeIncorrect, ); - } + } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) { + let (suggestion, borrow_kind) = match mutable { + Some(Mutability::Not) => (".read().unwrap()", "borrow"), + Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"), + None => return, + }; + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + blocking the current thread until it can be acquired" + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + return; + }; + + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{ty}`"), + ); } - // FIXME(compiler-errors): Support suggestions for other matching enum variants - _ => {} } } @@ -1631,6 +1828,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_for_deref_method( + &self, + err: &mut Diagnostic, + self_source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + ) { + 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), + deref_ty, + ty.hir_id, + ProbeScope::TraitsInScope, + ) { + if deref_ty.is_suggestable(self.tcx, true) + // If this method receives `&self`, then the provided + // argument _should_ coerce, so it's valid to suggest + // just changing the path. + && pick.item.fn_has_self_parameter + && let Some(self_ty) = + self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) + && self_ty.is_ref() + { + let suggested_path = match deref_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Str + | ty::Projection(_) + | ty::Param(_) => format!("{deref_ty}"), + _ => format!("<{deref_ty}>"), + }; + err.span_suggestion_verbose( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + suggested_path, + Applicability::MaybeIncorrect, + ); + } else { + err.span_note( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + ); + } + return; + } + } + } + /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { @@ -1763,6 +2016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>, )], + static_candidates: &[CandidateSource], unsatisfied_bounds: bool, ) { let mut alt_rcvr_sugg = false; @@ -1877,6 +2131,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => true, }) .filter(|info| { + // Static candidates are already implemented, and known not to work + // Do not suggest them again + static_candidates.iter().all(|sc| match *sc { + CandidateSource::Trait(def_id) => def_id != info.def_id, + CandidateSource::Impl(def_id) => { + self.tcx.trait_id_of_impl(def_id) != Some(info.def_id) + } + }) + }) + .filter(|info| { // We approximate the coherence rules to only suggest // traits that are legal to implement by requiring that // either the type or trait is local. Multi-dispatch means @@ -1999,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner).unwrap(); + let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); let (sp, mut introducer) = if let Some(span) = ast_generics.bounds_span_for_suggestions(def_id) { @@ -2158,6 +2422,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` + /// FIXME: currently not working for suggesting `map_or_else`, see #102408 + pub(crate) fn suggest_else_fn_with_closure( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + + if !self.can_coerce(output, expected) { + return false; + } + + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && + let hir::ExprKind::MethodCall( + hir::PathSegment { ident: method_name, .. }, + self_expr, + args, + .., + ) = call_expr.kind && + let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) { + let new_name = Ident { + name: Symbol::intern(&format!("{}_else", method_name.as_str())), + span: method_name.span, + }; + let probe = self.lookup_probe( + expr.span, + new_name, + self_ty, + self_expr, + ProbeScope::TraitsInScope, + ); + + // check the method arguments number + if let Ok(pick) = probe && + let fn_sig = self.tcx.fn_sig(pick.item.def_id) && + let fn_args = fn_sig.skip_binder().inputs() && + fn_args.len() == args.len() + 1 { + err.span_suggestion_verbose( + method_name.span.shrink_to_hi(), + &format!("try calling `{}` instead", new_name.name.as_str()), + "_else", + Applicability::MaybeIncorrect, + ); + return true; + } + } + false + } + /// Checks whether there is a local type somewhere in the chain of /// autoderefs of `rcvr_ty`. fn type_derefs_to_local( @@ -2232,7 +2550,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { fn print_disambiguation_help<'tcx>( item_name: Ident, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, err: &mut Diagnostic, trait_name: String, rcvr_ty: Ty<'_>, @@ -2244,7 +2562,7 @@ fn print_disambiguation_help<'tcx>( fn_has_self_parameter: bool, ) { let mut applicability = Applicability::MachineApplicable; - let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { + let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { let args = format!( "({}{})", if rcvr_ty.is_region_ptr() { @@ -2252,7 +2570,8 @@ fn print_disambiguation_help<'tcx>( } else { "" }, - args.iter() + std::iter::once(receiver) + .chain(args.iter()) .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { applicability = Applicability::HasPlaceholders; "_".to_owned() diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs new file mode 100644 index 000000000..895739976 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -0,0 +1,994 @@ +//! Code related to processing overloaded binary and unary operators. + +use super::method::MethodCallee; +use super::{has_expected_num_generic_args, FnCtxt}; +use crate::Expectation; +use rustc_ast as ast; +use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; +use rustc_hir as hir; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::ObligationCauseCode; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; +use rustc_session::errors::ExprParenthesesNeeded; +use rustc_span::source_map::Spanned; +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_type_ir::sty::TyKind::*; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Checks a `a <op>= b` + pub fn check_binop_assign( + &self, + expr: &'tcx hir::Expr<'tcx>, + op: hir::BinOp, + lhs: &'tcx hir::Expr<'tcx>, + rhs: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); + + let ty = + if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { + self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); + self.tcx.mk_unit() + } else { + return_ty + }; + + self.check_lhs_assignable(lhs, "E0067", op.span, |err| { + if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { + if self + .lookup_op_method( + lhs_deref_ty, + Some(rhs_ty), + Some(rhs), + Op::Binary(op, IsAssign::Yes), + expected, + ) + .is_ok() + { + // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have + // emitted a better suggestion during error handling in check_overloaded_binop. + if self + .lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs), + Op::Binary(op, IsAssign::Yes), + expected, + ) + .is_err() + { + err.downgrade_to_delayed_bug(); + } else { + // Otherwise, it's valid to suggest dereferencing the LHS here. + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing the left-hand side of this operation", + "*", + Applicability::MaybeIncorrect, + ); + } + } + } + }); + + ty + } + + /// Checks a potentially overloaded binary operator. + pub fn check_binop( + &self, + expr: &'tcx hir::Expr<'tcx>, + op: hir::BinOp, + lhs_expr: &'tcx hir::Expr<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + + debug!( + "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})", + expr.hir_id, expr, op, lhs_expr, rhs_expr + ); + + match BinOpCategory::from(op) { + BinOpCategory::Shortcircuit => { + // && and || are a simple case. + self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); + let lhs_diverges = self.diverges.get(); + self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); + + // Depending on the LHS' value, the RHS can never execute. + self.diverges.set(lhs_diverges); + + tcx.types.bool + } + _ => { + // Otherwise, we always treat operators as if they are + // overloaded. This is the way to be most flexible w/r/t + // types that get inferred. + let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( + expr, + lhs_expr, + rhs_expr, + op, + IsAssign::No, + expected, + ); + + // Supply type inference hints if relevant. Probably these + // hints should be enforced during select as part of the + // `consider_unification_despite_ambiguity` routine, but this + // more convenient for now. + // + // The basic idea is to help type inference by taking + // advantage of things we know about how the impls for + // scalar types are arranged. This is important in a + // scenario like `1_u32 << 2`, because it lets us quickly + // deduce that the result type should be `u32`, even + // though we don't know yet what type 2 has and hence + // can't pin this down to a specific impl. + if !lhs_ty.is_ty_var() + && !rhs_ty.is_ty_var() + && is_builtin_binop(lhs_ty, rhs_ty, op) + { + let builtin_return_ty = self.enforce_builtin_binop_types( + lhs_expr.span, + lhs_ty, + rhs_expr.span, + rhs_ty, + op, + ); + self.demand_suptype(expr.span, builtin_return_ty, return_ty); + } + + return_ty + } + } + } + + fn enforce_builtin_binop_types( + &self, + lhs_span: Span, + lhs_ty: Ty<'tcx>, + rhs_span: Span, + rhs_ty: Ty<'tcx>, + op: hir::BinOp, + ) -> Ty<'tcx> { + debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); + + // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. + // (See https://github.com/rust-lang/rust/issues/57447.) + let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); + + let tcx = self.tcx; + match BinOpCategory::from(op) { + BinOpCategory::Shortcircuit => { + self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); + self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); + tcx.types.bool + } + + BinOpCategory::Shift => { + // result type is same as LHS always + lhs_ty + } + + BinOpCategory::Math | BinOpCategory::Bitwise => { + // both LHS and RHS and result will have the same type + self.demand_suptype(rhs_span, lhs_ty, rhs_ty); + lhs_ty + } + + BinOpCategory::Comparison => { + // both LHS and RHS and result will have the same type + self.demand_suptype(rhs_span, lhs_ty, rhs_ty); + tcx.types.bool + } + } + } + + fn check_overloaded_binop( + &self, + expr: &'tcx hir::Expr<'tcx>, + lhs_expr: &'tcx hir::Expr<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + op: hir::BinOp, + is_assign: IsAssign, + expected: Expectation<'tcx>, + ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { + debug!( + "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", + expr.hir_id, op, is_assign + ); + + let lhs_ty = match is_assign { + IsAssign::No => { + // Find a suitable supertype of the LHS expression's type, by coercing to + // a type variable, to pass as the `Self` to the trait, avoiding invariant + // trait matching creating lifetime constraints that are too strict. + // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result + // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. + let lhs_ty = self.check_expr(lhs_expr); + let fresh_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: lhs_expr.span, + }); + self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) + } + IsAssign::Yes => { + // rust-lang/rust#52126: We have to use strict + // equivalence on the LHS of an assign-op like `+=`; + // overwritten or mutably-borrowed places cannot be + // coerced to a supertype. + self.check_expr(lhs_expr) + } + }; + let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); + + // N.B., as we have not yet type-checked the RHS, we don't have the + // type at hand. Make a variable to represent it. The whole reason + // for this indirection is so that, below, we can check the expr + // using this variable as the expected type, which sometimes lets + // us do better coercions than we would be able to do otherwise, + // particularly for things like `String + &String`. + let rhs_ty_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: rhs_expr.span, + }); + + let result = self.lookup_op_method( + lhs_ty, + Some(rhs_ty_var), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ); + + // see `NB` above + let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); + let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); + + let return_ty = match result { + Ok(method) => { + 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 autoref = Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), + target: method.sig.inputs()[0], + }; + self.apply_adjustments(lhs_expr, vec![autoref]); + } + } + 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, + }, + }; + let autoref = Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), + target: method.sig.inputs()[1], + }; + // HACK(eddyb) Bypass checks due to reborrows being in + // some cases applied on the RHS, on top of which we need + // to autoref, which is not allowed by apply_adjustments. + // self.apply_adjustments(rhs_expr, vec![autoref]); + self.typeck_results + .borrow_mut() + .adjustments_mut() + .entry(rhs_expr.hir_id) + .or_default() + .push(autoref); + } + } + self.write_method_call(expr.hir_id, method); + + method.sig.output() + } + // error types are considered "builtin" + Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), + Err(errors) => { + let (_, trait_def_id) = + lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); + let missing_trait = trait_def_id + .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); + let (mut err, output_def_id) = match is_assign { + IsAssign::Yes => { + let mut err = struct_span_err!( + self.tcx.sess, + expr.span, + E0368, + "binary assignment operation `{}=` cannot be applied to type `{}`", + op.node.as_str(), + lhs_ty, + ); + err.span_label( + lhs_expr.span, + format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), + ); + self.note_unmet_impls_on_type(&mut err, errors); + (err, None) + } + IsAssign::No => { + let message = match op.node { + hir::BinOpKind::Add => { + format!("cannot add `{rhs_ty}` to `{lhs_ty}`") + } + hir::BinOpKind::Sub => { + format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`") + } + hir::BinOpKind::Mul => { + format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::Div => { + format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::Rem => { + format!("cannot mod `{lhs_ty}` by `{rhs_ty}`") + } + hir::BinOpKind::BitAnd => { + format!("no implementation for `{lhs_ty} & {rhs_ty}`") + } + hir::BinOpKind::BitXor => { + format!("no implementation for `{lhs_ty} ^ {rhs_ty}`") + } + hir::BinOpKind::BitOr => { + format!("no implementation for `{lhs_ty} | {rhs_ty}`") + } + hir::BinOpKind::Shl => { + format!("no implementation for `{lhs_ty} << {rhs_ty}`") + } + hir::BinOpKind::Shr => { + format!("no implementation for `{lhs_ty} >> {rhs_ty}`") + } + _ => format!( + "binary operation `{}` cannot be applied to type `{}`", + op.node.as_str(), + lhs_ty + ), + }; + let output_def_id = trait_def_id.and_then(|def_id| { + self.tcx + .associated_item_def_ids(def_id) + .iter() + .find(|item_def_id| { + self.tcx.associated_item(*item_def_id).name == sym::Output + }) + .cloned() + }); + let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); + if !lhs_expr.span.eq(&rhs_expr.span) { + err.span_label(lhs_expr.span, lhs_ty.to_string()); + err.span_label(rhs_expr.span, rhs_ty.to_string()); + } + self.note_unmet_impls_on_type(&mut err, errors); + (err, output_def_id) + } + }; + + let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { + if self + .lookup_op_method( + lhs_deref_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + { + let msg = &format!( + "`{}{}` can be used on `{}` if you dereference the left-hand side", + op.node.as_str(), + match is_assign { + IsAssign::Yes => "=", + IsAssign::No => "", + }, + lhs_deref_ty, + ); + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_lo(), + msg, + "*", + rustc_errors::Applicability::MachineApplicable, + ); + } + }; + + let is_compatible = |lhs_ty, rhs_ty| { + self.lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + }; + + // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest + // `a += b` => `*a += b` if a is a mut ref. + if !op.span.can_be_used_for_suggestions() { + // Suppress suggestions when lhs and rhs are not in the same span as the error + } else if is_assign == IsAssign::Yes + && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) + { + suggest_deref_binop(lhs_deref_ty); + } else if is_assign == IsAssign::No + && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() + { + if self.type_is_copy_modulo_regions( + self.param_env, + *lhs_deref_ty, + lhs_expr.span, + ) { + suggest_deref_binop(*lhs_deref_ty); + } + } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { + is_compatible(lhs_ty, rhs_ty) + }) || self.suggest_two_fn_call( + &mut err, + rhs_expr, + rhs_ty, + lhs_expr, + lhs_ty, + |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), + ) { + // Cool + } + + if let Some(missing_trait) = missing_trait { + if op.node == hir::BinOpKind::Add + && self.check_str_addition( + lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, + ) + { + // This has nothing here because it means we did string + // concatenation (e.g., "Hello " + "World!"). This means + // we don't want the note in the else clause to be emitted + } else if lhs_ty.has_non_region_param() { + // Look for a TraitPredicate in the Fulfillment errors, + // and use it to generate a suggestion. + // + // Note that lookup_op_method must be called again but + // with a specific rhs_ty instead of a placeholder so + // the resulting predicate generates a more specific + // suggestion for the user. + let errors = self + .lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + expected, + ) + .unwrap_err(); + if !errors.is_empty() { + for error in errors { + if let Some(trait_pred) = + error.obligation.predicate.to_opt_poly_trait_pred() + { + let output_associated_item = match error.obligation.cause.code() + { + ObligationCauseCode::BinOp { + output_ty: Some(output_ty), + .. + } => { + // Make sure that we're attaching `Output = ..` to the right trait predicate + if let Some(output_def_id) = output_def_id + && let Some(trait_def_id) = trait_def_id + && self.tcx.parent(output_def_id) == trait_def_id + { + Some(("Output", *output_ty)) + } else { + None + } + } + _ => None, + }; + + self.err_ctxt().suggest_restricting_param_bound( + &mut err, + trait_pred, + output_associated_item, + self.body_id, + ); + } + } + } else { + // When we know that a missing bound is responsible, we don't show + // this note as it is redundant. + err.note(&format!( + "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" + )); + } + } + } + err.emit(); + self.tcx.ty_error() + } + }; + + (lhs_ty, rhs_ty, return_ty) + } + + /// Provide actionable suggestions when trying to add two strings with incorrect types, + /// like `&str + &str`, `String + String` and `&str + &String`. + /// + /// If this function returns `true` it means a note was printed, so we don't need + /// to print the normal "implementation of `std::ops::Add` might be missing" note + fn check_str_addition( + &self, + lhs_expr: &'tcx hir::Expr<'tcx>, + rhs_expr: &'tcx hir::Expr<'tcx>, + lhs_ty: Ty<'tcx>, + rhs_ty: Ty<'tcx>, + err: &mut Diagnostic, + is_assign: IsAssign, + op: hir::BinOp, + ) -> bool { + let str_concat_note = "string concatenation requires an owned `String` on the left"; + 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 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())) + }; + + match (lhs_ty.kind(), rhs_ty.kind()) { + (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str + if (*l_ty.kind() == Str || is_std_string(l_ty)) + && (*r_ty.kind() == Str + || is_std_string(r_ty) + || matches!( + r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str + )) => + { + if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` + err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); + err.note(str_concat_note); + if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { + err.span_suggestion_verbose( + lhs_expr.span.until(lhs_inner_expr.span), + rm_borrow_msg, + "", + Applicability::MachineApplicable + ); + } else { + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_hi(), + to_owned_msg, + ".to_owned()", + Applicability::MachineApplicable + ); + } + } + true + } + (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` + if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => + { + err.span_label( + op.span, + "`+` cannot be used to concatenate a `&str` with a `String`", + ); + match is_assign { + IsAssign::No => { + let sugg_msg; + let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { + sugg_msg = "remove the borrow on the left and add one on the right"; + (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned()) + } else { + sugg_msg = "create an owned `String` on the left and add a borrow on the right"; + (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned()) + }; + let suggestions = vec![ + lhs_sugg, + (rhs_expr.span.shrink_to_lo(), "&".to_owned()), + ]; + err.multipart_suggestion_verbose( + sugg_msg, + suggestions, + Applicability::MachineApplicable, + ); + } + IsAssign::Yes => { + err.note(str_concat_note); + } + } + true + } + _ => false, + } + } + + pub fn check_user_unop( + &self, + ex: &'tcx hir::Expr<'tcx>, + operand_ty: Ty<'tcx>, + op: hir::UnOp, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + assert!(op.is_by_value()); + match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { + Ok(method) => { + self.write_method_call(ex.hir_id, method); + method.sig.output() + } + Err(errors) => { + let actual = self.resolve_vars_if_possible(operand_ty); + if !actual.references_error() { + let mut err = struct_span_err!( + self.tcx.sess, + ex.span, + E0600, + "cannot apply unary operator `{}` to type `{}`", + op.as_str(), + actual + ); + err.span_label( + ex.span, + format!("cannot apply unary operator `{}`", op.as_str()), + ); + + if operand_ty.has_non_region_param() { + let predicates = errors.iter().filter_map(|error| { + error.obligation.predicate.to_opt_poly_trait_pred() + }); + for pred in predicates { + self.err_ctxt().suggest_restricting_param_bound( + &mut err, + pred, + None, + self.body_id, + ); + } + } + + let sp = self.tcx.sess.source_map().start_point(ex.span); + if let Some(sp) = + self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) + { + // If the previous expression was a block expression, suggest parentheses + // (turning this into a binary subtraction operation instead.) + // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + } else { + match actual.kind() { + Uint(_) if op == hir::UnOp::Neg => { + err.note("unsigned values cannot be negated"); + + if let hir::ExprKind::Unary( + _, + hir::Expr { + kind: + hir::ExprKind::Lit(Spanned { + node: ast::LitKind::Int(1, _), + .. + }), + .. + }, + ) = ex.kind + { + err.span_suggestion( + ex.span, + &format!( + "you may have meant the maximum value of `{actual}`", + ), + format!("{actual}::MAX"), + Applicability::MaybeIncorrect, + ); + } + } + Str | Never | Char | Tuple(_) | Array(_, _) => {} + Ref(_, lty, _) if *lty.kind() == Str => {} + _ => { + self.note_unmet_impls_on_type(&mut err, errors); + } + } + } + err.emit(); + } + self.tcx.ty_error() + } + } + } + + fn lookup_op_method( + &self, + lhs_ty: Ty<'tcx>, + other_ty: Option<Ty<'tcx>>, + other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + op: Op, + expected: Expectation<'tcx>, + ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { + let span = match op { + Op::Binary(op, _) => op.span, + Op::Unary(_, span) => span, + }; + let (opname, trait_did) = lang_item_for_op(self.tcx, op, span); + + debug!( + "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", + lhs_ty, op, opname, trait_did + ); + + // Catches cases like #83893, where a lang item is declared with the + // wrong number of generic arguments. Should have yielded an error + // elsewhere by now, but we have to catch it here so that we do not + // index `other_tys` out of bounds (if the lang item has too many + // generic arguments, `other_tys` is too short). + if !has_expected_num_generic_args( + self.tcx, + trait_did, + match op { + // Binary ops have a generic right-hand side, unary ops don't + Op::Binary(..) => 1, + Op::Unary(..) => 0, + }, + ) { + return Err(vec![]); + } + + let opname = Ident::with_dummy_span(opname); + let method = trait_did.and_then(|trait_did| { + self.lookup_op_method_in_trait( + span, + opname, + trait_did, + lhs_ty, + other_ty, + other_ty_expr, + expected, + ) + }); + + match (method, trait_did) { + (Some(ok), _) => { + let method = self.register_infer_ok_obligations(ok); + self.select_obligations_where_possible(false, |_| {}); + Ok(method) + } + (None, None) => Err(vec![]), + (None, Some(trait_did)) => { + let (obligation, _) = self.obligation_for_op_method( + span, + trait_did, + lhs_ty, + other_ty, + 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)) + } + } + } +} + +fn lang_item_for_op( + tcx: TyCtxt<'_>, + op: Op, + span: Span, +) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) { + let lang = tcx.lang_items(); + if let Op::Binary(op, IsAssign::Yes) = op { + match op.node { + hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), + hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), + hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), + hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), + hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), + hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), + hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), + hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), + hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), + hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), + hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt + | hir::BinOpKind::Eq + | hir::BinOpKind::Ne + | hir::BinOpKind::And + | hir::BinOpKind::Or => { + span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) + } + } + } else if let Op::Binary(op, IsAssign::No) = op { + match op.node { + hir::BinOpKind::Add => (sym::add, lang.add_trait()), + hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), + hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), + hir::BinOpKind::Div => (sym::div, lang.div_trait()), + hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), + hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), + hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), + hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), + hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), + hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), + hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), + hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), + hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), + hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), + hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), + hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), + hir::BinOpKind::And | hir::BinOpKind::Or => { + span_bug!(span, "&& and || are not overloadable") + } + } + } else if let Op::Unary(hir::UnOp::Not, _) = op { + (sym::not, lang.not_trait()) + } else if let Op::Unary(hir::UnOp::Neg, _) = op { + (sym::neg, lang.neg_trait()) + } else { + bug!("lookup_op_method: op not supported: {:?}", op) + } +} + +// Binary operator categories. These categories summarize the behavior +// with respect to the builtin operations supported. +enum BinOpCategory { + /// &&, || -- cannot be overridden + Shortcircuit, + + /// <<, >> -- when shifting a single integer, rhs can be any + /// integer type. For simd, types must match. + Shift, + + /// +, -, etc -- takes equal types, produces same type as input, + /// applicable to ints/floats/simd + Math, + + /// &, |, ^ -- takes equal types, produces same type as input, + /// applicable to ints/floats/simd/bool + Bitwise, + + /// ==, !=, etc -- takes equal types, produces bools, except for simd, + /// which produce the input type + Comparison, +} + +impl BinOpCategory { + fn from(op: hir::BinOp) -> BinOpCategory { + match op.node { + hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, + + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Mul + | hir::BinOpKind::Div + | hir::BinOpKind::Rem => BinOpCategory::Math, + + hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { + BinOpCategory::Bitwise + } + + hir::BinOpKind::Eq + | hir::BinOpKind::Ne + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt => BinOpCategory::Comparison, + + hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, + } + } +} + +/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) +#[derive(Clone, Copy, Debug, PartialEq)] +enum IsAssign { + No, + Yes, +} + +#[derive(Clone, Copy, Debug)] +enum Op { + Binary(hir::BinOp, IsAssign), + Unary(hir::UnOp, Span), +} + +/// Dereferences a single level of immutable referencing. +fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Ref(_, ty, hir::Mutability::Not) => *ty, + _ => ty, + } +} + +/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 +/// + u32, i16x4 == i16x4) and false if these types would have to be +/// overloaded to be legal. There are two reasons that we distinguish +/// builtin operations from overloaded ones (vs trying to drive +/// everything uniformly through the trait system and intrinsics or +/// something like that): +/// +/// 1. Builtin operations can trivially be evaluated in constants. +/// 2. For comparison operators applied to SIMD types the result is +/// not of type `bool`. For example, `i16x4 == i16x4` yields a +/// type like `i16x4`. This means that the overloaded trait +/// `PartialEq` is not applicable. +/// +/// Reason #2 is the killer. I tried for a while to always use +/// overloaded logic and just check the types in constants/codegen after +/// the fact, and it worked fine, except for SIMD types. -nmatsakis +fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { + // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. + // (See https://github.com/rust-lang/rust/issues/57447.) + let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); + + match BinOpCategory::from(op) { + BinOpCategory::Shortcircuit => true, + + BinOpCategory::Shift => { + lhs.references_error() + || rhs.references_error() + || lhs.is_integral() && rhs.is_integral() + } + + BinOpCategory::Math => { + lhs.references_error() + || rhs.references_error() + || lhs.is_integral() && rhs.is_integral() + || lhs.is_floating_point() && rhs.is_floating_point() + } + + BinOpCategory::Bitwise => { + lhs.references_error() + || rhs.references_error() + || lhs.is_integral() && rhs.is_integral() + || lhs.is_floating_point() && rhs.is_floating_point() + || lhs.is_bool() && rhs.is_bool() + } + + BinOpCategory::Comparison => { + lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() + } + } +} + +struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); + +impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.1, + }), + _ => ty.super_fold_with(self), + } + } +} diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 837c32355..ea90da4a6 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,6 +1,5 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_ast as ast; - use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -569,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::Unannotated => def_bm, + hir::BindingAnnotation::NONE => def_bm, _ => BindingMode::convert(ba), }; // ...and store it in a side table: @@ -600,7 +599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { @@ -610,7 +609,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { local_ty } - fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) { + fn check_binding_alt_eq_ty( + &self, + ba: hir::BindingAnnotation, + span: Span, + var_id: HirId, + ty: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) { let var_ty = self.local_ty(span, var_id).decl_ty; if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { let hir = self.tcx.hir(); @@ -628,12 +634,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let pre = if in_match { "in the same arm, " } else { "" }; err.note(&format!("{}a binding must have the same type in all alternatives", pre)); - // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing - // `ref` or `&` to the pattern. + self.suggest_adding_missing_ref_or_removing_ref( + &mut err, + span, + var_ty, + self.resolve_vars_with_obligations(ty), + ba, + ); err.emit(); } } + fn suggest_adding_missing_ref_or_removing_ref( + &self, + err: &mut Diagnostic, + span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ba: hir::BindingAnnotation, + ) { + match (expected.kind(), actual.kind(), ba) { + (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) + if self.can_eq(self.param_env, *inner_ty, actual).is_ok() => + { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider adding `ref`", + "ref ", + Applicability::MaybeIncorrect, + ); + } + (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) + if self.can_eq(self.param_env, expected, *inner_ty).is_ok() => + { + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(4)), + "consider removing `ref`", + "", + Applicability::MaybeIncorrect, + ); + } + _ => (), + } + } + // Precondition: pat is a Ref(_) pattern fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) { let tcx = self.tcx; @@ -882,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { - hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => { + hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), "bind the struct field to a different name instead", @@ -936,7 +980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, subpats: &'tcx [Pat<'tcx>], - ddpos: Option<usize>, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, @@ -1021,7 +1065,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check subpatterns. if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.is_some() + || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some() { let ty::Adt(_, substs) = pat_ty.kind() else { bug!("unexpected pattern type {:?}", pat_ty); @@ -1209,14 +1253,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, elements: &'tcx [Pat<'tcx>], - ddpos: Option<usize>, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); - if ddpos.is_some() { + if ddpos.as_opt_usize().is_some() { // Require known type only when `..` is present. if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() { expected_len = tys.len(); @@ -1352,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .copied() .filter(|(field, _)| { - field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx) && !matches!( tcx.eval_stability(field.did, None, DUMMY_SP, None), EvalResult::Deny { .. } @@ -1745,10 +1789,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(), ); - self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| { - let mut lint = build.build("some fields are not explicitly listed"); + self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( "ensure that all fields are mentioned explicitly by adding the suggested fields", ); @@ -1756,7 +1798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", ty, )); - lint.emit(); + + lint }); } diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 2e0f37eba..ba8cf6926 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,5 +1,5 @@ -use crate::check::method::MethodCallee; -use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp}; +use crate::method::MethodCallee; +use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 22c9e7961..22c9e7961 100644 --- a/compiler/rustc_typeck/src/check/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index dd8f943b9..4dea40829 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -352,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and that the path can be captured with required capture kind (depending on use in closure, /// move closure etc.) /// - /// Returns the set of of adjusted information along with the inferred closure kind and span + /// Returns the set of adjusted information along with the inferred closure kind and span /// associated with the closure kind inference. /// /// Note that we *always* infer a minimal kind, even if @@ -749,10 +749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, closure_head_span, + reasons.migration_message(), |lint| { - let mut diagnostics_builder = lint.build( - &reasons.migration_message(), - ); for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { // Labels all the usage of the captured variable and why they are responsible // for migration being needed @@ -760,13 +758,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { let cause_span = self.tcx.hir().span(*capture_expr_id); - diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", + lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span } => { - diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", self.tcx.hir().name(*var_hir_id), )); } @@ -781,13 +779,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", v = self.tcx.hir().name(*var_hir_id), )); } @@ -800,7 +798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { let var_name = self.tcx.hir().name(*var_hir_id); - diagnostics_builder.span_label(closure_head_span, format!("\ + lint.span_label(closure_head_span, format!("\ in Rust 2018, this closure implements {missing_trait} \ as `{var_name}` implements {missing_trait}, but in Rust 2021, \ this closure will no longer implement {missing_trait} \ @@ -814,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); + lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); let diagnostic_msg = format!( "add a dummy let to cause {} to be fully captured", @@ -857,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We take the indentation from the next non-empty line. let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), &diagnostic_msg, format!("\n{indent}{migration_string};"), @@ -868,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // braces, but with more than just the opening // brace on the first line. We put the `let` // directly after the `{`. - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), &diagnostic_msg, format!(" {migration_string};"), @@ -877,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // This is a closure without braces around the body. // We add braces to add the `let` before the body. - diagnostics_builder.multipart_suggestion( + lint.multipart_suggestion( &diagnostic_msg, vec![ (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), @@ -887,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } else { - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_span, &diagnostic_msg, migration_string, @@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - diagnostics_builder.emit(); + lint }, ); } @@ -1217,7 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine all the reasons of why the root variable should be captured as a result of // auto trait implementation issues - auto_trait_migration_reasons.extend(capture_trait_reasons.clone()); + auto_trait_migration_reasons.extend(capture_trait_reasons.iter().copied()); diagnostics_info.push(MigrationLintNote { captures_info, @@ -2024,6 +2022,10 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, closure_id: hir::HirId, ) -> bool { + if tcx.sess.rust_2021() { + return false; + } + let (level, _) = tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f549807c3..1e26daa9c 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -2,8 +2,7 @@ // unresolved type variables and replaces "ty_var" types with their // substitutions. -use crate::check::FnCtxt; - +use crate::FnCtxt; use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; @@ -16,6 +15,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -192,6 +192,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + // (ouz-a 1005988): Normally `[T] : std::ops::Index<usize>` should be normalized + // into [T] but currently `Where` clause stops the normalization process for it, + // here we compare types of expr and base in a code without `Where` clause they would be equal + // if they are not we don't modify the expr, hence we bypass the ICE + fn is_builtin_index( + &mut self, + typeck_results: &TypeckResults<'tcx>, + e: &hir::Expr<'_>, + base_ty: Ty<'tcx>, + index_ty: Ty<'tcx>, + ) -> bool { + if let Some(elem_ty) = base_ty.builtin_index() { + let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; + let resolved_exp_ty = self.resolve(exp_ty, &e.span); + + elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize + } else { + false + } + } + // Similar to operators, indexing is always assumed to be overloaded // Here, correct cases where an indexing expression can be simplified // to use builtin indexing because the index type is known to be @@ -222,8 +243,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) }); let index_ty = self.fcx.resolve_vars_if_possible(index_ty); + let resolved_base_ty = self.resolve(*base_ty, &base.span); - if base_ty.builtin_index().is_some() && index_ty == self.fcx.tcx.types.usize { + if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { // Remove the method call record typeck_results.type_dependent_defs_mut().remove(e.hir_id); typeck_results.node_substs_mut().remove(e.hir_id); @@ -292,6 +314,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_expr(self, e); } + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + match &p.kind { + hir::GenericParamKind::Lifetime { .. } => { + // Nothing to write back here + } + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); + } + } + } + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); @@ -468,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer.drain(..) { + for mut diag in errors_buffer { self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); } } @@ -503,33 +536,37 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let opaque_types = self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { - let hidden_type = match decl.origin { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) + let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); + let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); + + struct RecursionChecker { + def_id: LocalDefId, + } + impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::Opaque(def_id, _) = *t.kind() { + if def_id == self.def_id.to_def_id() { + return ControlFlow::Break(()); } } - if ty - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() - { - return; - } - Some(ty) + t.super_visit_with(self) } - hir::OpaqueTyOrigin::TyAlias => None, - }; + } + if hidden_type + .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) + .is_break() + { + continue; + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.fcx.infcx.tcx, + true, + decl.origin, + ); + self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } } @@ -667,7 +704,7 @@ impl Locatable for hir::HirId { /// unresolved types and so forth. struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -684,27 +721,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } } - fn report_type_error(&self, t: Ty<'tcx>) { + fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) { if !self.tcx.sess.has_errors().is_some() { self.infcx + .err_ctxt() .emit_inference_failure_err( Some(self.body.id()), self.span.to_span(self.tcx), - t.into(), - E0282, - false, - ) - .emit(); - } - } - - fn report_const_error(&self, c: ty::Const<'tcx>) { - if self.tcx.sess.has_errors().is_none() { - self.infcx - .emit_inference_failure_err( - Some(self.body.id()), - self.span.to_span(self.tcx), - c.into(), + p.into(), E0282, false, ) @@ -749,7 +773,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_type_error(t); + self.report_error(t); self.replaced_with_error = true; self.tcx().ty_error() } @@ -766,7 +790,7 @@ 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_const_error(ct); + self.report_error(ct); self.replaced_with_error = true; self.tcx().const_error(ct.ty()) } |