diff options
Diffstat (limited to 'compiler')
431 files changed, 12201 insertions, 9245 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 9165b4f2df3..197dd7f9c9e 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -186,7 +186,7 @@ pub trait LayoutCalculator { let (present_first, present_second) = { let mut present_variants = variants .iter_enumerated() - .filter_map(|(i, v)| if absent(v) { None } else { Some(i) }); + .filter_map(|(i, v)| if !repr.c() && absent(v) { None } else { Some(i) }); (present_variants.next(), present_variants.next()) }; let present_first = match present_first { @@ -621,7 +621,7 @@ where let discr_type = repr.discr_type(); let bits = Integer::from_attr(dl, discr_type).size().bits(); for (i, mut val) in discriminants { - if variants[i].iter().any(|f| f.abi.is_uninhabited()) { + if !repr.c() && variants[i].iter().any(|f| f.abi.is_uninhabited()) { continue; } if discr_type.is_signed() { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 31c66a56bea..78332d66f03 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1429,7 +1429,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> { /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { index: VariantIdx }, - /// Enum-likes with more than one inhabited variant: each variant comes with + /// Enum-likes with more than one variant: each variant comes with /// a *discriminant* (usually the same as the variant index but the user can /// assign explicit discriminant values). That discriminant is encoded /// as a *tag* on the machine. The layout of each variant is diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4a3ce0e0c30..75c656973f9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -176,6 +176,8 @@ pub enum GenericArgs { AngleBracketed(AngleBracketedArgs), /// The `(A, B)` and `C` in `Foo(A, B) -> C`. Parenthesized(ParenthesizedArgs), + /// `(..)` in return type notation. + ParenthesizedElided(Span), } impl GenericArgs { @@ -187,6 +189,7 @@ impl GenericArgs { match self { AngleBracketed(data) => data.span, Parenthesized(data) => data.span, + ParenthesizedElided(span) => *span, } } } @@ -194,11 +197,11 @@ impl GenericArgs { /// Concrete argument in the sequence of generic args. #[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericArg { - /// `'a` in `Foo<'a>` + /// `'a` in `Foo<'a>`. Lifetime(Lifetime), - /// `Bar` in `Foo<Bar>` + /// `Bar` in `Foo<Bar>`. Type(P<Ty>), - /// `1` in `Foo<1>` + /// `1` in `Foo<1>`. Const(AnonConst), } @@ -352,7 +355,7 @@ pub enum GenericParamKind { ty: P<Ty>, /// Span of the `const` keyword. kw_span: Span, - /// Optional default value for the const generic param + /// Optional default value for the const generic param. default: Option<AnonConst>, }, } @@ -711,6 +714,7 @@ pub enum ByRef { } impl ByRef { + #[must_use] pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { if let ByRef::Yes(old_mutbl) = &mut self { *old_mutbl = cmp::min(*old_mutbl, mutbl); @@ -829,7 +833,7 @@ pub enum PatKind { /// only one rest pattern may occur in the pattern sequences. Rest, - // A never pattern `!` + // A never pattern `!`. Never, /// Parentheses in patterns used for grouping (i.e., `(PAT)`). @@ -1118,9 +1122,9 @@ impl LocalKind { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Arm { pub attrs: AttrVec, - /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }` + /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`. pub pat: P<Pat>, - /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }` + /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`. pub guard: Option<P<Expr>>, /// Match arm body. Omitted if the pattern is a never pattern. pub body: Option<P<Expr>>, @@ -1351,12 +1355,12 @@ pub struct Closure { pub fn_arg_span: Span, } -/// Limit types of a range (inclusive or exclusive) +/// Limit types of a range (inclusive or exclusive). #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)] pub enum RangeLimits { - /// Inclusive at the beginning, exclusive at the end + /// Inclusive at the beginning, exclusive at the end. HalfOpen, - /// Inclusive at the beginning and end + /// Inclusive at the beginning and end. Closed, } @@ -1397,9 +1401,9 @@ pub struct StructExpr { pub enum ExprKind { /// An array (e.g, `[a, b, c, d]`). Array(ThinVec<P<Expr>>), - /// Allow anonymous constants from an inline `const` block + /// Allow anonymous constants from an inline `const` block. ConstBlock(AnonConst), - /// A function call + /// A function call. /// /// The first field resolves to the function itself, /// and the second field is the list of arguments. @@ -1453,8 +1457,11 @@ pub enum ExprKind { /// A block (`'label: { ... }`). Block(P<Block>, Option<Label>), /// An `async` block (`async move { ... }`), - /// or a `gen` block (`gen move { ... }`) - Gen(CaptureBy, P<Block>, GenBlockKind), + /// or a `gen` block (`gen move { ... }`). + /// + /// The span is the "decl", which is the header before the body `{ }` + /// including the `asyng`/`gen` keywords and possibly `move`. + Gen(CaptureBy, P<Block>, GenBlockKind, Span), /// An await expression (`my_future.await`). Span is of await keyword. Await(P<Expr>, Span), @@ -2048,7 +2055,7 @@ impl UintTy { /// * the `A: Bound` in `Trait<A: Bound>` /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`) -/// * the `f(): Bound` in `Trait<f(): Bound>` (feature `return_type_notation`) +/// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`) #[derive(Clone, Encodable, Decodable, Debug)] pub struct AssocItemConstraint { pub id: NodeId, @@ -2150,9 +2157,9 @@ pub enum TyKind { Never, /// A tuple (`(A, B, C, D,...)`). Tup(ThinVec<P<Ty>>), - /// An anonymous struct type i.e. `struct { foo: Type }` + /// An anonymous struct type i.e. `struct { foo: Type }`. AnonStruct(NodeId, ThinVec<FieldDef>), - /// An anonymous union type i.e. `union { bar: Type }` + /// An anonymous union type i.e. `union { bar: Type }`. AnonUnion(NodeId, ThinVec<FieldDef>), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`. @@ -2226,9 +2233,9 @@ pub enum TraitObjectSyntax { #[derive(Clone, Encodable, Decodable, Debug)] pub enum PreciseCapturingArg { - /// Lifetime parameter + /// Lifetime parameter. Lifetime(Lifetime), - /// Type or const parameter + /// Type or const parameter. Arg(Path, NodeId), } @@ -2522,11 +2529,11 @@ pub enum Safety { /// Iterator`. #[derive(Copy, Clone, Encodable, Decodable, Debug)] pub enum CoroutineKind { - /// `async`, which returns an `impl Future` + /// `async`, which returns an `impl Future`. Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - /// `gen`, which returns an `impl Iterator` + /// `gen`, which returns an `impl Iterator`. Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, - /// `async gen`, which returns an `impl AsyncIterator` + /// `async gen`, which returns an `impl AsyncIterator`. AsyncGen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId }, } @@ -2743,7 +2750,7 @@ pub struct Variant { pub data: VariantData, /// Explicit discriminant, e.g., `Foo = 1`. pub disr_expr: Option<AnonConst>, - /// Is a macro placeholder + /// Is a macro placeholder. pub is_placeholder: bool, } @@ -3017,19 +3024,19 @@ impl Item { /// `extern` qualifier on a function item or function type. #[derive(Clone, Copy, Encodable, Decodable, Debug)] pub enum Extern { - /// No explicit extern keyword was used + /// No explicit extern keyword was used. /// - /// E.g. `fn foo() {}` + /// E.g. `fn foo() {}`. None, - /// An explicit extern keyword was used, but with implicit ABI + /// An explicit extern keyword was used, but with implicit ABI. /// - /// E.g. `extern fn foo() {}` + /// E.g. `extern fn foo() {}`. /// - /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`) + /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`). Implicit(Span), - /// An explicit extern keyword was used with an explicit ABI + /// An explicit extern keyword was used with an explicit ABI. /// - /// E.g. `extern "C" fn foo() {}` + /// E.g. `extern "C" fn foo() {}`. Explicit(StrLit, Span), } @@ -3048,13 +3055,13 @@ impl Extern { /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`). #[derive(Clone, Copy, Encodable, Decodable, Debug)] pub struct FnHeader { - /// Whether this is `unsafe`, or has a default safety + /// Whether this is `unsafe`, or has a default safety. pub safety: Safety, /// Whether this is `async`, `gen`, or nothing. pub coroutine_kind: Option<CoroutineKind>, /// The `const` keyword, if any pub constness: Const, - /// The `extern` keyword and corresponding ABI string, if any + /// The `extern` keyword and corresponding ABI string, if any. pub ext: Extern, } @@ -3248,7 +3255,7 @@ pub enum ItemKind { /// /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`. Trait(Box<Trait>), - /// Trait alias + /// Trait alias. /// /// E.g., `trait Foo = Bar + Quux;`. TraitAlias(Generics, GenericBounds), diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 2cf811e9122..7754ca0a0f5 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -10,8 +10,6 @@ use crate::{AssocItem, Expr, ForeignItem, Item, NodeId}; use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use crate::{AttrVec, Attribute, Stmt, StmtKind}; -use rustc_span::Span; - use std::fmt; use std::marker::PhantomData; @@ -91,37 +89,6 @@ impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T { } } -/// A trait for AST nodes having a span. -pub trait HasSpan { - fn span(&self) -> Span; -} - -macro_rules! impl_has_span { - ($($T:ty),+ $(,)?) => { - $( - impl HasSpan for $T { - fn span(&self) -> Span { - self.span - } - } - )+ - }; -} - -impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility); - -impl<T: AstDeref<Target: HasSpan>> HasSpan for T { - fn span(&self) -> Span { - self.ast_deref().span() - } -} - -impl HasSpan for AttrItem { - fn span(&self) -> Span { - self.span() - } -} - /// A trait for AST nodes having (or not having) collected tokens. pub trait HasTokens { fn tokens(&self) -> Option<&LazyAttrTokenStream>; diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 593c78df3cd..088ae9ba441 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -202,14 +202,17 @@ impl Attribute { } } - pub fn tokens(&self) -> TokenStream { + // Named `get_tokens` to distinguish it from the `<Attribute as HasTokens>::tokens` method. + pub fn get_tokens(&self) -> TokenStream { match &self.kind { - AttrKind::Normal(normal) => normal - .tokens - .as_ref() - .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) - .to_attr_token_stream() - .to_tokenstream(), + AttrKind::Normal(normal) => TokenStream::new( + normal + .tokens + .as_ref() + .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) + .to_attr_token_stream() + .to_token_trees(), + ), &AttrKind::DocComment(comment_kind, data) => TokenStream::token_alone( token::DocComment(comment_kind, self.style, data), self.span, diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 7ca950e50e6..846a108091f 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -44,7 +44,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens}; +pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index a81c335aa23..cbf21317f1a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -582,6 +582,7 @@ fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vis: & match generic_args { GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data), GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data), + GenericArgs::ParenthesizedElided(span) => vis.visit_span(span), } } @@ -703,7 +704,7 @@ fn visit_attr_tt<T: MutVisitor>(tt: &mut AttrTokenTree, vis: &mut T) { visit_attr_tts(tts, vis); visit_delim_span(dspan, vis); } - AttrTokenTree::Attributes(AttributesData { attrs, tokens }) => { + AttrTokenTree::AttrsTarget(AttrsTarget { attrs, tokens }) => { visit_attrs(attrs, vis); visit_lazy_tts_opt_mut(Some(tokens), vis); } @@ -1528,8 +1529,9 @@ pub fn noop_visit_expr<T: MutVisitor>( visit_opt(label, |label| vis.visit_label(label)); vis.visit_block(blk); } - ExprKind::Gen(_capture_by, body, _kind) => { + ExprKind::Gen(_capture_by, body, _kind, decl_span) => { vis.visit_block(body); + vis.visit_span(decl_span); } ExprKind::Await(expr, await_kw_span) => { vis.visit_expr(expr); diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index b4ddbe20689..ee068f19332 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,7 +14,7 @@ //! ownership of the original. use crate::ast::{AttrStyle, StmtKind}; -use crate::ast_traits::{HasAttrs, HasSpan, HasTokens}; +use crate::ast_traits::{HasAttrs, HasTokens}; use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use crate::AttrVec; @@ -23,7 +23,6 @@ use rustc_data_structures::sync::{self, Lrc}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::{sym, Span, SpanDecoder, SpanEncoder, Symbol, DUMMY_SP}; -use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; use std::{cmp, fmt, iter}; @@ -171,8 +170,8 @@ pub enum AttrTokenTree { Delimited(DelimSpan, DelimSpacing, Delimiter, AttrTokenStream), /// Stores the attributes for an attribute target, /// along with the tokens for that attribute target. - /// See `AttributesData` for more information - Attributes(AttributesData), + /// See `AttrsTarget` for more information + AttrsTarget(AttrsTarget), } impl AttrTokenStream { @@ -180,42 +179,33 @@ impl AttrTokenStream { AttrTokenStream(Lrc::new(tokens)) } - /// Converts this `AttrTokenStream` to a plain `TokenStream`. - /// During conversion, `AttrTokenTree::Attributes` get 'flattened' + /// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`. + /// During conversion, `AttrTokenTree::AttrsTarget` get 'flattened' /// back to a `TokenStream` of the form `outer_attr attr_target`. /// If there are inner attributes, they are inserted into the proper /// place in the attribute target tokens. - pub fn to_tokenstream(&self) -> TokenStream { - let trees: Vec<_> = self - .0 - .iter() - .flat_map(|tree| match &tree { + pub fn to_token_trees(&self) -> Vec<TokenTree> { + let mut res = Vec::with_capacity(self.0.len()); + for tree in self.0.iter() { + match tree { AttrTokenTree::Token(inner, spacing) => { - smallvec![TokenTree::Token(inner.clone(), *spacing)].into_iter() + res.push(TokenTree::Token(inner.clone(), *spacing)); } AttrTokenTree::Delimited(span, spacing, delim, stream) => { - smallvec![TokenTree::Delimited( + res.push(TokenTree::Delimited( *span, *spacing, *delim, - stream.to_tokenstream() - ),] - .into_iter() + TokenStream::new(stream.to_token_trees()), + )) } - AttrTokenTree::Attributes(data) => { - let idx = data + AttrTokenTree::AttrsTarget(target) => { + let idx = target .attrs .partition_point(|attr| matches!(attr.style, crate::AttrStyle::Outer)); - let (outer_attrs, inner_attrs) = data.attrs.split_at(idx); - - let mut target_tokens: Vec<_> = data - .tokens - .to_attr_token_stream() - .to_tokenstream() - .0 - .iter() - .cloned() - .collect(); + let (outer_attrs, inner_attrs) = target.attrs.split_at(idx); + + let mut target_tokens = target.tokens.to_attr_token_stream().to_token_trees(); if !inner_attrs.is_empty() { let mut found = false; // Check the last two trees (to account for a trailing semi) @@ -237,7 +227,7 @@ impl AttrTokenStream { let mut stream = TokenStream::default(); for inner_attr in inner_attrs { - stream.push_stream(inner_attr.tokens()); + stream.push_stream(inner_attr.get_tokens()); } stream.push_stream(delim_tokens.clone()); *tree = TokenTree::Delimited(*span, *spacing, *delim, stream); @@ -251,17 +241,14 @@ impl AttrTokenStream { "Failed to find trailing delimited group in: {target_tokens:?}" ); } - let mut flat: SmallVec<[_; 1]> = - SmallVec::with_capacity(target_tokens.len() + outer_attrs.len()); for attr in outer_attrs { - flat.extend(attr.tokens().0.iter().cloned()); + res.extend(attr.get_tokens().0.iter().cloned()); } - flat.extend(target_tokens); - flat.into_iter() + res.extend(target_tokens); } - }) - .collect(); - TokenStream::new(trees) + } + } + res } } @@ -275,7 +262,7 @@ impl AttrTokenStream { /// have an `attrs` field containing the `#[cfg(FALSE)]` attr, /// and a `tokens` field storing the (unparsed) tokens `struct Foo {}` #[derive(Clone, Debug, Encodable, Decodable)] -pub struct AttributesData { +pub struct AttrsTarget { /// Attributes, both outer and inner. /// These are stored in the original order that they were parsed in. pub attrs: AttrVec, @@ -409,8 +396,8 @@ impl PartialEq<TokenStream> for TokenStream { } impl TokenStream { - pub fn new(streams: Vec<TokenTree>) -> TokenStream { - TokenStream(Lrc::new(streams)) + pub fn new(tts: Vec<TokenTree>) -> TokenStream { + TokenStream(Lrc::new(tts)) } pub fn is_empty(&self) -> bool { @@ -449,19 +436,19 @@ impl TokenStream { TokenStream::new(vec![TokenTree::token_alone(kind, span)]) } - pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream { + pub fn from_ast(node: &(impl HasAttrs + HasTokens + fmt::Debug)) -> TokenStream { let Some(tokens) = node.tokens() else { - panic!("missing tokens for node at {:?}: {:?}", node.span(), node); + panic!("missing tokens for node: {:?}", node); }; let attrs = node.attrs(); let attr_stream = if attrs.is_empty() { tokens.to_attr_token_stream() } else { - let attr_data = - AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() }; - AttrTokenStream::new(vec![AttrTokenTree::Attributes(attr_data)]) + let target = + AttrsTarget { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() }; + AttrTokenStream::new(vec![AttrTokenTree::AttrsTarget(target)]) }; - attr_stream.to_tokenstream() + TokenStream::new(attr_stream.to_token_trees()) } pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream { @@ -778,6 +765,7 @@ mod size_asserts { static_assert_size!(AttrTokenStream, 8); static_assert_size!(AttrTokenTree, 32); static_assert_size!(LazyAttrTokenStream, 8); + static_assert_size!(Option<LazyAttrTokenStream>, 8); // must be small, used in many AST nodes static_assert_size!(TokenStream, 8); static_assert_size!(TokenTree, 32); // tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 4b2544ac47e..1a80a9ccdbf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -1,7 +1,8 @@ //! Routines the parser and pretty-printer use to classify AST nodes. use crate::ast::ExprKind::*; -use crate::{ast, token::Delimiter}; +use crate::ast::{self, MatchKind}; +use crate::token::Delimiter; /// This classification determines whether various syntactic positions break out /// of parsing the current expression (true) or continue parsing more of the @@ -81,6 +82,82 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { } } +/// Returns whether the leftmost token of the given expression is the label of a +/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`. +/// +/// Such expressions are not allowed as the value of an unlabeled break. +/// +/// ```ignore (illustrative) +/// 'outer: { +/// break 'inner: loop { break 'inner 1 } + 1; // invalid syntax +/// +/// break 'outer 'inner: loop { break 'inner 1 } + 1; // okay +/// +/// break ('inner: loop { break 'inner 1 } + 1); // okay +/// +/// break ('inner: loop { break 'inner 1 }) + 1; // okay +/// } +/// ``` +pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { + loop { + match &expr.kind { + Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => { + return label.is_some(); + } + + Assign(e, _, _) + | AssignOp(_, e, _) + | Await(e, _) + | Binary(_, e, _) + | Call(e, _) + | Cast(e, _) + | Field(e, _) + | Index(e, _, _) + | Match(e, _, MatchKind::Postfix) + | Range(Some(e), _, _) + | Try(e) => { + expr = e; + } + MethodCall(method_call) => { + expr = &method_call.receiver; + } + + AddrOf(..) + | Array(..) + | Become(..) + | Break(..) + | Closure(..) + | ConstBlock(..) + | Continue(..) + | FormatArgs(..) + | Gen(..) + | If(..) + | IncludedBytes(..) + | InlineAsm(..) + | Let(..) + | Lit(..) + | MacCall(..) + | Match(_, _, MatchKind::Prefix) + | OffsetOf(..) + | Paren(..) + | Path(..) + | Range(None, _, _) + | Repeat(..) + | Ret(..) + | Struct(..) + | TryBlock(..) + | Tup(..) + | Type(..) + | Unary(..) + | Underscore + | Yeet(..) + | Yield(..) + | Err(..) + | Dummy => return false, + } + } +} + pub enum TrailingBrace<'a> { /// Trailing brace in a macro call, like the one in `x as *const brace! {}`. /// We will suggest changing the macro call to a different delimiter. @@ -234,6 +311,6 @@ fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> { ast::FnRetTy::Default(_) => None, ast::FnRetTy::Ty(ret) => Some(ret), }, - ast::GenericArgs::AngleBracketed(_) => None, + ast::GenericArgs::AngleBracketed(_) | ast::GenericArgs::ParenthesizedElided(_) => None, } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 0f3c30048ce..f6929057bed 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -609,6 +609,7 @@ where walk_list!(visitor, visit_ty, inputs); try_visit!(visitor.visit_fn_ret_ty(output)); } + GenericArgs::ParenthesizedElided(_span) => {} } V::Result::output() } @@ -1122,7 +1123,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V visit_opt!(visitor, visit_label, opt_label); try_visit!(visitor.visit_block(block)); } - ExprKind::Gen(_capt, body, _kind) => try_visit!(visitor.visit_block(body)), + ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)), ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)), ExprKind::Assign(lhs, rhs, _span) => { try_visit!(visitor.visit_expr(lhs)); diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 58f65f1257f..9ed93d481e7 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -36,10 +36,15 @@ ast_lowering_bad_return_type_notation_inputs = argument types not allowed with return type notation .suggestion = remove the input types +ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..` + .suggestion = add `..` + ast_lowering_bad_return_type_notation_output = return type not allowed with return type notation .suggestion = remove the return type +ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet + ast_lowering_base_expression_double_dot = base expression required after `..` .suggestion = add a base expression here diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 3d4b6a1f033..4c77892a6b7 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -393,6 +393,17 @@ pub enum BadReturnTypeNotation { #[suggestion(code = "", applicability = "maybe-incorrect")] span: Span, }, + #[diag(ast_lowering_bad_return_type_notation_needs_dots)] + NeedsDots { + #[primary_span] + #[suggestion(code = "(..)", applicability = "maybe-incorrect")] + span: Span, + }, + #[diag(ast_lowering_bad_return_type_notation_position)] + Position { + #[primary_span] + span: Span, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 79cff0fbcd2..218fa974022 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -103,7 +103,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, ParenthesizedGenericArgs::Err, ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, // Method calls can't have bound modifiers None, )); @@ -227,7 +226,7 @@ impl<'hir> LoweringContext<'_, 'hir> { *fn_arg_span, ), }, - ExprKind::Gen(capture_clause, block, genblock_kind) => { + ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => { let desugaring_kind = match genblock_kind { GenBlockKind::Async => hir::CoroutineDesugaring::Async, GenBlockKind::Gen => hir::CoroutineDesugaring::Gen, @@ -237,6 +236,7 @@ impl<'hir> LoweringContext<'_, 'hir> { *capture_clause, e.id, None, + *decl_span, e.span, desugaring_kind, hir::CoroutineSource::Block, @@ -616,6 +616,7 @@ impl<'hir> LoweringContext<'_, 'hir> { capture_clause: CaptureBy, closure_node_id: NodeId, return_ty: Option<hir::FnRetTy<'hir>>, + fn_decl_span: Span, span: Span, desugaring_kind: hir::CoroutineDesugaring, coroutine_source: hir::CoroutineSource, @@ -692,7 +693,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bound_generic_params: &[], fn_decl, body, - fn_decl_span: self.lower_span(span), + fn_decl_span: self.lower_span(fn_decl_span), fn_arg_span: None, kind: hir::ClosureKind::Coroutine(coroutine_kind), constness: hir::Constness::NotConst, @@ -1083,6 +1084,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( &inner_decl, |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), + fn_decl_span, body.span, coroutine_kind, hir::CoroutineSource::Closure, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 4c7e8c24d32..0ad23b53566 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -56,7 +56,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, ) { - let mut lctx = LoweringContext::new(self.tcx, self.resolver); + let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); for (def_id, info) in lctx.children { @@ -190,6 +190,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, (ty, body_id)) = self.lower_generics( generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -211,6 +212,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // declaration (decl), not the return types. let coroutine_kind = header.coroutine_kind; let body_id = this.lower_maybe_coroutine_body( + *fn_sig_span, span, hir_id, decl, @@ -220,7 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let itctx = ImplTraitContext::Universal; let (generics, decl) = - this.lower_generics(generics, header.constness, id, itctx, |this| { + this.lower_generics(generics, header.constness, false, id, itctx, |this| { this.lower_fn_decl( decl, id, @@ -264,6 +266,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, ty) = self.lower_generics( &generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| match ty { @@ -292,6 +295,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, variants) = self.lower_generics( generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -306,6 +310,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, struct_def) = self.lower_generics( generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, struct_def), @@ -316,6 +321,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, vdata) = self.lower_generics( generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| this.lower_variant_data(hir_id, vdata), @@ -347,12 +353,9 @@ impl<'hir> LoweringContext<'_, 'hir> { // parent lifetime. let itctx = ImplTraitContext::Universal; let (generics, (trait_ref, lowered_ty)) = - self.lower_generics(ast_generics, *constness, id, itctx, |this| { + self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| { let modifiers = TraitBoundModifiers { - constness: match *constness { - Const::Yes(span) => BoundConstness::Maybe(span), - Const::No => BoundConstness::Never, - }, + constness: BoundConstness::Never, asyncness: BoundAsyncness::Normal, // we don't use this in bound lowering polarity: BoundPolarity::Positive, @@ -388,6 +391,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)), }; hir::ItemKind::Impl(self.arena.alloc(hir::Impl { + constness: self.lower_constness(*constness), safety: self.lower_safety(*safety, hir::Safety::Safe), polarity, defaultness, @@ -399,15 +403,10 @@ impl<'hir> LoweringContext<'_, 'hir> { })) } ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => { - // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible - let constness = attrs - .unwrap_or(&[]) - .iter() - .find(|x| x.has_name(sym::const_trait)) - .map_or(Const::No, |x| Const::Yes(x.span)); let (generics, (safety, items, bounds)) = self.lower_generics( generics, - constness, + Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -428,6 +427,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, bounds) = self.lower_generics( generics, Const::No, + false, id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -608,30 +608,48 @@ impl<'hir> LoweringContext<'_, 'hir> { // This is used to track which lifetimes have already been defined, // and which need to be replicated when lowering an async fn. - let generics = match parent_hir.node().expect_item().kind { + let parent_item = parent_hir.node().expect_item(); + let constness = match parent_item.kind { hir::ItemKind::Impl(impl_) => { self.is_in_trait_impl = impl_.of_trait.is_some(); - &impl_.generics + // N.B. the impl should always lower to methods that have `const host: bool` params if the trait + // is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from + // calling non-const impls are done through associated types. + if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) { + if let Some(local_def) = def_id.as_local() { + match &self.ast_index[local_def] { + AstOwner::Item(ast::Item { attrs, .. }) => attrs + .iter() + .find(|attr| attr.has_name(sym::const_trait)) + .map_or(Const::No, |attr| Const::Yes(attr.span)), + _ => Const::No, + } + } else { + if self.tcx.is_const_trait(def_id) { + // FIXME(effects) span + Const::Yes(self.tcx.def_ident_span(def_id).unwrap()) + } else { + Const::No + } + } + } else { + Const::No + } } - hir::ItemKind::Trait(_, _, generics, _, _) => generics, + hir::ItemKind::Trait(_, _, _, _, _) => parent_hir + .attrs + .get(parent_item.hir_id().local_id) + .iter() + .find(|attr| attr.has_name(sym::const_trait)) + .map_or(Const::No, |attr| Const::Yes(attr.span)), kind => { span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr()) } }; - if self.tcx.features().effects { - self.host_param_id = generics - .params - .iter() - .find(|param| { - matches!(param.kind, hir::GenericParamKind::Const { is_host_effect: true, .. }) - }) - .map(|param| param.def_id); - } - match ctxt { - AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)), - AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)), + AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)), + AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)), } } @@ -647,7 +665,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let fdec = &sig.decl; let itctx = ImplTraitContext::Universal; let (generics, (fn_dec, fn_args)) = - self.lower_generics(generics, Const::No, i.id, itctx, |this| { + self.lower_generics(generics, Const::No, false, i.id, itctx, |this| { ( // Disallow `impl Trait` in foreign items. this.lower_fn_decl( @@ -764,7 +782,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { + fn lower_trait_item( + &mut self, + i: &AssocItem, + trait_constness: Const, + ) -> &'hir hir::TraitItem<'hir> { let hir_id = self.lower_node_id(i.id); self.lower_attrs(hir_id, &i.attrs); let trait_item_def_id = hir_id.expect_owner(); @@ -774,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, kind) = self.lower_generics( generics, Const::No, + false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -794,11 +817,13 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, + trait_constness, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => { let body_id = self.lower_maybe_coroutine_body( + sig.span, i.span, hir_id, &sig.decl, @@ -811,6 +836,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, + trait_constness, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -820,6 +846,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, kind) = self.lower_generics( &generics, Const::No, + false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -892,7 +919,11 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Err(guar)) } - fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { + fn lower_impl_item( + &mut self, + i: &AssocItem, + constness_of_trait: Const, + ) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); @@ -903,6 +934,7 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( generics, Const::No, + false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { @@ -915,6 +947,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { let body_id = self.lower_maybe_coroutine_body( + sig.span, i.span, hir_id, &sig.decl, @@ -927,6 +960,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, sig.header.coroutine_kind, + constness_of_trait, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -937,6 +971,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_generics( &generics, Const::No, + false, i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| match ty { @@ -1111,6 +1146,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// `gen {}` block as appropriate. fn lower_maybe_coroutine_body( &mut self, + fn_decl_span: Span, span: Span, fn_id: hir::HirId, decl: &FnDecl, @@ -1124,6 +1160,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( decl, |this| this.lower_block_expr(body), + fn_decl_span, body.span, coroutine_kind, hir::CoroutineSource::Fn, @@ -1145,6 +1182,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, decl: &FnDecl, lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::Expr<'hir>, + fn_decl_span: Span, body_span: Span, coroutine_kind: CoroutineKind, coroutine_source: hir::CoroutineSource, @@ -1315,13 +1353,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let closure_id = coroutine_kind.closure_id(); - let span = if let FnRetTy::Default(span) = decl.output - && matches!(coroutine_source, rustc_hir::CoroutineSource::Closure) - { - body_span.with_lo(span.lo()) - } else { - body_span - }; let coroutine_expr = self.make_desugared_coroutine_expr( // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1330,7 +1361,8 @@ impl<'hir> LoweringContext<'_, 'hir> { CaptureBy::Ref, closure_id, None, - span, + fn_decl_span, + body_span, desugaring_kind, coroutine_source, mkbody, @@ -1352,15 +1384,18 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, kind: FnDeclKind, coroutine_kind: Option<CoroutineKind>, + parent_constness: Const, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); // Don't pass along the user-provided constness of trait associated functions; we don't want to // synthesize a host effect param for them. We reject `const` on them during AST validation. - let constness = if kind == FnDeclKind::Inherent { sig.header.constness } else { Const::No }; + let constness = + if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness }; let itctx = ImplTraitContext::Universal; - let (generics, decl) = self.lower_generics(generics, constness, id, itctx, |this| { - this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) - }); + let (generics, decl) = + self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| { + this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) + }); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } @@ -1436,6 +1471,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, generics: &Generics, constness: Const, + force_append_constness: bool, parent_node_id: NodeId, itctx: ImplTraitContext, f: impl FnOnce(&mut Self) -> T, @@ -1496,7 +1532,9 @@ impl<'hir> LoweringContext<'_, 'hir> { // if the effects feature is enabled. This needs to be done before we lower where // clauses since where clauses need to bind to the DefId of the host param let host_param_parts = if let Const::Yes(span) = constness - && self.tcx.features().effects + // if this comes from implementing a `const` trait, we must force constness to be appended + // to the impl item, no matter whether effects is enabled. + && (self.tcx.features().effects || force_append_constness) { let span = self.lower_span(span); let param_node_id = self.next_node_id(); @@ -1609,6 +1647,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }), )), )), + // FIXME(effects) we might not need a default. default: Some(self.arena.alloc(hir::AnonConst { def_id: anon_const, hir_id: const_id, @@ -1616,6 +1655,7 @@ impl<'hir> LoweringContext<'_, 'hir> { span, })), is_host_effect: true, + synthetic: true, }, colon_span: None, pure_wrt_drop: false, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0a06304fcec..24748d2d009 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -142,14 +142,19 @@ struct LoweringContext<'a, 'hir> { generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>, host_param_id: Option<LocalDefId>, + ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { - fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { + fn new( + tcx: TyCtxt<'hir>, + resolver: &'a mut ResolverAstLowering, + ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>, + ) -> Self { Self { // Pseudo-globals. tcx, - resolver: resolver, + resolver, arena: tcx.hir_arena, // HirId handling. @@ -185,6 +190,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), generics_def_id_map: Default::default(), host_param_id: None, + ast_index, } } @@ -979,20 +985,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0 } GenericArgs::Parenthesized(data) => { - if data.inputs.is_empty() && matches!(data.output, FnRetTy::Default(..)) { - let parenthesized = if self.tcx.features().return_type_notation { - hir::GenericArgsParentheses::ReturnTypeNotation - } else { - self.emit_bad_parenthesized_trait_in_assoc_ty(data); - hir::GenericArgsParentheses::No - }; - GenericArgsCtor { - args: Default::default(), - constraints: &[], - parenthesized, - span: data.inputs_span, - } - } else if let Some(first_char) = constraint.ident.as_str().chars().next() + if let Some(first_char) = constraint.ident.as_str().chars().next() && first_char.is_ascii_lowercase() { let mut err = if !data.inputs.is_empty() { @@ -1004,7 +997,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: data.inputs_span.shrink_to_hi().to(ty.span), }) } else { - unreachable!("inputs are empty and return type is not provided") + self.dcx().create_err(errors::BadReturnTypeNotation::NeedsDots { + span: data.inputs_span, + }) }; if !self.tcx.features().return_type_notation && self.tcx.sess.is_nightly_build() @@ -1034,6 +1029,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .0 } } + GenericArgs::ParenthesizedElided(span) => GenericArgsCtor { + args: Default::default(), + constraints: &[], + parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation, + span: *span, + }, }; gen_args_ctor.into_generic_args(self) } else { @@ -2135,7 +2136,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param: &GenericParam, source: hir::GenericParamSource, ) -> hir::GenericParam<'hir> { - let (name, kind) = self.lower_generic_param_kind(param, source); + let (name, kind) = self.lower_generic_param_kind( + param, + source, + attr::contains_name(¶m.attrs, sym::rustc_runtime), + ); let hir_id = self.lower_node_id(param.id); self.lower_attrs(hir_id, ¶m.attrs); @@ -2155,6 +2160,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, param: &GenericParam, source: hir::GenericParamSource, + is_host_effect: bool, ) -> (hir::ParamName, hir::GenericParamKind<'hir>) { match ¶m.kind { GenericParamKind::Lifetime => { @@ -2220,7 +2226,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ( hir::ParamName::Plain(self.lower_ident(param.ident)), - hir::GenericParamKind::Const { ty, default, is_host_effect: false }, + hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false }, ) } } @@ -2607,78 +2613,6 @@ struct GenericArgsCtor<'hir> { } impl<'hir> GenericArgsCtor<'hir> { - fn push_constness( - &mut self, - lcx: &mut LoweringContext<'_, 'hir>, - constness: ast::BoundConstness, - ) { - if !lcx.tcx.features().effects { - return; - } - - let (span, body) = match constness { - BoundConstness::Never => return, - BoundConstness::Always(span) => { - let span = lcx.lower_span(span); - - let body = hir::ExprKind::Lit( - lcx.arena.alloc(hir::Lit { node: LitKind::Bool(false), span }), - ); - - (span, body) - } - BoundConstness::Maybe(span) => { - let span = lcx.lower_span(span); - - let Some(host_param_id) = lcx.host_param_id else { - lcx.dcx().span_delayed_bug( - span, - "no host param id for call in const yet no errors reported", - ); - return; - }; - - let hir_id = lcx.next_id(); - let res = Res::Def(DefKind::ConstParam, host_param_id.to_def_id()); - let body = hir::ExprKind::Path(hir::QPath::Resolved( - None, - lcx.arena.alloc(hir::Path { - span, - res, - segments: arena_vec![ - lcx; - hir::PathSegment::new( - Ident { name: sym::host, span }, - hir_id, - res - ) - ], - }), - )); - - (span, body) - } - }; - let body = lcx.lower_body(|lcx| (&[], lcx.expr(span, body))); - - let id = lcx.next_node_id(); - let hir_id = lcx.next_id(); - - let def_id = lcx.create_def( - lcx.current_hir_id_owner.def_id, - id, - kw::Empty, - DefKind::AnonConst, - span, - ); - - lcx.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); - self.args.push(hir::GenericArg::Const(hir::ConstArg { - value: lcx.arena.alloc(hir::AnonConst { def_id, hir_id, body, span }), - is_desugared_from_effects: true, - })) - } - fn is_empty(&self) -> bool { self.args.is_empty() && self.constraints.is_empty() diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 9d38e1e6784..6303584bb78 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,7 +1,8 @@ use crate::ImplTraitPosition; use super::errors::{ - AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, GenericTypeWithParentheses, UseAngleBrackets, + AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation, + GenericTypeWithParentheses, UseAngleBrackets, }; use super::ResolverAstLoweringExt; use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs}; @@ -107,8 +108,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode, parenthesized_generic_args, itctx, - // if this is the last segment, add constness to the trait path - if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None }, bound_modifier_allowed_features.clone(), ) }, @@ -165,7 +164,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ParenthesizedGenericArgs::Err, itctx, None, - None, )); let qpath = hir::QPath::TypeRelative(ty, hir_segment); @@ -208,7 +206,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ParenthesizedGenericArgs::Err, ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, - None, ) })), span: self.lower_span(p.span), @@ -222,7 +219,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode: ParamMode, parenthesized_generic_args: ParenthesizedGenericArgs, itctx: ImplTraitContext, - constness: Option<ast::BoundConstness>, // Additional features ungated with a bound modifier like `async`. // This is passed down to the implicit associated type binding in // parenthesized bounds. @@ -276,6 +272,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } }, + GenericArgs::ParenthesizedElided(span) => { + self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span }); + ( + GenericArgsCtor { + args: Default::default(), + constraints: &[], + parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation, + span: *span, + }, + false, + ) + } } } else { ( @@ -289,10 +297,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }; - if let Some(constness) = constness { - generic_args.push_constness(self, constness); - } - let has_lifetimes = generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ba4b6130b60..dd0d904c52c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1,10 +1,20 @@ -// Validate AST before lowering it to HIR. -// -// This pass is supposed to catch things that fit into AST data structures, -// but not permitted by the language. It runs after expansion when AST is frozen, -// so it can check for erroneous constructions produced by syntax extensions. -// This pass is supposed to perform only simple checks not requiring name resolution -// or type checking or some other kind of complex analysis. +//! Validate AST before lowering it to HIR. +//! +//! This pass intends to check that the constructed AST is *syntactically valid* to allow the rest +//! of the compiler to assume that the AST is valid. These checks cannot be performed during parsing +//! because attribute macros are allowed to accept certain pieces of invalid syntax such as a +//! function without body outside of a trait definition: +//! +//! ```ignore (illustrative) +//! #[my_attribute] +//! mod foo { +//! fn missing_body(); +//! } +//! ``` +//! +//! These checks are run post-expansion, after AST is frozen, to be able to check for erroneous +//! constructions produced by proc macros. This pass is only intended for simple checks that do not +//! require name resolution or type checking, or other kinds of complex analysis. use itertools::{Either, Itertools}; use rustc_ast::ptr::P; @@ -459,13 +469,18 @@ impl<'a> AstValidator<'a> { fn check_item_safety(&self, span: Span, safety: Safety) { match self.extern_mod_safety { Some(extern_safety) => { - if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) - && (extern_safety == Safety::Default || !self.features.unsafe_extern_blocks) - { - self.dcx().emit_err(errors::InvalidSafetyOnExtern { - item_span: span, - block: self.current_extern_span().shrink_to_lo(), - }); + if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) { + if extern_safety == Safety::Default { + self.dcx().emit_err(errors::InvalidSafetyOnExtern { + item_span: span, + block: Some(self.current_extern_span().shrink_to_lo()), + }); + } else if !self.features.unsafe_extern_blocks { + self.dcx().emit_err(errors::InvalidSafetyOnExtern { + item_span: span, + block: None, + }); + } } } None => { @@ -1088,7 +1103,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } } else if let &Safety::Unsafe(span) = safety { - this.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" }); + let mut diag = this + .dcx() + .create_err(errors::UnsafeItem { span, kind: "extern block" }); + rustc_session::parse::add_feature_diagnostics( + &mut diag, + self.session, + sym::unsafe_extern_blocks, + ); + diag.emit(); } if abi.is_none() { @@ -1289,6 +1312,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.with_impl_trait(None, |this| this.visit_ty(ty)); } } + GenericArgs::ParenthesizedElided(_span) => {} } } @@ -1445,7 +1469,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { span: args.span, }); } - None => {} + Some(ast::GenericArgs::ParenthesizedElided(_)) | None => {} } } } @@ -1693,7 +1717,9 @@ fn deny_equality_constraints( // Add `<Bar = RhsTy>` to `Foo`. match &mut assoc_path.segments[len].args { Some(args) => match args.deref_mut() { - GenericArgs::Parenthesized(_) => continue, + GenericArgs::Parenthesized(_) | GenericArgs::ParenthesizedElided(..) => { + continue; + } GenericArgs::AngleBracketed(args) => { args.args.push(arg); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 965d8fac712..bfb90476450 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -222,7 +222,7 @@ pub struct InvalidSafetyOnExtern { #[primary_span] pub item_span: Span, #[suggestion(code = "unsafe ", applicability = "machine-applicable", style = "verbose")] - pub block: Span, + pub block: Option<Span>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index e1c1a027a30..9cf3182daea 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; -use rustc_ast::{attr, AssocItemConstraint, AssocItemConstraintKind, NodeId}; +use rustc_ast::{attr, NodeId}; use rustc_ast::{token, PatKind}; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; @@ -445,23 +445,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_fn(self, fn_kind) } - fn visit_assoc_item_constraint(&mut self, constraint: &'a AssocItemConstraint) { - if let AssocItemConstraintKind::Bound { .. } = constraint.kind - && let Some(ast::GenericArgs::Parenthesized(args)) = constraint.gen_args.as_ref() - && args.inputs.is_empty() - && let ast::FnRetTy::Default(..) = args.output - { - gate!( - &self, - return_type_notation, - constraint.span, - "return type notation is experimental" - ); - } - - visit::walk_assoc_item_constraint(self, constraint) - } - fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { let is_fn = match &i.kind { ast::AssocItemKind::Fn(_) => true, @@ -566,6 +549,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { unsafe_extern_blocks, "`unsafe extern {}` blocks and `safe` keyword are experimental" ); + gate_all!(return_type_notation, "return type notation is experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { @@ -611,10 +595,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); - // Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now, - // used to be gated under associated_type_bounds, which are right above, so RTN needs to - // be too. - gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental"); gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 0225c95dca8..0568d368d8c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1060,6 +1060,11 @@ impl<'a> PrintState<'a> for State<'a> { self.word(")"); self.print_fn_ret_ty(&data.output); } + ast::GenericArgs::ParenthesizedElided(_) => { + self.word("("); + self.word(".."); + self.word(")"); + } } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index f2f6594e686..5b13858f839 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -5,6 +5,7 @@ use ast::{ForLoopKind, MatchKind}; use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::token; +use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::{self as ast, BlockCheckMode}; @@ -540,7 +541,7 @@ impl<'a> State<'a> { self.ibox(0); self.print_block_with_attrs(blk, attrs); } - ast::ExprKind::Gen(capture_clause, blk, kind) => { + ast::ExprKind::Gen(capture_clause, blk, kind, _decl_span) => { self.word_nbsp(kind.modifier()); self.print_capture_clause(*capture_clause); // cbox/ibox in analogy to the `ExprKind::Block` arm above @@ -610,9 +611,12 @@ impl<'a> State<'a> { } if let Some(expr) = opt_expr { self.space(); - self.print_expr_maybe_paren( + self.print_expr_cond_paren( expr, - parser::PREC_JUMP, + // Parenthesize if required by precedence, or in the + // case of `break 'inner: loop { break 'inner 1 } + 1` + expr.precedence().order() < parser::PREC_JUMP + || (opt_label.is_none() && classify::leading_labeled_expr(expr)), fixup.subsequent_subexpression(), ); } diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index f26f8711dd4..8eb44458137 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -6,8 +6,8 @@ use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { - pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { +impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { + pub fn dcx(&self) -> DiagCtxtHandle<'infcx> { self.infcx.dcx() } @@ -18,7 +18,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { place: &str, borrow_place: &str, value_place: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { self.dcx().create_err(crate::session_diagnostics::MoveBorrow { place, span, @@ -34,7 +34,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { desc: &str, borrow_span: Span, borrow_desc: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), span, @@ -54,7 +54,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { old_loan_span: Span, old_opt_via: &str, old_load_end_span: Option<Span>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") }; let mut err = struct_span_code_err!( self.dcx(), @@ -101,7 +101,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { desc: &str, old_loan_span: Span, old_load_end_span: Option<Span>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let mut err = struct_span_code_err!( self.dcx(), new_loan_span, @@ -134,7 +134,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { noun_old: &str, old_opt_via: &str, previous_end_span: Option<Span>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let mut err = struct_span_code_err!( self.dcx(), new_loan_span, @@ -166,7 +166,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { old_opt_via: &str, previous_end_span: Option<Span>, second_borrow_desc: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let mut err = struct_span_code_err!( self.dcx(), new_loan_span, @@ -198,7 +198,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { kind_old: &str, msg_old: &str, old_load_end_span: Option<Span>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") }; let mut err = struct_span_code_err!( self.dcx(), @@ -239,7 +239,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { span: Span, borrow_span: Span, desc: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), span, @@ -256,12 +256,12 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { span: Span, desc: &str, is_arg: bool, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" }; struct_span_code_err!(self.dcx(), span, E0384, "cannot assign {} {}", msg, desc) } - pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> Diag<'tcx> { + pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> Diag<'infcx> { struct_span_code_err!(self.dcx(), span, E0594, "cannot assign to {}", desc) } @@ -269,7 +269,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, move_from_span: Span, move_from_desc: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), move_from_span, @@ -287,7 +287,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { move_from_span: Span, ty: Ty<'_>, is_index: Option<bool>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let type_name = match (&ty.kind(), is_index) { (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array", (&ty::Slice(_), _) => "slice", @@ -308,7 +308,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, move_from_span: Span, container_ty: Ty<'_>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), move_from_span, @@ -325,7 +325,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { verb: &str, optional_adverb_for_moved: &str, moved_path: Option<String>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); struct_span_code_err!( @@ -344,7 +344,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { span: Span, path: &str, reason: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), span, @@ -362,7 +362,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { immutable_place: &str, immutable_section: &str, action: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), mutate_span, @@ -380,7 +380,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, span: Span, yield_span: Span, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind; struct_span_code_err!( self.dcx(), @@ -391,7 +391,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { .with_span_label(yield_span, "possible yield occurs here") } - pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'tcx> { + pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), borrow_span, @@ -400,7 +400,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { ) } - pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'tcx> { + pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'infcx> { struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path,) } @@ -410,7 +410,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { return_kind: &str, reference_desc: &str, path_desc: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), span, @@ -433,7 +433,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { borrowed_path: &str, capture_span: Span, scope: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), closure_span, @@ -445,7 +445,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { .with_span_label(closure_span, format!("may outlive borrowed value {borrowed_path}")) } - pub(crate) fn thread_local_value_does_not_live_long_enough(&self, span: Span) -> Diag<'tcx> { + pub(crate) fn thread_local_value_does_not_live_long_enough(&self, span: Span) -> Diag<'infcx> { struct_span_code_err!( self.dcx(), span, @@ -454,7 +454,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> { ) } - pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'tcx> { + pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> { struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",) } } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index b54e05b2b34..bb2fc3b67e9 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -1,4 +1,6 @@ +use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker}; use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; @@ -48,6 +50,110 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> { &self.outlives } + + /// Computes cycles (SCCs) in the graph of regions. In particular, + /// find all regions R1, R2 such that R1: R2 and R2: R1 and group + /// them into an SCC, and find the relationships between SCCs. + pub(crate) fn compute_sccs( + &self, + static_region: RegionVid, + definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, + ) -> ConstraintSccs { + let constraint_graph = self.graph(definitions.len()); + let region_graph = &constraint_graph.region_graph(self, static_region); + ConstraintSccs::new_with_annotation(®ion_graph, |r| { + RegionTracker::new(r, &definitions[r]) + }) + } + + /// This method handles Universe errors by rewriting the constraint + /// graph. For each strongly connected component in the constraint + /// graph such that there is a series of constraints + /// A: B: C: ... : X where + /// A's universe is smaller than X's and A is a placeholder, + /// add a constraint that A: 'static. This is a safe upper bound + /// in the face of borrow checker/trait solver limitations that will + /// eventually go away. + /// + /// For a more precise definition, see the documentation for + /// [`RegionTracker::has_incompatible_universes()`]. + /// + /// This edge case used to be handled during constraint propagation + /// by iterating over the strongly connected components in the constraint + /// graph while maintaining a set of bookkeeping mappings similar + /// to what is stored in `RegionTracker` and manually adding 'sttaic as + /// needed. + /// + /// It was rewritten as part of the Polonius project with the goal of moving + /// higher-kindedness concerns out of the path of the borrow checker, + /// for two reasons: + /// + /// 1. Implementing Polonius is difficult enough without also + /// handling them. + /// 2. The long-term goal is to handle higher-kinded concerns + /// in the trait solver, where they belong. This avoids + /// logic duplication and allows future trait solvers + /// to compute better bounds than for example our + /// "must outlive 'static" here. + /// + /// This code is a stop-gap measure in preparation for the future trait solver. + /// + /// Every constraint added by this method is an + /// internal `IllegalUniverse` constraint. + #[instrument(skip(self, universal_regions, definitions))] + pub(crate) fn add_outlives_static( + &mut self, + universal_regions: &UniversalRegions<'tcx>, + definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, + ) -> ConstraintSccs { + let fr_static = universal_regions.fr_static; + let sccs = self.compute_sccs(fr_static, definitions); + + // Changed to `true` if we added any constraints to `self` and need to + // recompute SCCs. + let mut added_constraints = false; + + for scc in sccs.all_sccs() { + // No point in adding 'static: 'static! + // This micro-optimisation makes somewhat sense + // because static outlives *everything*. + if scc == sccs.scc(fr_static) { + continue; + } + + let annotation = sccs.annotation(scc); + + // If this SCC participates in a universe violation, + // e.g. if it reaches a region with a universe smaller than + // the largest region reached, add a requirement that it must + // outlive `'static`. + if annotation.has_incompatible_universes() { + // Optimisation opportunity: this will add more constraints than + // needed for correctness, since an SCC upstream of another with + // a universe violation will "infect" its downstream SCCs to also + // outlive static. + added_constraints = true; + let scc_representative_outlives_static = OutlivesConstraint { + sup: annotation.representative, + sub: fr_static, + category: ConstraintCategory::IllegalUniverse, + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }; + self.push(scc_representative_outlives_static); + } + } + + if added_constraints { + // We changed the constraint set and so must recompute SCCs. + self.compute_sccs(fr_static, definitions) + } else { + // If we didn't add any back-edges; no more work needs doing + sccs + } + } } impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> { diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index f2b5ddcd782..00a30dc2240 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -15,24 +15,24 @@ use std::fmt; use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext}; /// The results of the dataflow analyses used by the borrow checker. -pub struct BorrowckResults<'mir, 'tcx> { - pub(crate) borrows: Results<'tcx, Borrows<'mir, 'tcx>>, - pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, - pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>, +pub struct BorrowckResults<'a, 'mir, 'tcx> { + pub(crate) borrows: Results<'tcx, Borrows<'a, 'mir, 'tcx>>, + pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>, + pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'mir, 'tcx>>, } /// The transient state of the dataflow analyses used by the borrow checker. #[derive(Debug)] -pub struct BorrowckFlowState<'mir, 'tcx> { - pub(crate) borrows: <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, - pub(crate) uninits: <MaybeUninitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, - pub(crate) ever_inits: <EverInitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, +pub struct BorrowckFlowState<'a, 'mir, 'tcx> { + pub(crate) borrows: <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, + pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, + pub(crate) ever_inits: <EverInitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain, } -impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> { +impl<'a, 'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'mir, 'tcx> { // All three analyses are forward, but we have to use just one here. - type Direction = <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction; - type FlowState = BorrowckFlowState<'mir, 'tcx>; + type Direction = <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction; + type FlowState = BorrowckFlowState<'a, 'mir, 'tcx>; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { BorrowckFlowState { @@ -106,11 +106,11 @@ rustc_index::newtype_index! { /// `BorrowIndex`, and maps each such index to a `BorrowData` /// describing the borrow. These indexes are used for representing the /// borrows in compact bitvectors. -pub struct Borrows<'mir, 'tcx> { +pub struct Borrows<'a, 'mir, 'tcx> { tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, - borrow_set: &'mir BorrowSet<'tcx>, + borrow_set: &'a BorrowSet<'tcx>, borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>, } @@ -389,12 +389,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { } } -impl<'mir, 'tcx> Borrows<'mir, 'tcx> { +impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, - regioncx: &'mir RegionInferenceContext<'tcx>, - borrow_set: &'mir BorrowSet<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &'a BorrowSet<'tcx>, ) -> Self { let mut borrows_out_of_scope_at_location = calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set); @@ -494,7 +494,7 @@ impl<'mir, 'tcx> Borrows<'mir, 'tcx> { } } -impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { +impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> { type Domain = BitSet<BorrowIndex>; const NAME: &'static str = "borrows"; @@ -517,7 +517,7 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { /// region stops containing the CFG points reachable from the issuing location. /// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of /// `a.b.c` when `a` is overwritten. -impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { +impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> { type Idx = BorrowIndex; fn domain_size(&self, _: &mir::Body<'tcx>) -> usize { @@ -617,8 +617,8 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex { - fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl DebugWithContext<Borrows<'_, '_, '_>> for BorrowIndex { + fn fmt_with(&self, ctxt: &Borrows<'_, '_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", ctxt.location(*self)) } } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index d46febffba8..8bf3e670ff2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -149,13 +149,13 @@ trait TypeOpInfo<'tcx> { fn base_universe(&self) -> ty::UniverseIndex; - fn nice_error( + fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, - ) -> Option<Diag<'tcx>>; + ) -> Option<Diag<'infcx>>; #[instrument(level = "debug", skip(self, mbcx))] fn report_error( @@ -231,18 +231,25 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { self.base_universe } - fn nice_error( + fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, - ) -> Option<Diag<'tcx>> { + ) -> Option<Diag<'infcx>> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); type_op_prove_predicate_with_cause(&ocx, key, cause); - try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) + let diag = try_extract_error_from_fulfill_cx( + &ocx, + mbcx.mir_def_id(), + placeholder_region, + error_region, + )? + .with_dcx(mbcx.dcx()); + Some(diag) } } @@ -268,13 +275,13 @@ where self.base_universe } - fn nice_error( + fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, - ) -> Option<Diag<'tcx>> { + ) -> Option<Diag<'infcx>> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); @@ -288,7 +295,14 @@ where let (param_env, value) = key.into_parts(); let _ = ocx.normalize(&cause, param_env, value.value); - try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) + let diag = try_extract_error_from_fulfill_cx( + &ocx, + mbcx.mir_def_id(), + placeholder_region, + error_region, + )? + .with_dcx(mbcx.dcx()); + Some(diag) } } @@ -308,18 +322,25 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { self.base_universe } - fn nice_error( + fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, - ) -> Option<Diag<'tcx>> { + ) -> Option<Diag<'infcx>> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?; - try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) + let diag = try_extract_error_from_fulfill_cx( + &ocx, + mbcx.mir_def_id(), + placeholder_region, + error_region, + )? + .with_dcx(mbcx.dcx()); + Some(diag) } } @@ -334,13 +355,13 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { self.base_universe.unwrap() } - fn nice_error( + fn nice_error<'infcx>( &self, - mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>, + mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>, _cause: ObligationCause<'tcx>, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, - ) -> Option<Diag<'tcx>> { + ) -> Option<Diag<'infcx>> { try_extract_error_from_region_constraints( mbcx.infcx, mbcx.mir_def_id(), @@ -358,12 +379,12 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { } #[instrument(skip(ocx), level = "debug")] -fn try_extract_error_from_fulfill_cx<'tcx>( - ocx: &ObligationCtxt<'_, 'tcx>, +fn try_extract_error_from_fulfill_cx<'a, 'tcx>( + ocx: &ObligationCtxt<'a, 'tcx>, generic_param_scope: LocalDefId, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, -) -> Option<Diag<'tcx>> { +) -> Option<Diag<'a>> { // We generally shouldn't have errors here because the query was // already run, but there's no point using `span_delayed_bug` // when we're going to emit an error here anyway. @@ -381,15 +402,15 @@ fn try_extract_error_from_fulfill_cx<'tcx>( } #[instrument(level = "debug", skip(infcx, region_var_origin, universe_of_region))] -fn try_extract_error_from_region_constraints<'tcx>( - infcx: &InferCtxt<'tcx>, +fn try_extract_error_from_region_constraints<'a, 'tcx>( + infcx: &'a InferCtxt<'tcx>, generic_param_scope: LocalDefId, placeholder_region: ty::Region<'tcx>, error_region: Option<ty::Region<'tcx>>, region_constraints: &RegionConstraintData<'tcx>, mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin, mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex, -) -> Option<Diag<'tcx>> { +) -> Option<Diag<'a>> { let placeholder_universe = match placeholder_region.kind() { ty::RePlaceholder(p) => p.universe, ty::ReVar(vid) => universe_of_region(vid), diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 1cc7fee718e..b1e302e5e27 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -34,11 +34,12 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, Symbol}; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; -use rustc_trait_selection::traits::error_reporting::FindExprBySpan; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use std::iter; +use std::ops::ControlFlow; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -73,7 +74,7 @@ enum StorageDeadOrDrop<'tcx> { Destructor(Ty<'tcx>), } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { pub(crate) fn report_use_of_moved_or_uninitialized( &mut self, location: Location, @@ -341,7 +342,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { fn suggest_ref_or_clone( &self, mpi: MovePathIndex, - err: &mut Diag<'tcx>, + err: &mut Diag<'infcx>, in_pattern: &mut bool, move_spans: UseSpans<'tcx>, ) { @@ -517,7 +518,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { desired_action: InitializationRequiringAction, span: Span, use_spans: UseSpans<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { // We need all statements in the body where the binding was assigned to later find all // the branching code paths where the binding *wasn't* assigned to. let inits = &self.move_data.init_path_map[mpi]; @@ -784,20 +785,20 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// binding declaration within every scope we inspect. struct Finder { hir_id: hir::HirId, - found: bool, } impl<'hir> Visitor<'hir> for Finder { - fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) { + type Result = ControlFlow<()>; + fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) -> Self::Result { if pat.hir_id == self.hir_id { - self.found = true; + return ControlFlow::Break(()); } - hir::intravisit::walk_pat(self, pat); + hir::intravisit::walk_pat(self, pat) } - fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { + fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) -> Self::Result { if ex.hir_id == self.hir_id { - self.found = true; + return ControlFlow::Break(()); } - hir::intravisit::walk_expr(self, ex); + hir::intravisit::walk_expr(self, ex) } } // The immediate HIR parent of the moved expression. We'll look for it to be a call. @@ -822,9 +823,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { _ => continue, }; if let Some(&hir_id) = local_hir_id { - let mut finder = Finder { hir_id, found: false }; - finder.visit_expr(e); - if finder.found { + if (Finder { hir_id }).visit_expr(e).is_break() { // The current scope includes the declaration of the binding we're accessing, we // can't look up any further for loops. break; @@ -839,9 +838,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(cond, ..), .. }) => { - let mut finder = Finder { hir_id: expr.hir_id, found: false }; - finder.visit_expr(cond); - if finder.found { + if (Finder { hir_id: expr.hir_id }).visit_expr(cond).is_break() { // The expression where the move error happened is in a `while let` // condition Don't suggest clone as it will likely end in an // infinite loop. @@ -1288,7 +1285,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { return false; } // Try to find predicates on *generic params* that would allow copying `ty` - let suggestion = + let mut suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { format!(": {symbol}.clone()") } else { @@ -1296,6 +1293,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { }; let mut sugg = Vec::with_capacity(2); let mut inner_expr = expr; + let mut is_raw_ptr = false; + let typeck_result = self.infcx.tcx.typeck(self.mir_def_id()); // Remove uses of `&` and `*` when suggesting `.clone()`. while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) = &inner_expr.kind @@ -1306,14 +1305,32 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { return false; } inner_expr = inner; + if let Some(inner_type) = typeck_result.node_type_opt(inner.hir_id) { + if matches!(inner_type.kind(), ty::RawPtr(..)) { + is_raw_ptr = true; + break; + } + } } - if inner_expr.span.lo() != expr.span.lo() { + // Cloning the raw pointer doesn't make sense in some cases and would cause a type mismatch error. (see #126863) + if inner_expr.span.lo() != expr.span.lo() && !is_raw_ptr { + // Remove "(*" or "(&" sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new())); } + // Check whether `expr` is surrounded by parentheses or not. let span = if inner_expr.span.hi() != expr.span.hi() { // Account for `(*x)` to suggest `x.clone()`. - expr.span.with_lo(inner_expr.span.hi()) + if is_raw_ptr { + expr.span.shrink_to_hi() + } else { + // Remove the close parenthesis ")" + expr.span.with_lo(inner_expr.span.hi()) + } } else { + if is_raw_ptr { + sugg.push((expr.span.shrink_to_lo(), "(".to_string())); + suggestion = ").clone()".to_string(); + } expr.span.shrink_to_hi() }; sugg.push((span, suggestion)); @@ -1441,7 +1458,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { location: Location, (place, _span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let borrow_spans = self.retrieve_borrow_spans(borrow); let borrow_span = borrow_spans.args_or_use(); @@ -1491,7 +1508,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { (place, span): (Place<'tcx>, Span), gen_borrow_kind: BorrowKind, issued_borrow: &BorrowData<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let issued_spans = self.retrieve_borrow_spans(issued_borrow); let issued_span = issued_spans.args_or_use(); @@ -1782,7 +1799,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { err } - fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'tcx>, place: Place<'tcx>) { + fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'infcx>, place: Place<'tcx>) { let tcx = self.infcx.tcx; let hir = tcx.hir(); let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; @@ -1817,7 +1834,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { pub struct Holds<'tcx> { ty: Ty<'tcx>, - holds: bool, } impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Holds<'tcx> { @@ -1825,7 +1841,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { if t == self.ty { - self.holds = true; + return ControlFlow::Break(()); } t.super_visit_with(self) } @@ -1843,9 +1859,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { && rcvr_ty == ty && let ty::Ref(_, inner, _) = rcvr_ty.kind() && let inner = inner.peel_refs() - && let mut v = (Holds { ty: inner, holds: false }) - && let _ = v.visit_ty(local_ty) - && v.holds + && (Holds { ty: inner }).visit_ty(local_ty).is_break() && let None = self.infcx.type_implements_trait_shallow(clone, inner, self.param_env) { err.span_label( @@ -2841,7 +2855,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { drop_span: Span, borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { debug!( "report_local_value_does_not_live_long_enough(\ {:?}, {:?}, {:?}, {:?}, {:?}\ @@ -3016,7 +3030,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, drop_span: Span, borrow_span: Span, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { debug!( "report_thread_local_value_does_not_live_long_enough(\ {:?}, {:?}\ @@ -3041,7 +3055,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { borrow_spans: UseSpans<'tcx>, proper_span: Span, explanation: BorrowExplanation<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = explanation { @@ -3206,7 +3220,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { return_span: Span, category: ConstraintCategory<'tcx>, opt_place_desc: Option<&String>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'infcx>> { let return_kind = match category { ConstraintCategory::Return(_) => "return", ConstraintCategory::Yield => "yield", @@ -3299,7 +3313,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { constraint_span: Span, captured_var: &str, scope: &str, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let tcx = self.infcx.tcx; let args_span = use_span.args_or_use(); @@ -3411,7 +3425,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { upvar_span: Span, upvar_name: Symbol, escape_span: Span, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let tcx = self.infcx.tcx; let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id()); @@ -3713,7 +3727,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { if tcx.is_diagnostic_item(sym::deref_method, method_did) { let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::resolve(tcx, self.param_env, deref_target, method_args) + Instance::try_resolve(tcx, self.param_env, deref_target, method_args) .transpose() }); if let Some(Ok(instance)) = deref_target { @@ -3737,13 +3751,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { assigned_span: Span, err_place: Place<'tcx>, ) { - let (from_arg, local_decl, local_name) = match err_place.as_local() { - Some(local) => ( - self.body.local_kind(local) == LocalKind::Arg, - Some(&self.body.local_decls[local]), - self.local_names[local], - ), - None => (false, None, None), + let (from_arg, local_decl) = match err_place.as_local() { + Some(local) => { + (self.body.local_kind(local) == LocalKind::Arg, Some(&self.body.local_decls[local])) + } + None => (false, None), }; // If root local is initialized immediately (everything apart from let @@ -3775,13 +3787,12 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { err.span_label(assigned_span, format!("first assignment to {place_description}")); } if let Some(decl) = local_decl - && let Some(name) = local_name && decl.can_be_made_mutable() { - err.span_suggestion( - decl.source_info.span, + err.span_suggestion_verbose( + decl.source_info.span.shrink_to_lo(), "consider making this binding mutable", - format!("mut {name}"), + "mut ".to_string(), Applicability::MachineApplicable, ); if !from_arg @@ -3793,10 +3804,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { })) ) { - err.span_suggestion( - decl.source_info.span, + err.span_suggestion_verbose( + decl.source_info.span.shrink_to_lo(), "to modify the original value, take a borrow instead", - format!("ref mut {name}"), + "ref mut ".to_string(), Applicability::MaybeIncorrect, ); } @@ -4308,15 +4319,14 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { } /// Detect whether one of the provided spans is a statement nested within the top-most visited expr -struct ReferencedStatementsVisitor<'a>(&'a [Span], bool); +struct ReferencedStatementsVisitor<'a>(&'a [Span]); -impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { - fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { +impl<'v> Visitor<'v> for ReferencedStatementsVisitor<'_> { + type Result = ControlFlow<()>; + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result { match s.kind { - hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => { - self.1 = true; - } - _ => {} + hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => ControlFlow::Break(()), + _ => ControlFlow::Continue(()), } } } @@ -4358,9 +4368,7 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> { hir::ExprKind::If(cond, body, None) => { // `if` expressions with no `else` that initialize the binding might be missing an // `else` arm. - let mut v = ReferencedStatementsVisitor(self.spans, false); - v.visit_expr(body); - if v.1 { + if ReferencedStatementsVisitor(self.spans).visit_expr(body).is_break() { self.errors.push(( cond.span, format!( @@ -4377,11 +4385,9 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> { hir::ExprKind::If(cond, body, Some(other)) => { // `if` expressions where the binding is only initialized in one of the two arms // might be missing a binding initialization. - let mut a = ReferencedStatementsVisitor(self.spans, false); - a.visit_expr(body); - let mut b = ReferencedStatementsVisitor(self.spans, false); - b.visit_expr(other); - match (a.1, b.1) { + let a = ReferencedStatementsVisitor(self.spans).visit_expr(body).is_break(); + let b = ReferencedStatementsVisitor(self.spans).visit_expr(other).is_break(); + match (a, b) { (true, true) | (false, false) => {} (true, false) => { if other.span.is_desugaring(DesugaringKind::WhileLoop) { @@ -4420,11 +4426,7 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> { // arms might be missing an initialization. let results: Vec<bool> = arms .iter() - .map(|arm| { - let mut v = ReferencedStatementsVisitor(self.spans, false); - v.visit_arm(arm); - v.1 - }) + .map(|arm| ReferencedStatementsVisitor(self.spans).visit_arm(arm).is_break()) .collect(); if results.iter().any(|x| *x) && !results.iter().all(|x| *x) { for (arm, seen) in arms.iter().zip(results) { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 6165a718a30..ffe52f939dd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; -use rustc_trait_selection::traits::error_reporting::FindExprBySpan; +use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use crate::region_infer::{BlameConstraint, ExtraConstraintInfo}; use crate::{ diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 842ed38f1e2..b7fbb71a0cf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -27,8 +27,8 @@ use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{ type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode, }; @@ -69,7 +69,7 @@ pub(super) struct DescribePlaceOpt { pub(super) struct IncludingTupleField(pub(super) bool); -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure /// is moved after being invoked. /// @@ -86,7 +86,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, location: Location, place: PlaceRef<'tcx>, - diag: &mut Diag<'_>, + diag: &mut Diag<'infcx>, ) -> bool { debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place); let mut target = place.local_or_deref_local(); diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 12fa4c4f5ee..4b6c1b29f28 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; -use rustc_trait_selection::traits::error_reporting::FindExprBySpan; +use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use crate::diagnostics::CapturedMessageOpt; use crate::diagnostics::{DescribePlaceOpt, UseSpans}; @@ -93,7 +93,7 @@ enum GroupedMoveError<'tcx> { }, } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { pub(crate) fn report_move_errors(&mut self) { let grouped_errors = self.group_move_errors(); for error in grouped_errors { @@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { self.buffer_error(err); } - fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'tcx> { + fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> { let description = if place.projection.len() == 1 { format!("static item {}", self.describe_any_place(place.as_ref())) } else { @@ -428,7 +428,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { deref_target_place: Place<'tcx>, span: Span, use_spans: Option<UseSpans<'tcx>>, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let tcx = self.infcx.tcx; // Inspect the type of the content behind the // borrow to provide feedback about why this @@ -639,12 +639,27 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) { match self.infcx.tcx.sess.source_map().span_to_snippet(span) { Ok(snippet) if snippet.starts_with('*') => { - err.span_suggestion_verbose( - span.with_hi(span.lo() + BytePos(1)), - "consider removing the dereference here", - String::new(), - Applicability::MaybeIncorrect, - ); + let sp = span.with_lo(span.lo() + BytePos(1)); + let inner = self.find_expr(sp); + let mut is_raw_ptr = false; + if let Some(inner) = inner { + let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) { + if matches!(inner_type.kind(), ty::RawPtr(..)) { + is_raw_ptr = true; + } + } + } + // If the `inner` is a raw pointer, do not suggest removing the "*", see #126863 + // FIXME: need to check whether the assigned object can be a raw pointer, see `tests/ui/borrowck/issue-20801.rs`. + if !is_raw_ptr { + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(1)), + "consider removing the dereference here", + String::new(), + Applicability::MaybeIncorrect, + ); + } } _ => { err.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 93fac3181ba..26b0d23b166 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -16,9 +16,9 @@ use rustc_middle::{ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use crate::diagnostics::BorrowedContentSource; use crate::util::FindAssignments; @@ -30,7 +30,7 @@ pub(crate) enum AccessKind { Mutate, } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { pub(crate) fn report_mutability_error( &mut self, access_place: Place<'tcx>, @@ -408,10 +408,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { fn_decl.implicit_self, hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut ) { - err.span_suggestion( - upvar_ident.span, + err.span_suggestion_verbose( + upvar_ident.span.shrink_to_lo(), "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), + "mut ", Applicability::MachineApplicable, ); break; @@ -419,10 +419,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { } } } else { - err.span_suggestion( - upvar_ident.span, + err.span_suggestion_verbose( + upvar_ident.span.shrink_to_lo(), "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), + "mut ", Applicability::MachineApplicable, ); } @@ -449,8 +449,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { .is_ok_and(|snippet| snippet.starts_with("&mut ")) => { err.span_label(span, format!("cannot {act}")); - err.span_suggestion( - span, + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(5)), "try removing `&mut` here", "", Applicability::MaybeIncorrect, @@ -541,7 +541,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { } /// Suggest `map[k] = v` => `map.insert(k, v)` and the like. - fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) { + fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) @@ -550,13 +550,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// Walks through the HIR, looking for the corresponding span for this error. /// When it finds it, see if it corresponds to assignment operator whose LHS /// is an index expr. - struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { + struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> { assign_span: Span, - err: &'a mut Diag<'tcx>, + err: &'a mut Diag<'infcx>, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { + impl<'a, 'cx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'cx, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { @@ -755,13 +755,16 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. }) = node - && let Ok(name) = - self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span) { - err.span_suggestion( - pat_span, + err.multipart_suggestion( "consider changing this to be mutable", - format!("&(mut {name})"), + vec![ + (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()), + ( + local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()), + ")".to_string(), + ), + ], Applicability::MachineApplicable, ); return; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index db78edc45b9..55147ee337f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -10,14 +10,12 @@ use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicate::BoundPredicate; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; -use rustc_infer::infer::{ - error_reporting::nice_region_error::{ - self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, - HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, - }, - error_reporting::unexpected_hidden_region_diagnostic, - NllRegionVariableOrigin, RelateParamBound, +use rustc_infer::infer::error_reporting::nice_region_error::{ + self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, + HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, }; +use rustc_infer::infer::error_reporting::region::unexpected_hidden_region_diagnostic; +use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; @@ -66,7 +64,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Predicate(_) | ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal => "", + | ConstraintCategory::Internal + | ConstraintCategory::IllegalUniverse => "", } } } @@ -160,7 +159,7 @@ pub struct ErrorConstraintInfo<'tcx> { pub(super) span: Span, } -impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { /// Converts a region inference variable into a `ty::Region` that /// we can use for error reporting. If `r` is universally bound, /// then we use the name that we have on record for it. If `r` is @@ -360,7 +359,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); let diag = unexpected_hidden_region_diagnostic( - self.infcx.tcx, + self.infcx, self.mir_def_id(), span, named_ty, @@ -589,7 +588,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { &self, errci: &ErrorConstraintInfo<'tcx>, kind: ReturnConstraint, - ) -> Diag<'tcx> { + ) -> Diag<'infcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; @@ -658,7 +657,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// | ^^^^^^^^^^ `x` escapes the function body here /// ``` #[instrument(level = "debug", skip(self))] - fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> { + fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> { let ErrorConstraintInfo { span, category, .. } = errci; let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region( @@ -767,7 +766,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// | is returning data with lifetime `'b` /// ``` #[allow(rustc::diagnostic_outside_of_impl)] // FIXME - fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> { + fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> { let ErrorConstraintInfo { fr, fr_is_local, @@ -948,7 +947,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { return; } - if let Ok(Some(instance)) = ty::Instance::resolve( + if let Ok(Some(instance)) = ty::Instance::try_resolve( tcx, self.param_env, *fn_did, diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 51ea59e2092..af96f115385 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -213,8 +213,32 @@ trait FactCell { fn to_string(&self, location_table: &LocationTable) -> String; } -impl<A: Debug> FactCell for A { - default fn to_string(&self, _location_table: &LocationTable) -> String { +impl FactCell for BorrowIndex { + fn to_string(&self, _location_table: &LocationTable) -> String { + format!("{self:?}") + } +} + +impl FactCell for Local { + fn to_string(&self, _location_table: &LocationTable) -> String { + format!("{self:?}") + } +} + +impl FactCell for MovePathIndex { + fn to_string(&self, _location_table: &LocationTable) -> String { + format!("{self:?}") + } +} + +impl FactCell for PoloniusRegionVid { + fn to_string(&self, _location_table: &LocationTable) -> String { + format!("{self:?}") + } +} + +impl FactCell for RegionVid { + fn to_string(&self, _location_table: &LocationTable) -> String { format!("{self:?}") } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 69efee2fbdc..d258c68b959 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -7,7 +7,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(let_chains)] -#![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] @@ -310,8 +309,8 @@ fn do_mir_borrowck<'tcx>( promoted_mbcx.report_move_errors(); diags = promoted_mbcx.diags; - struct MoveVisitor<'a, 'b, 'mir, 'cx, 'tcx> { - ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'cx, 'tcx>, + struct MoveVisitor<'a, 'b, 'mir, 'infcx, 'tcx> { + ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'infcx, 'tcx>, } impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, '_, 'tcx> { @@ -528,8 +527,8 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } } -struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> { - infcx: &'cx BorrowckInferCtxt<'tcx>, +struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> { + infcx: &'infcx BorrowckInferCtxt<'tcx>, param_env: ParamEnv<'tcx>, body: &'mir Body<'tcx>, move_data: &'a MoveData<'tcx>, @@ -596,7 +595,7 @@ struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> { /// Results of Polonius analysis. polonius_output: Option<Rc<PoloniusOutput>>, - diags: diags::BorrowckDiags<'tcx>, + diags: diags::BorrowckDiags<'infcx, 'tcx>, move_errors: Vec<MoveError<'tcx>>, } @@ -605,15 +604,15 @@ struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> - for MirBorrowckCtxt<'_, 'mir, '_, 'tcx> +impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> + for MirBorrowckCtxt<'a, 'mir, '_, 'tcx> { - type FlowState = Flows<'mir, 'tcx>; + type FlowState = Flows<'a, 'mir, 'tcx>; fn visit_statement_before_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, stmt: &'mir Statement<'tcx>, location: Location, ) { @@ -683,7 +682,7 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> fn visit_terminator_before_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, term: &'mir Terminator<'tcx>, loc: Location, ) { @@ -728,6 +727,12 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> } self.mutate_place(loc, (*destination, span), Deep, flow_state); } + TerminatorKind::TailCall { func, args, fn_span: _ } => { + self.consume_operand(loc, (func, span), flow_state); + for arg in args { + self.consume_operand(loc, (&arg.node, arg.span), flow_state); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(loc, (cond, span), flow_state); if let AssertKind::BoundsCheck { len, index } = &**msg { @@ -794,7 +799,7 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> fn visit_terminator_after_primary_effect( &mut self, _results: &mut R, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, term: &'mir Terminator<'tcx>, loc: Location, ) { @@ -814,9 +819,8 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop => { - // Returning from the function implicitly kills storage for all locals and statics. - // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. let borrow_set = self.borrow_set.clone(); @@ -988,7 +992,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { place_span: (Place<'tcx>, Span), kind: (AccessDepth, ReadOrWrite), is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { let (sd, rw) = kind; @@ -1038,7 +1042,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { place_span: (Place<'tcx>, Span), sd: AccessDepth, rw: ReadOrWrite, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) -> bool { let mut error_reported = false; let borrow_set = Rc::clone(&self.borrow_set); @@ -1179,7 +1183,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, place_span: (Place<'tcx>, Span), kind: AccessDepth, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { // Write of P[i] or *P requires P init'd. self.check_if_assigned_path_is_moved(location, place_span, flow_state); @@ -1197,7 +1201,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { &mut self, location: Location, (rvalue, span): (&'mir Rvalue<'tcx>, Span), - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { match rvalue { &Rvalue::Ref(_ /*rgn*/, bk, place) => { @@ -1455,7 +1459,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { &mut self, location: Location, (operand, span): (&'mir Operand<'tcx>, Span), - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { match *operand { Operand::Copy(place) => { @@ -1579,7 +1583,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { &mut self, location: Location, span: Span, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { // Two-phase borrow support: For each activation that is newly // generated at this statement, check if it interferes with @@ -1743,7 +1747,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1848,7 +1852,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, desired_action: InitializationRequiringAction, place_span: (PlaceRef<'tcx>, Span), - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { let maybe_uninits = &flow_state.uninits; @@ -1947,7 +1951,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { &mut self, location: Location, (place, span): (Place<'tcx>, Span), - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { debug!("check_if_assigned_path_is_moved place: {:?}", place); @@ -2013,7 +2017,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { location: Location, base: PlaceRef<'tcx>, span: Span, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) { // rust-lang/rust#21232: Until Rust allows reads from the // initialized parts of partially initialized structs, we @@ -2104,7 +2108,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { (place, span): (Place<'tcx>, Span), kind: ReadOrWrite, is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, location: Location, ) -> bool { debug!( @@ -2220,7 +2224,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { fn is_local_ever_initialized( &self, local: Local, - flow_state: &Flows<'mir, 'tcx>, + flow_state: &Flows<'_, 'mir, 'tcx>, ) -> Option<InitIndex> { let mpi = self.move_data.rev_lookup.find_local(local)?; let ii = &self.move_data.init_path_map[mpi]; @@ -2228,7 +2232,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> { } /// Adds the place into the used mutable variables set - fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'mir, 'tcx>) { + fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'_, 'mir, 'tcx>) { match root_place { RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => { // If the local may have been initialized, and it is now currently being @@ -2428,12 +2432,12 @@ mod diags { use super::*; - enum BufferedDiag<'tcx> { - Error(Diag<'tcx>), - NonError(Diag<'tcx, ()>), + enum BufferedDiag<'infcx> { + Error(Diag<'infcx>), + NonError(Diag<'infcx, ()>), } - impl<'tcx> BufferedDiag<'tcx> { + impl<'infcx> BufferedDiag<'infcx> { fn sort_span(&self) -> Span { match self { BufferedDiag::Error(diag) => diag.sort_span, @@ -2442,7 +2446,7 @@ mod diags { } } - pub struct BorrowckDiags<'tcx> { + pub struct BorrowckDiags<'infcx, 'tcx> { /// This field keeps track of move errors that are to be reported for given move indices. /// /// There are situations where many errors can be reported for a single move out (see @@ -2457,15 +2461,15 @@ mod diags { /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary /// when errors in the map are being re-added to the error buffer so that errors with the /// same primary span come out in a consistent order. - buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'tcx>)>, + buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>, - buffered_mut_errors: FxIndexMap<Span, (Diag<'tcx>, usize)>, + buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>, /// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics. - buffered_diags: Vec<BufferedDiag<'tcx>>, + buffered_diags: Vec<BufferedDiag<'infcx>>, } - impl<'tcx> BorrowckDiags<'tcx> { + impl<'infcx, 'tcx> BorrowckDiags<'infcx, 'tcx> { pub fn new() -> Self { BorrowckDiags { buffered_move_errors: BTreeMap::new(), @@ -2474,28 +2478,28 @@ mod diags { } } - pub fn buffer_error(&mut self, diag: Diag<'tcx>) { + pub fn buffer_error(&mut self, diag: Diag<'infcx>) { self.buffered_diags.push(BufferedDiag::Error(diag)); } - pub fn buffer_non_error(&mut self, diag: Diag<'tcx, ()>) { + pub fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { self.buffered_diags.push(BufferedDiag::NonError(diag)); } } - impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { - pub fn buffer_error(&mut self, diag: Diag<'tcx>) { + impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { + pub fn buffer_error(&mut self, diag: Diag<'infcx>) { self.diags.buffer_error(diag); } - pub fn buffer_non_error(&mut self, diag: Diag<'tcx, ()>) { + pub fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { self.diags.buffer_non_error(diag); } pub fn buffer_move_error( &mut self, move_out_indices: Vec<MoveOutIndex>, - place_and_err: (PlaceRef<'tcx>, Diag<'tcx>), + place_and_err: (PlaceRef<'tcx>, Diag<'infcx>), ) -> bool { if let Some((_, diag)) = self.diags.buffered_move_errors.insert(move_out_indices, place_and_err) @@ -2508,12 +2512,12 @@ mod diags { } } - pub fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'tcx>, usize)> { + pub fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> { // FIXME(#120456) - is `swap_remove` correct? self.diags.buffered_mut_errors.swap_remove(&span) } - pub fn buffer_mut_error(&mut self, span: Span, diag: Diag<'tcx>, count: usize) { + pub fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) { self.diags.buffered_mut_errors.insert(span, (diag, count)); } @@ -2554,7 +2558,7 @@ mod diags { pub fn has_move_error( &self, move_out_indices: &[MoveOutIndex], - ) -> Option<&(PlaceRef<'tcx>, Diag<'tcx>)> { + ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> { self.diags.buffered_move_errors.get(move_out_indices) } } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 923cf7e9405..2ffa9ba5b4d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -81,7 +81,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( promoted: &IndexSlice<Promoted, Body<'tcx>>, location_table: &LocationTable, param_env: ty::ParamEnv<'tcx>, - flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, + flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'_, 'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, upvars: &[&ty::CapturedPlace<'tcx>], @@ -262,13 +262,13 @@ pub(super) fn dump_mir_results<'tcx>( #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] -pub(super) fn dump_annotation<'tcx>( - infcx: &BorrowckInferCtxt<'tcx>, +pub(super) fn dump_annotation<'tcx, 'cx>( + infcx: &'cx BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>, - diags: &mut crate::diags::BorrowckDiags<'tcx>, + diags: &mut crate::diags::BorrowckDiags<'cx, 'tcx>, ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); @@ -285,7 +285,7 @@ pub(super) fn dump_annotation<'tcx>( let def_span = tcx.def_span(body.source.def_id()); let mut err = if let Some(closure_region_requirements) = closure_region_requirements { - let mut err = tcx.dcx().struct_span_note(def_span, "external requirements"); + let mut err = infcx.dcx().struct_span_note(def_span, "external requirements"); regioncx.annotate(tcx, &mut err); @@ -304,7 +304,7 @@ pub(super) fn dump_annotation<'tcx>( err } else { - let mut err = tcx.dcx().struct_span_note(def_span, "no external requirements"); + let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements"); regioncx.annotate(tcx, &mut err); err diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 6979910a02d..30dfc4c21b0 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { } self.mutate_place(location, *destination, Deep); } + TerminatorKind::TailCall { func, args, .. } => { + self.consume_operand(location, func); + for arg in args { + self.consume_operand(location, &arg.node); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(location, cond); use rustc_middle::mir::AssertKind; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c56eaaff076..44a84fb9d7f 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -62,7 +62,7 @@ pub struct RegionTracker { /// The representative Region Variable Id for this SCC. We prefer /// placeholders over existentially quantified variables, otherwise /// it's the one with the smallest Region Variable ID. - representative: RegionVid, + pub(crate) representative: RegionVid, /// Is the current representative a placeholder? representative_is_placeholder: bool, @@ -97,7 +97,7 @@ impl scc::Annotation for RegionTracker { } impl RegionTracker { - fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { let (representative_is_placeholder, representative_is_existential) = match definition.origin { rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false), @@ -116,7 +116,9 @@ impl RegionTracker { representative_is_existential, } } - fn universe(self) -> UniverseIndex { + + /// The smallest-indexed universe reachable from and/or in this SCC. + fn min_universe(self) -> UniverseIndex { self.min_reachable_universe } @@ -132,8 +134,8 @@ impl RegionTracker { /// Returns `true` if during the annotated SCC reaches a placeholder /// with a universe larger than the smallest reachable one, `false` otherwise. - pub fn has_incompatible_universes(&self) -> bool { - self.universe().cannot_name(self.max_placeholder_universe_reached) + pub(crate) fn has_incompatible_universes(&self) -> bool { + self.min_universe().cannot_name(self.max_placeholder_universe_reached) } } @@ -163,7 +165,7 @@ pub struct RegionInferenceContext<'tcx> { /// The SCC computed from `constraints` and the constraint /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. - constraint_sccs: Rc<ConstraintSccs>, + constraint_sccs: ConstraintSccs, /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required @@ -401,7 +403,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universal_regions: Rc<UniversalRegions<'tcx>>, placeholder_indices: Rc<PlaceholderIndices>, universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>, - outlives_constraints: OutlivesConstraintSet<'tcx>, + mut outlives_constraints: OutlivesConstraintSet<'tcx>, member_constraints_in: MemberConstraintSet<'tcx, RegionVid>, universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>, type_tests: Vec<TypeTest<'tcx>>, @@ -419,17 +421,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|info| RegionDefinition::new(info.universe, info.origin)) .collect(); - let fr_static = universal_regions.fr_static; + let constraint_sccs = + outlives_constraints.add_outlives_static(&universal_regions, &definitions); let constraints = Frozen::freeze(outlives_constraints); let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); - let constraint_sccs = { - let constraint_graph = constraints.graph(definitions.len()); - let region_graph = &constraint_graph.region_graph(&constraints, fr_static); - let sccs = ConstraintSccs::new_with_annotation(®ion_graph, |r| { - RegionTracker::new(r, &definitions[r]) - }); - Rc::new(sccs) - }; if cfg!(debug_assertions) { sccs_info(infcx, &constraint_sccs); @@ -548,21 +543,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } NllRegionVariableOrigin::Placeholder(placeholder) => { - // Each placeholder region is only visible from - // its universe `ui` and its extensions. So we - // can't just add it into `scc` unless the - // universe of the scc can name this region. - let scc_universe = self.scc_universe(scc); - if scc_universe.can_name(placeholder.universe) { - self.scc_values.add_element(scc, placeholder); - } else { - debug!( - "init_free_and_bound_regions: placeholder {:?} is \ - not compatible with universe {:?} of its SCC {:?}", - placeholder, scc_universe, scc, - ); - self.add_incompatible_universe(scc); - } + self.scc_values.add_element(scc, placeholder); } NllRegionVariableOrigin::Existential { .. } => { @@ -744,23 +725,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// (which is assured by iterating over SCCs in dependency order). #[instrument(skip(self), level = "debug")] fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) { - let constraint_sccs = self.constraint_sccs.clone(); - // Walk each SCC `B` such that `A: B`... - for &scc_b in constraint_sccs.successors(scc_a) { + for &scc_b in self.constraint_sccs.successors(scc_a) { debug!(?scc_b); - - // ...and add elements from `B` into `A`. One complication - // arises because of universes: If `B` contains something - // that `A` cannot name, then `A` can only contain `B` if - // it outlives static. - if self.universe_compatible(scc_b, scc_a) { - // `A` can name everything that is in `B`, so just - // merge the bits. - self.scc_values.add_region(scc_a, scc_b); - } else { - self.add_incompatible_universe(scc_a); - } + self.scc_values.add_region(scc_a, scc_b); } // Now take member constraints into account. @@ -814,7 +782,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if !self.constraint_sccs().annotation(scc).universe().is_root() { + if !self.constraint_sccs().annotation(scc).min_universe().is_root() { return; } @@ -886,35 +854,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let universe_a = self.constraint_sccs().annotation(scc_a).universe(); - let universe_b = self.constraint_sccs().annotation(scc_b).universe(); + let a_annotation = self.constraint_sccs().annotation(scc_a); + let b_annotation = self.constraint_sccs().annotation(scc_b); + let a_universe = a_annotation.min_universe(); - // Quick check: if scc_b's declared universe is a subset of + // If scc_b's declared universe is a subset of // scc_a's declared universe (typically, both are ROOT), then // it cannot contain any problematic universe elements. - if universe_a.can_name(universe_b) { + if a_universe.can_name(b_annotation.min_universe()) { return true; } - // Otherwise, we have to iterate over the universe elements in - // B's value, and check whether all of them are nameable - // from universe_a - self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe)) - } - - /// Extend `scc` so that it can outlive some placeholder region - /// from a universe it can't name; at present, the only way for - /// this to be true is if `scc` outlives `'static`. This is - /// actually stricter than necessary: ideally, we'd support bounds - /// like `for<'a: 'b>` that might then allow us to approximate - /// `'a` with `'b` and not `'static`. But it will have to do for - /// now. - fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) { - debug!("add_incompatible_universe(scc={:?})", scc); - - let fr_static = self.universal_regions.fr_static; - self.scc_values.add_all_points(scc); - self.scc_values.add_element(scc, fr_static); + // Otherwise, there can be no placeholder in `b` with a too high + // universe index to name from `a`. + a_universe.can_name(b_annotation.max_placeholder_universe_reached) } /// Once regions have been propagated, this method is used to see @@ -1022,7 +975,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { "lower_bound = {:?} r_scc={:?} universe={:?}", lower_bound, r_scc, - self.constraint_sccs.annotation(r_scc).universe() + self.constraint_sccs.annotation(r_scc).min_universe() ); // If the type test requires that `T: 'a` where `'a` is a @@ -1539,7 +1492,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The minimum universe of any variable reachable from this /// SCC, inside or outside of it. fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.constraint_sccs().annotation(scc).universe() + self.constraint_sccs().annotation(scc).min_universe() } /// Checks the final value for the free region `fr` to see if it /// grew too large. In particular, examine what `end(X)` points @@ -1896,6 +1849,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // This loop can be hot. for constraint in outgoing_edges_from_graph { + if matches!(constraint.category, ConstraintCategory::IllegalUniverse) { + debug!("Ignoring illegal universe constraint: {constraint:?}"); + continue; + } handle_constraint(constraint); } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 67e5c8352df..c0e91ce32e3 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; @@ -285,7 +285,7 @@ impl<'tcx> InferCtxt<'tcx> { } if let Err(guar) = - check_opaque_type_parameter_valid(self.tcx, opaque_type_key, instantiated_ty.span) + check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span) { return Ty::new_error(self.tcx, guar); } @@ -294,6 +294,10 @@ impl<'tcx> InferCtxt<'tcx> { .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false) .ty; + if let Err(e) = definition_ty.error_reported() { + return Ty::new_error(self.tcx, e); + } + // `definition_ty` does not live in of the current inference context, // so lets make sure that we don't accidentally misuse our current `infcx`. match check_opaque_type_well_formed( @@ -387,10 +391,11 @@ fn check_opaque_type_well_formed<'tcx>( /// [rustc-dev-guide chapter]: /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html fn check_opaque_type_parameter_valid<'tcx>( - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, opaque_type_key: OpaqueTypeKey<'tcx>, span: Span, ) -> Result<(), ErrorGuaranteed> { + let tcx = infcx.tcx; let opaque_generics = tcx.generics_of(opaque_type_key.def_id); let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); @@ -420,7 +425,7 @@ fn check_opaque_type_parameter_valid<'tcx>( opaque_env.param_is_error(i)?; - return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { + return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam { ty: arg, kind, span, @@ -438,7 +443,7 @@ fn check_opaque_type_parameter_valid<'tcx>( .collect(); #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] - return Err(tcx + return Err(infcx .dcx() .struct_span_err(span, "non-defining opaque type use in defining scope") .with_span_note(spans, format!("{descr} used multiple times")) diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 7553e3ee04f..431a704687d 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -11,8 +11,8 @@ use rustc_middle::traits::query::OutlivesBound; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::solve::deeply_normalize; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index b777e01f7a6..6d6425b5f19 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -34,7 +34,7 @@ pub(super) fn generate<'mir, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, elements: &Rc<DenseLocationMap>, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, move_data: &MoveData<'tcx>, ) { debug!("liveness::generate"); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 359c4ea0eb1..eb86c8d06f1 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -43,7 +43,7 @@ pub(super) fn trace<'mir, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, elements: &Rc<DenseLocationMap>, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec<Local>, boring_locals: Vec<Local>, @@ -101,7 +101,7 @@ pub(super) fn trace<'mir, 'tcx>( } /// Contextual state for the type-liveness coroutine. -struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { +struct LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx> { /// Current type-checker, giving us our inference context etc. typeck: &'me mut TypeChecker<'typeck, 'tcx>, @@ -119,7 +119,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { /// Results of dataflow tracking which variables (and paths) have been /// initialized. - flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'flow, 'tcx>>, + flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'a, 'flow, 'tcx>>, /// Index indicating where each variable is assigned, used, or /// dropped. @@ -131,8 +131,8 @@ struct DropData<'tcx> { region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, } -struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { - cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>, +struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> { + cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>, /// Set of points that define the current local. defs: BitSet<PointIndex>, @@ -153,8 +153,8 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { stack: Vec<PointIndex>, } -impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { - fn new(cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>) -> Self { +impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> { + fn new(cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>) -> Self { let num_points = cx.elements.num_points(); LivenessResults { cx, @@ -507,7 +507,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { } } -impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { +impl<'tcx> LivenessContext<'_, '_, '_, '_, 'tcx> { /// Returns `true` if the local variable (or some part of it) is initialized at the current /// cursor position. Callers should call one of the `seek` methods immediately before to point /// the cursor to the desired location. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 81bde14a82f..db4b5209145 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -38,6 +38,7 @@ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; @@ -49,6 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; +use crate::renumber::RegionCtxt; use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::{ borrow_set::BorrowSet, @@ -129,7 +131,7 @@ pub(crate) fn type_check<'mir, 'tcx>( location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, all_facts: &mut Option<AllFacts>, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc<DenseLocationMap>, upvars: &[&ty::CapturedPlace<'tcx>], @@ -1352,7 +1354,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // FIXME: check the values } - TerminatorKind::Call { func, args, destination, call_source, target, .. } => { + TerminatorKind::Call { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { + let call_source = match term.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + self.check_operand(func, term_location); for arg in args { self.check_operand(&arg.node, term_location); @@ -1425,7 +1434,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - self.check_call_dest(body, term, &sig, *destination, *target, term_location); + if let TerminatorKind::Call { destination, target, .. } = term.kind { + self.check_call_dest(body, term, &sig, destination, target, term_location); + } // The ordinary liveness rules will ensure that all // regions in the type of the callee are live here. We @@ -1443,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .add_location(region_vid, term_location); } - self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source); + self.check_call_inputs(body, term, func, &sig, args, term_location, call_source); } TerminatorKind::Assert { cond, msg, .. } => { self.check_operand(cond, term_location); @@ -1675,6 +1686,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!(self, block_data, "return on cleanup block") } } + TerminatorKind::TailCall { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "tailcall on cleanup block") + } + } TerminatorKind::CoroutineDrop { .. } => { if is_cleanup { span_mirbug!(self, block_data, "coroutine_drop in cleanup block") @@ -2319,7 +2335,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => { + let mut normalize = |t| self.normalize(t, location); + let src_tail = + tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ()); + let dst_tail = + tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ()); + + // This checks (lifetime part of) vtable validity for pointer casts, + // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits). + // + // Note that other checks (such as denying `dyn Send` -> `dyn Debug`) are in `rustc_hir_typeck`. + if let ty::Dynamic(src_tty, ..) = src_tail.kind() + && let ty::Dynamic(dst_tty, ..) = dst_tail.kind() + && src_tty.principal().is_some() + && dst_tty.principal().is_some() + { + // Remove auto traits. + // Auto trait checks are handled in `rustc_hir_typeck` as FCW. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_static, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_static, + ty::Dyn, + )); + + // Replace trait object lifetimes with fresh vars, to allow casts like + // `*mut dyn FnOnce() + 'a` -> `*mut dyn FnOnce() + 'static`, + let src_obj = + freshen_single_trait_object_lifetime(self.infcx, src_obj); + let dst_obj = + freshen_single_trait_object_lifetime(self.infcx, dst_obj); + + debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); + + self.eq_types( + src_obj, + dst_obj, + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ) + .unwrap(); + } + } _ => { span_mirbug!( self, @@ -2842,3 +2908,16 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { Ok(output) } } + +fn freshen_single_trait_object_lifetime<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Ty<'tcx> { + let &ty::Dynamic(tty, _, dyn_kind @ ty::Dyn) = ty.kind() else { bug!("expected trait object") }; + + let fresh = infcx + .next_region_var(rustc_infer::infer::RegionVariableOrigin::MiscVariable(DUMMY_SP), || { + RegionCtxt::Unknown + }); + infcx.tcx.mk_ty_from_kind(ty::Dynamic(tty, fresh, dyn_kind)) +} diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index b9a82046e59..02b9c2d48b1 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,8 +1,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases}; -use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation}; -use rustc_infer::infer::NllRegionVariableOrigin; +use rustc_infer::infer::relate::{ + PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, +}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_infer::traits::solve::Goal; use rustc_infer::traits::Obligation; use rustc_middle::mir::ConstraintCategory; @@ -522,7 +523,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx } } -impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { +impl<'bccx, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> { fn span(&self) -> Span { self.locations.span(self.type_checker.body) } diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs index 25e1f6268e0..e2de6b8b4a9 100644 --- a/compiler/rustc_borrowck/src/used_muts.rs +++ b/compiler/rustc_borrowck/src/used_muts.rs @@ -45,10 +45,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { /// MIR visitor for collecting used mutable variables. /// The 'visit lifetime represents the duration of the MIR walk. -struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'cx, 'tcx> { +struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'infcx, 'tcx> { temporary_used_locals: FxIndexSet<Local>, never_initialized_mut_locals: &'visit mut FxIndexSet<Local>, - mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx>, + mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx>, } impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> { diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 2d1269e1b6a..b56bfa98357 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -17,6 +17,9 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm -> *[false] clobber_abi, options }, or additional template string +builtin_macros_asm_expected_string_literal = expected string literal + .label = not a string literal + builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option @@ -25,6 +28,8 @@ builtin_macros_asm_modifier_invalid = asm template modifier must be a single cha builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive +builtin_macros_asm_no_matched_argument_name = there is no argument named `{$name}` + builtin_macros_asm_noreturn = asm outputs are not allowed with the `noreturn` option builtin_macros_asm_opt_already_provided = the `{$symbol}` option was already provided @@ -228,10 +233,16 @@ builtin_macros_only_one_argument = {$name} takes 1 argument builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` +builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions = the `#[{$path}]` attribute may only be used on bare functions + +builtin_macros_proc_macro_attribute_only_usable_with_crate_type = the `#[{$path}]` attribute is only usable with crates of the `proc-macro` crate type + builtin_macros_requires_cfg_pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required +builtin_macros_source_uitls_expected_item = expected item, found `{$token}` + builtin_macros_takes_no_arguments = {$name} takes no arguments builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 64238e81b26..dd0f9aaf221 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -390,9 +390,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, } Err(opt_lit) => { let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.dcx().struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - return Err(err); + return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span })); } }; @@ -639,14 +637,13 @@ fn expand_preparsed_asm( match args.named_args.get(&Symbol::intern(name)) { Some(&idx) => Some(idx), None => { - let msg = format!("there is no argument named `{name}`"); let span = arg.position_span; ecx.dcx() - .struct_span_err( - template_span + .create_err(errors::AsmNoMatchedArgumentName { + name: name.to_owned(), + span: template_span .from_inner(InnerSpan::new(span.start, span.end)), - msg, - ) + }) .emit(); None } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index a98cb6f0f76..c664891dad5 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -298,7 +298,7 @@ impl<'cx, 'a> Context<'cx, 'a> { // sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test. ExprKind::Assign(_, _, _) | ExprKind::AssignOp(_, _, _) - | ExprKind::Gen(_, _, _) + | ExprKind::Gen(_, _, _, _) | ExprKind::Await(_, _) | ExprKind::Block(_, _) | ExprKind::Break(_, _) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 03aff6f9633..b09975c0ba7 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -38,16 +38,14 @@ pub(crate) fn cfg_eval( lint_node_id: NodeId, ) -> Annotatable { let features = Some(features); - CfgEval { cfg: &mut StripUnconfigured { sess, features, config_tokens: true, lint_node_id } } + CfgEval(StripUnconfigured { sess, features, config_tokens: true, lint_node_id }) .configure_annotatable(annotatable) // Since the item itself has already been configured by the `InvocationCollector`, // we know that fold result vector will contain exactly one element. .unwrap() } -struct CfgEval<'a, 'b> { - cfg: &'a mut StripUnconfigured<'b>, -} +struct CfgEval<'a>(StripUnconfigured<'a>); fn flat_map_annotatable( vis: &mut impl MutVisitor, @@ -125,9 +123,9 @@ fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { res.is_break() } -impl CfgEval<'_, '_> { +impl CfgEval<'_> { fn configure<T: HasAttrs + HasTokens>(&mut self, node: T) -> Option<T> { - self.cfg.configure(node) + self.0.configure(node) } fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> { @@ -195,8 +193,8 @@ impl CfgEval<'_, '_> { // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrTokenStream` (specifically, we capture - // `AttrTokenTree::AttributesData` for all occurrences of `#[cfg]` and `#[cfg_attr]`) - let mut parser = Parser::new(&self.cfg.sess.psess, orig_tokens, None); + // `AttrTokenTree::AttrsTarget` for all occurrences of `#[cfg]` and `#[cfg_attr]`) + let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); parser.capture_cfg = true; match parse_annotatable_with(&mut parser) { Ok(a) => annotatable = a, @@ -212,16 +210,16 @@ impl CfgEval<'_, '_> { } } -impl MutVisitor for CfgEval<'_, '_> { +impl MutVisitor for CfgEval<'_> { #[instrument(level = "trace", skip(self))] fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { - self.cfg.configure_expr(expr, false); + self.0.configure_expr(expr, false); mut_visit::noop_visit_expr(expr, self); } #[instrument(level = "trace", skip(self))] fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) { - self.cfg.configure_expr(expr, true); + self.0.configure_expr(expr, true); mut_visit::noop_visit_expr(expr, self); } diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 577523a1d5a..7a65ed97f00 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -240,7 +240,7 @@ fn has_a_default_variant(item: &Annotatable) -> bool { if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) { ControlFlow::Break(()) } else { - // no need to subrecurse. + // no need to walk the variant, we are only looking for top level variants ControlFlow::Continue(()) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs index ea054a7e355..bbc7cd39627 100644 --- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs +++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs @@ -3,8 +3,9 @@ use std::mem::swap; use ast::HasAttrs; use rustc_ast::{ self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, - TraitBoundModifiers, + TraitBoundModifiers, VariantData, }; +use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -24,11 +25,43 @@ pub fn expand_deriving_smart_ptr( _is_const: bool, ) { let (name_ident, generics) = if let Annotatable::Item(aitem) = item - && let ItemKind::Struct(_, g) = &aitem.kind + && let ItemKind::Struct(struct_data, g) = &aitem.kind { + let is_transparent = aitem.attrs.iter().any(|attr| { + attr::find_repr_attrs(cx.sess, attr) + .into_iter() + .any(|r| matches!(r, attr::ReprTransparent)) + }); + if !is_transparent { + cx.dcx() + .struct_span_err( + span, + "`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`", + ) + .emit(); + return; + } + if !matches!( + struct_data, + VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _) + if !fields.is_empty()) + { + cx.dcx() + .struct_span_err( + span, + "`SmartPointer` can only be derived on `struct`s with at least one field", + ) + .emit(); + return; + } (aitem.ident, g) } else { - cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit(); + cx.dcx() + .struct_span_err( + span, + "`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`", + ) + .emit(); return; }; diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index ed2f98f2a39..49d640436c2 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -729,6 +729,14 @@ pub(crate) struct AsmExpectedComma { } #[derive(Diagnostic)] +#[diag(builtin_macros_asm_expected_string_literal)] +pub(crate) struct AsmExpectedStringLiteral { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] #[diag(builtin_macros_asm_underscore_input)] pub(crate) struct AsmUnderscoreInput { #[primary_span] @@ -782,6 +790,14 @@ pub(crate) struct AsmNoReturn { } #[derive(Diagnostic)] +#[diag(builtin_macros_asm_no_matched_argument_name)] +pub(crate) struct AsmNoMatchedArgumentName { + pub(crate) name: String, + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] #[diag(builtin_macros_asm_mayunwind)] pub(crate) struct AsmMayUnwind { #[primary_span] @@ -872,3 +888,27 @@ pub(crate) struct TakesNoArguments<'a> { pub span: Span, pub name: &'a str, } + +#[derive(Diagnostic)] +#[diag(builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions)] +pub(crate) struct AttributeOnlyBeUsedOnBareFunctions<'a> { + #[primary_span] + pub span: Span, + pub path: &'a str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_proc_macro_attribute_only_usable_with_crate_type)] +pub(crate) struct AttributeOnlyUsableWithCrateType<'a> { + #[primary_span] + pub span: Span, + pub path: &'a str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_source_uitls_expected_item)] +pub(crate) struct ExpectedItem<'a> { + #[primary_span] + pub span: Span, + pub token: &'a str, +} diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 99d0191958d..a8a595ea579 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -214,12 +214,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { }; if !is_fn { - let msg = format!( - "the `#[{}]` attribute may only be used on bare functions", - pprust::path_to_string(&attr.get_normal_item().path), - ); - - self.dcx.span_err(attr.span, msg); + self.dcx + .create_err(errors::AttributeOnlyBeUsedOnBareFunctions { + span: attr.span, + path: &pprust::path_to_string(&attr.get_normal_item().path), + }) + .emit(); return; } @@ -228,12 +228,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { } if !self.is_proc_macro_crate { - let msg = format!( - "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type", - pprust::path_to_string(&attr.get_normal_item().path), - ); - - self.dcx.span_err(attr.span, msg); + self.dcx + .create_err(errors::AttributeOnlyUsableWithCrateType { + span: attr.span, + path: &pprust::path_to_string(&attr.get_normal_item().path), + }) + .emit(); return; } diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index dc1d82df0c3..44db12cf695 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,3 +1,4 @@ +use crate::errors; use crate::util::{ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, }; @@ -165,9 +166,13 @@ pub(crate) fn expand_include<'cx>( Ok(Some(item)) => ret.push(item), Ok(None) => { if self.p.token != token::Eof { - let token = pprust::token_to_string(&self.p.token); - let msg = format!("expected item, found `{token}`"); - self.p.dcx().span_err(self.p.token.span, msg); + self.p + .dcx() + .create_err(errors::ExpectedItem { + span: self.p.token.span, + token: &pprust::token_to_string(&self.p.token), + }) + .emit(); } break; diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index b7063f35a3e..1ed6f8fc359 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -55,10 +55,6 @@ jobs: if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' run: rustup set default-host x86_64-apple-darwin - - name: Select XCode version - if: matrix.os == 'macos-latest' - run: sudo xcode-select -s /Applications/Xcode_14.3.1.app - - name: Prepare dependencies run: ./y.sh prepare diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 1f5a6513f63..a2ae3d63fb9 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -66,6 +66,9 @@ jobs: env: TARGET_TRIPLE: aarch64-unknown-linux-gnu apt_deps: gcc-aarch64-linux-gnu qemu-user + - os: macos-latest + env: + TARGET_TRIPLE: aarch64-apple-darwin - os: ubuntu-latest env: TARGET_TRIPLE: s390x-unknown-linux-gnu @@ -108,10 +111,6 @@ jobs: sudo apt-get update sudo apt-get install -y ${{ matrix.apt_deps }} - - name: Select XCode version - if: matrix.os == 'macos-latest' - run: sudo xcode-select -s /Applications/Xcode_14.3.1.app - - name: Prepare dependencies run: ./y.sh prepare @@ -214,6 +213,9 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: x86_64-apple-darwin + - os: macos-latest + env: + TARGET_TRIPLE: aarch64-apple-darwin # cross-compile from Linux to Windows using mingw - os: ubuntu-latest env: @@ -248,10 +250,6 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-mingw-w64-x86-64 - - name: Select XCode version - if: matrix.os == 'macos-latest' - run: sudo xcode-select -s /Applications/Xcode_14.3.1.app - - name: Prepare dependencies run: ./y.sh prepare @@ -282,7 +280,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ github.ref == 'refs/heads/master' }} - needs: [rustfmt, test, bench, dist] + # FIXME add the bench job back to the dependency list once rust-lang/rust#125493 gets merged + needs: [rustfmt, test, dist] permissions: contents: write # for creating the dev tag and release diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 33fe52ddbdd..15c9e9d66fa 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -46,18 +46,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b27922a6879b5b5361d0a084cb0b1941bf109a98540addcb932da13b68bed4" +checksum = "0b6b33d7e757a887989eb18b35712b2a67d96171ec3149d1bfb657b29b7b367c" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304c455b28bf56372729acb356afbb55d622f2b0f2f7837aa5e57c138acaac4d" +checksum = "b9acf15cb22be42d07c3b57d7856329cb228b7315d385346149df2566ad5e4aa" dependencies = [ "bumpalo", "cranelift-bforest", @@ -70,45 +70,46 @@ dependencies = [ "hashbrown 0.14.3", "log", "regalloc2", + "rustc-hash", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1653c56b99591d07f67c5ca7f9f25888948af3f4b97186bff838d687d666f613" +checksum = "e934d301392b73b3f8b0540391fb82465a0f179a3cee7c726482ac4727efcc97" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5b6a9cf6b6eb820ee3f973a0db313c05dc12d370f37b4fe9630286e1672573f" +checksum = "8afb2a2566b3d54b854dfb288b3b187f6d3d17d6f762c92898207eba302931da" [[package]] name = "cranelift-control" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d06e6bf30075fb6bed9e034ec046475093392eea1aff90eb5c44c4a033d19a" +checksum = "0100f33b704cdacd01ad66ff41f8c5030d57cbff078e2a4e49ab1822591299fa" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29be04f931b73cdb9694874a295027471817f26f26d2f0ebe5454153176b6e3a" +checksum = "a8cfdc315e5d18997093e040a8d234bea1ac1e118a716d3e30f40d449e78207b" [[package]] name = "cranelift-frontend" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07fd7393041d7faa2f37426f5dc7fc04003b70988810e8c063beefeff1cd8f9" +checksum = "0f74b84f16af2e982b0c0c72233503d9d55cbfe3865dbe807ca28dc6642a28b5" dependencies = [ "cranelift-codegen", "log", @@ -118,15 +119,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f341d7938caa6dff8149dac05bb2b53fc680323826b83b4cf175ab9f5139a3c9" +checksum = "adf306d3dde705fb94bd48082f01d38c4ededc74293a4c007805f610bf08bc6e" [[package]] name = "cranelift-jit" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42733555e06433f1461570e09dbd756dafc228b4dac75c597cdbdc518de07522" +checksum = "f5c5cfb8bbd3339cd25cca30e7516ff8fe5cb1feeddde6980cc4d5ef34df97bb" dependencies = [ "anyhow", "cranelift-codegen", @@ -144,9 +145,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84950af02bb85f3da764d53a953b43bb29a732e793d4fe24637a61591be9a024" +checksum = "7c9b0d4269b36fd858e6d8f20cd4938941186fb831488c361888cb2d6b33a9a6" dependencies = [ "anyhow", "cranelift-codegen", @@ -155,9 +156,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82af6066e6448d26eeabb7aa26a43f7ff79f8217b06bade4ee6ef230aecc8880" +checksum = "1ea0ebdef7aff4a79bcbc8b6495f31315f16b3bf311152f472eaa8d679352581" dependencies = [ "cranelift-codegen", "libc", @@ -166,9 +167,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.107.0" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00af56107039ed150391df6f753298c7b08f2b6a2e0727d216b5fa599d684d8b" +checksum = "19e33439ec20db058bc7cc3410f9748ab1ad90a35cef713d625c736f43e3820d" dependencies = [ "anyhow", "cranelift-codegen", @@ -278,9 +279,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "object" -version = "0.33.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "crc32fast", "hashbrown 0.14.3", @@ -410,10 +411,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "20.0.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9f93a3289057b26dc75eb84d6e60d7694f7d169c7c09597495de6e016a13ff" +checksum = "5afe2f0499542f9a4bcfa1b55bfdda803b6ade4e7c93c6b99e0f39dba44b0a91" dependencies = [ + "anyhow", "cfg-if", "libc", "windows-sys", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 2015cdbcc2a..2969a6cf6ec 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,15 +8,15 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.107.0", default-features = false, features = ["std", "unwind", "all-arch"] } -cranelift-frontend = { version = "0.107.0" } -cranelift-module = { version = "0.107.0" } -cranelift-native = { version = "0.107.0" } -cranelift-jit = { version = "0.107.0", optional = true } -cranelift-object = { version = "0.107.0" } +cranelift-codegen = { version = "0.109.0", default-features = false, features = ["std", "unwind", "all-arch"] } +cranelift-frontend = { version = "0.109.0" } +cranelift-module = { version = "0.109.0" } +cranelift-native = { version = "0.109.0" } +cranelift-jit = { version = "0.109.0", optional = true } +cranelift-object = { version = "0.109.0" } target-lexicon = "0.12.0" gimli = { version = "0.28", default-features = false, features = ["write"]} -object = { version = "0.33", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } indexmap = "2.0.0" libloading = { version = "0.8.0", optional = true } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 00ea15cb38c..eb21e027dd0 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -70,7 +70,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system |FreeBSD|✅[^no-rustup]|❓|❓|❓| |AIX|❌[^xcoff]|N/A|N/A|❌[^xcoff]| |Other unixes|❓|❓|❓|❓| -|macOS|✅|❌[^apple-silicon]|N/A|N/A| +|macOS|✅|✅[^no-rustup]|N/A|N/A| |Windows|✅[^no-rustup]|❌|N/A|N/A| ✅: Fully supported and tested @@ -80,7 +80,6 @@ For more docs on how to build and test see [build_system/usage.txt](build_system Not all targets are available as rustup component for nightly. See notes in the platform support matrix. [^xcoff]: XCOFF object file format is not supported. -[^apple-silicon]: Tracked in [#1248](https://github.com/rust-lang/rustc_codegen_cranelift/issues/1248). [^no-rustup]: Not available as rustup component for nightly. You can build it yourself. ## Usage diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 196ff8fda75..dfbe0f51e7b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -267,10 +267,6 @@ fn build_clif_sysroot_for_triple( prefix.to_str().unwrap() )); } - rustflags.push("-Zunstable-options".to_owned()); - for (name, values) in EXTRA_CHECK_CFGS { - rustflags.push(check_cfg_arg(name, *values)); - } compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); if channel == "release" { @@ -330,34 +326,3 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> { Some(target_libs) } - -// Copied from https://github.com/rust-lang/rust/blob/4fd98a4b1b100f5329c6efae18031791f64372d2/src/bootstrap/src/utils/helpers.rs#L569-L585 -/// Create a `--check-cfg` argument invocation for a given name -/// and it's values. -fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String { - // Creating a string of the values by concatenating each value: - // ',values("tvos","watchos")' or '' (nothing) when there are no values. - let next = match values { - Some(values) => { - let mut tmp = values.iter().flat_map(|val| [",", "\"", val, "\""]).collect::<String>(); - - tmp.insert_str(1, "values("); - tmp.push(')'); - tmp - } - None => "".to_string(), - }; - format!("--check-cfg=cfg({name}{next})") -} - -const EXTRA_CHECK_CFGS: &[(&str, Option<&[&str]>)] = &[ - ("bootstrap", None), - ("stdarch_intel_sde", None), - ("no_fp_fmt_parse", None), - ("no_global_oom_handling", None), - ("no_rc", None), - ("no_sync", None), - ("netbsd10", None), - ("backtrace_in_libstd", None), - ("target_arch", Some(&["xtensa"])), -]; diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 278f334796a..790d9cbd9fc 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -329,7 +329,6 @@ pub(crate) fn run_tests( struct TestRunner<'a> { is_native: bool, jit_supported: bool, - use_unstable_features: bool, skip_tests: &'a [&'a str], dirs: Dirs, target_compiler: Compiler, @@ -361,15 +360,7 @@ impl<'a> TestRunner<'a> { && target_compiler.triple.contains("x86_64") && !target_compiler.triple.contains("windows"); - Self { - is_native, - jit_supported, - use_unstable_features, - skip_tests, - dirs, - target_compiler, - stdlib_source, - } + Self { is_native, jit_supported, skip_tests, dirs, target_compiler, stdlib_source } } fn run_testsuite(&self, tests: &[TestCase]) { @@ -393,31 +384,13 @@ impl<'a> TestRunner<'a> { match *cmd { TestCaseCmd::Custom { func } => func(self), TestCaseCmd::BuildLib { source, crate_types } => { - if self.use_unstable_features { - self.run_rustc([source, "--crate-type", crate_types]); - } else { - self.run_rustc([ - source, - "--crate-type", - crate_types, - "--cfg", - "no_unstable_features", - ]); - } + self.run_rustc([source, "--crate-type", crate_types]); } TestCaseCmd::BuildBin { source } => { - if self.use_unstable_features { - self.run_rustc([source]); - } else { - self.run_rustc([source, "--cfg", "no_unstable_features"]); - } + self.run_rustc([source]); } TestCaseCmd::BuildBinAndRun { source, args } => { - if self.use_unstable_features { - self.run_rustc([source]); - } else { - self.run_rustc([source, "--cfg", "no_unstable_features"]); - } + self.run_rustc([source]); self.run_out_command( source.split('/').last().unwrap().split('.').next().unwrap(), args, @@ -472,7 +445,6 @@ impl<'a> TestRunner<'a> { cmd.arg(&self.target_compiler.triple); cmd.arg("-Cpanic=abort"); cmd.arg("-Zunstable-options"); - cmd.arg("--check-cfg=cfg(no_unstable_features)"); cmd.arg("--check-cfg=cfg(jit)"); cmd.args(args); cmd diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index aab20f67248..7d361a9ab2b 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -333,12 +333,7 @@ fn main() { #[cfg(all(not(jit), not(all(windows, target_env = "gnu"))))] test_tls(); - #[cfg(all( - not(jit), - not(no_unstable_features), - target_arch = "x86_64", - any(target_os = "linux", target_os = "macos") - ))] + #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))] unsafe { global_asm_test(); naked_test(); @@ -367,17 +362,12 @@ fn stack_val_align() { assert_eq!(&a as *const Foo as usize % 8192, 0); } -#[cfg(all( - not(jit), - not(no_unstable_features), - target_arch = "x86_64", - any(target_os = "linux", target_os = "macos") -))] +#[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))] extern "C" { fn global_asm_test(); } -#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "linux"))] +#[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] global_asm! { " .global global_asm_test @@ -387,7 +377,7 @@ global_asm! { " } -#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "macos"))] +#[cfg(all(not(jit), target_arch = "x86_64", target_os = "macos"))] global_asm! { " .global _global_asm_test @@ -397,7 +387,7 @@ global_asm! { " } -#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64"))] +#[cfg(all(not(jit), target_arch = "x86_64"))] #[naked] extern "C" fn naked_test() { unsafe { diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 7347b2e7789..6cedd84adfe 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -251,6 +251,9 @@ unsafe fn test_simd() { test_mm_add_epi8(); test_mm_add_pd(); test_mm_cvtepi8_epi16(); + #[cfg(not(jit))] + test_mm_cvtps_epi32(); + test_mm_cvttps_epi32(); test_mm_cvtsi128_si64(); test_mm_extract_epi8(); @@ -476,6 +479,41 @@ unsafe fn test_mm256_permutevar8x32_epi32() { assert_eq_m256i(r, e); } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +#[cfg(not(jit))] +unsafe fn test_mm_cvtps_epi32() { + let floats: [f32; 4] = [1.5, -2.5, i32::MAX as f32 + 1.0, f32::NAN]; + + let float_vec = _mm_loadu_ps(floats.as_ptr()); + let int_vec = _mm_cvtps_epi32(float_vec); + + let mut ints: [i32; 4] = [0; 4]; + _mm_storeu_si128(ints.as_mut_ptr() as *mut __m128i, int_vec); + + // this is very different from `floats.map(|f| f as i32)`! + let expected_ints: [i32; 4] = [2, -2, i32::MIN, i32::MIN]; + + assert_eq!(ints, expected_ints); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn test_mm_cvttps_epi32() { + let floats: [f32; 4] = [1.5, -2.5, i32::MAX as f32 + 1.0, f32::NAN]; + + let float_vec = _mm_loadu_ps(floats.as_ptr()); + let int_vec = _mm_cvttps_epi32(float_vec); + + let mut ints: [i32; 4] = [0; 4]; + _mm_storeu_si128(ints.as_mut_ptr() as *mut __m128i, int_vec); + + // this is very different from `floats.map(|f| f as i32)`! + let expected_ints: [i32; 4] = [1, -2, i32::MIN, i32::MIN]; + + assert_eq!(ints, expected_ints); +} + fn test_checked_mul() { let u: Option<u8> = u8::from_str_radix("1000", 10).ok(); assert_eq!(u, None); diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml index c8c7b45bc9a..9ea53e8f848 100644 --- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml +++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml @@ -4,12 +4,12 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.29.0", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] @@ -134,6 +134,17 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -147,9 +158,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -189,9 +200,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "compiler_builtins", "memchr", @@ -286,9 +297,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -396,8 +407,6 @@ dependencies = [ "core", "getopts", "libc", - "panic_abort", - "panic_unwind", "std", ] @@ -430,7 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.28.1", "rustc-std-workspace-core", ] diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index a2ba79cbe90..cfa91744a0e 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-13" +channel = "nightly-2024-06-30" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 689cda21643..c1b7e4b0e07 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -36,9 +36,8 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR rm tests/ui/asm/x86_64/evex512-implicit-feature.rs # unimplemented AVX512 x86 vendor intrinsic # exotic linkages -rm tests/ui/issues/issue-33992.rs # unsupported linkages -rm tests/incremental/hashes/function_interfaces.rs # same -rm tests/incremental/hashes/statics.rs # same +rm tests/incremental/hashes/function_interfaces.rs +rm tests/incremental/hashes/statics.rs # variadic arguments rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs @@ -60,13 +59,20 @@ rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported # requires LTO rm -r tests/run-make/cdylib -rm -r tests/run-make/issue-14500 -rm -r tests/run-make/issue-64153 rm -r tests/run-make/codegen-options-parsing rm -r tests/run-make/lto-* rm -r tests/run-make/reproducible-build-2 rm -r tests/run-make/issue-109934-lto-debuginfo rm -r tests/run-make/no-builtins-lto +rm -r tests/run-make/reachable-extern-fn-available-lto + +# coverage instrumentation +rm tests/ui/consts/precise-drop-with-coverage.rs +rm tests/ui/issues/issue-85461.rs +rm -r tests/ui/instrument-coverage/ + +# missing f16/f128 support +rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs # optimization tests # ================== @@ -74,6 +80,7 @@ rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations rm tests/ui/codegen/init-large-type.rs # same rm tests/ui/issues/issue-40883.rs # same rm -r tests/run-make/fmt-write-bloat/ # tests an optimization +rm tests/ui/statics/const_generics.rs # same # backend specific tests # ====================== @@ -85,6 +92,7 @@ rm -r tests/run-make/sepcomp-cci-copies # same rm -r tests/run-make/volatile-intrinsics # same rm -r tests/run-make/llvm-ident # same rm -r tests/run-make/no-builtins-attribute # same +rm -r tests/run-make/pgo-gen-no-imp-symbols # same rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific @@ -93,13 +101,14 @@ rm -r tests/run-make/print-to-output # requires --print relocation-models # requires asm, llvm-ir and/or llvm-bc emit support # ============================================= rm -r tests/run-make/emit-named-files -rm -r tests/run-make/issue-30063 rm -r tests/run-make/multiple-emits rm -r tests/run-make/output-type-permutations rm -r tests/run-make/emit-to-stdout rm -r tests/run-make/compressed-debuginfo rm -r tests/run-make/symbols-include-type-name - +rm -r tests/run-make/notify-all-emit-artifacts +rm -r tests/run-make/reset-codegen-1 +rm -r tests/run-make/inline-always-many-cgu # giving different but possibly correct results # ============================================= @@ -118,6 +127,7 @@ rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contain # ============ rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort +rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation warning for -Cinline-threshold # bugs in the test suite # ====================== @@ -148,12 +158,12 @@ index 9607ff02f96..b7d97caf9a2 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -34,8 +34,6 @@ pub fn bare() -> Self { - /// Construct a \`rustdoc\` invocation with \`-L \$(TARGET_RPATH_DIR)\` set. + #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); -- let target_rpath_dir = env::var_os("TARGET_RPATH_DIR").unwrap(); +- let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); - cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); - Self { cmd, stdin: None } + Self { cmd } } EOF diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 695dbaf2804..9dc94ab33ea 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -5,8 +5,9 @@ mod pass_mode; mod returning; use std::borrow::Cow; +use std::mem; -use cranelift_codegen::ir::SigRef; +use cranelift_codegen::ir::{ArgumentPurpose, SigRef}; use cranelift_codegen::isa::CallConv; use cranelift_module::ModuleError; use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; @@ -17,7 +18,7 @@ use rustc_middle::ty::TypeVisitableExt; use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_target::abi::call::{Conv, FnAbi}; +use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::spec::abi::Abi; use self::pass_mode::*; @@ -370,9 +371,14 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Handle special calls like intrinsics and empty drop glue. let instance = if let ty::FnDef(def_id, fn_args) = *func.layout().ty.kind() { - let instance = - ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args) - .polymorphize(fx.tcx); + let instance = ty::Instance::expect_resolve( + fx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + fn_args, + source_info.span, + ) + .polymorphize(fx.tcx); if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { if target.is_some() { @@ -487,6 +493,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let args = args; assert_eq!(fn_abi.args.len(), args.len()); + #[derive(Copy, Clone)] enum CallTarget { Direct(FuncRef), Indirect(SigRef, Value), @@ -532,7 +539,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( }; self::returning::codegen_with_call_return_arg(fx, &fn_abi.ret, ret_place, |fx, return_ptr| { - let call_args = return_ptr + let mut call_args = return_ptr .into_iter() .chain(first_arg_override.into_iter()) .chain( @@ -545,47 +552,118 @@ pub(crate) fn codegen_terminator_call<'tcx>( ) .collect::<Vec<Value>>(); - let call_inst = match func_ref { + // FIXME: Find a cleaner way to support varargs. + if fn_abi.c_variadic { + adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args); + } + + match func_ref { CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args), CallTarget::Indirect(sig, func_ptr) => { fx.bcx.ins().call_indirect(sig, func_ptr, &call_args) } + } + }); + + if let Some(dest) = target { + let ret_block = fx.get_block(dest); + fx.bcx.ins().jump(ret_block, &[]); + } else { + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + } + + fn adjust_call_for_c_variadic<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + source_info: mir::SourceInfo, + target: CallTarget, + call_args: &mut Vec<Value>, + ) { + if fn_abi.conv != Conv::C { + fx.tcx.dcx().span_fatal( + source_info.span, + format!("Variadic call for non-C abi {:?}", fn_abi.conv), + ); + } + let sig_ref = match target { + CallTarget::Direct(func_ref) => fx.bcx.func.dfg.ext_funcs[func_ref].signature, + CallTarget::Indirect(sig_ref, _) => sig_ref, }; + // `mem::take()` the `params` so that `fx.bcx` can be used below. + let mut abi_params = mem::take(&mut fx.bcx.func.dfg.signatures[sig_ref].params); + + // Recalculate the parameters in the signature to ensure the signature contains the variadic arguments. + let has_return_arg = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); + // Drop everything except the return argument (if there is one). + abi_params.truncate(if has_return_arg { 1 } else { 0 }); + // Add the fixed arguments. + abi_params.extend( + fn_abi.args[..fn_abi.fixed_count as usize] + .iter() + .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()), + ); + let fixed_arg_count = abi_params.len(); + // Add the variadic arguments. + abi_params.extend( + fn_abi.args[fn_abi.fixed_count as usize..] + .iter() + .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()), + ); - // FIXME find a cleaner way to support varargs - if fn_sig.c_variadic() { - if !matches!(fn_sig.abi(), Abi::C { .. }) { + if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" { + // Add any padding arguments needed for Apple AArch64. + // There's no need to pad the argument list unless variadic arguments are actually being + // passed. + if abi_params.len() > fixed_arg_count { + // 128-bit integers take 2 registers, and everything else takes 1. + // FIXME: Add support for non-integer types + // This relies on the checks below to ensure all arguments are integer types and + // that the ABI is "C". + // The return argument isn't counted as it goes in its own dedicated register. + let integer_registers_used: usize = abi_params + [if has_return_arg { 1 } else { 0 }..fixed_arg_count] + .iter() + .map(|arg| if arg.value_type.bits() == 128 { 2 } else { 1 }) + .sum(); + // The ABI uses 8 registers before it starts pushing arguments to the stack. Pad out + // the registers if needed to ensure the variadic arguments are passed on the stack. + if integer_registers_used < 8 { + abi_params.splice( + fixed_arg_count..fixed_arg_count, + (integer_registers_used..8).map(|_| AbiParam::new(types::I64)), + ); + call_args.splice( + fixed_arg_count..fixed_arg_count, + (integer_registers_used..8).map(|_| fx.bcx.ins().iconst(types::I64, 0)), + ); + } + } + + // `StructArgument` is not currently used by the `aarch64` ABI, and is therefore not + // handled when calculating how many padding arguments to use. Assert that this remains + // the case. + assert!(abi_params.iter().all(|param| matches!( + param.purpose, + // The only purposes used are `Normal` and `StructReturn`. + ArgumentPurpose::Normal | ArgumentPurpose::StructReturn + ))); + } + + // Check all parameters are integers. + for param in abi_params.iter() { + if !param.value_type.is_int() { + // FIXME: Set %al to upperbound on float args once floats are supported. fx.tcx.dcx().span_fatal( source_info.span, - format!("Variadic call for non-C abi {:?}", fn_sig.abi()), + format!("Non int ty {:?} for variadic call", param.value_type), ); } - let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap(); - let abi_params = call_args - .into_iter() - .map(|arg| { - let ty = fx.bcx.func.dfg.value_type(arg); - if !ty.is_int() { - // FIXME set %al to upperbound on float args once floats are supported - fx.tcx.dcx().span_fatal( - source_info.span, - format!("Non int ty {:?} for variadic call", ty), - ); - } - AbiParam::new(ty) - }) - .collect::<Vec<AbiParam>>(); - fx.bcx.func.dfg.signatures[sig_ref].params = abi_params; } - call_inst - }); + assert_eq!(abi_params.len(), call_args.len()); - if let Some(dest) = target { - let ret_block = fx.get_block(dest); - fx.bcx.ins().jump(ret_block, &[]); - } else { - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + // Put the `AbiParam`s back in the signature. + fx.bcx.func.dfg.signatures[sig_ref].params = abi_params; } } diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index e8af3e8c255..b4a3825e996 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -11,15 +11,10 @@ use rustc_session::config::OomStrategy; use crate::prelude::*; /// Returns whether an allocator shim was created -pub(crate) fn codegen( - tcx: TyCtxt<'_>, - module: &mut impl Module, - unwind_context: &mut UnwindContext, -) -> bool { +pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool { let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; codegen_inner( module, - unwind_context, kind, tcx.alloc_error_handler_kind(()).unwrap(), tcx.sess.opts.unstable_opts.oom, @@ -28,8 +23,7 @@ pub(crate) fn codegen( } fn codegen_inner( - module: &mut impl Module, - unwind_context: &mut UnwindContext, + module: &mut dyn Module, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind, oom_strategy: OomStrategy, @@ -67,7 +61,6 @@ fn codegen_inner( }; crate::common::create_wrapper_function( module, - unwind_context, sig, &global_fn_name(method.name), &default_fn_name(method.name), @@ -82,7 +75,6 @@ fn codegen_inner( }; crate::common::create_wrapper_function( module, - unwind_context, sig, "__rust_alloc_error_handler", &alloc_error_handler_name(alloc_error_handler_kind), diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index b117dc496c2..5adbbb09ac8 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -249,9 +249,7 @@ pub(crate) fn compile_fn( } // Define debuginfo for function - let isa = module.isa(); let debug_context = &mut cx.debug_context; - let unwind_context = &mut cx.unwind_context; cx.profiler.generic_activity("generate debug info").run(|| { if let Some(debug_context) = debug_context { codegened_func.func_debug_cx.unwrap().finalize( @@ -260,7 +258,6 @@ pub(crate) fn compile_fn( context, ); } - unwind_context.add_function(codegened_func.func_id, &context, isa); }); } @@ -494,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) }); } + // FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls + TerminatorKind::TailCall { fn_span, .. } => span_bug!( + *fn_span, + "tail calls are not yet supported in `rustc_codegen_cranelift` backend" + ), TerminatorKind::InlineAsm { template, operands, @@ -909,7 +911,7 @@ fn codegen_stmt<'tcx>( | StatementKind::PlaceMention(..) | StatementKind::AscribeUserType(..) => {} - StatementKind::Coverage { .. } => fx.tcx.dcx().fatal("-Zcoverage is unimplemented"), + StatementKind::Coverage { .. } => unreachable!(), StatementKind::Intrinsic(ref intrinsic) => match &**intrinsic { // We ignore `assume` intrinsics, they are only useful for optimizations NonDivergingIntrinsic::Assume(_) => {} diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 21d0cd2d30f..09317139936 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -247,7 +247,6 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool { pub(crate) fn create_wrapper_function( module: &mut dyn Module, - unwind_context: &mut UnwindContext, sig: Signature, wrapper_name: &str, callee_name: &str, @@ -280,7 +279,6 @@ pub(crate) fn create_wrapper_function( bcx.finalize(); } module.define_function(wrapper_func_id, &mut ctx).unwrap(); - unwind_context.add_function(wrapper_func_id, &ctx, module.isa()); } pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { @@ -395,6 +393,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { // FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets // a way to specify stack slot alignment. size: (size + abi_align - 1) / abi_align * abi_align, + align_shift: 4, }); Pointer::stack_slot(stack_slot) } else { @@ -405,6 +404,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { // FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets // a way to specify stack slot alignment. size: (size + align) / abi_align * abi_align, + align_shift: 4, }); let base_ptr = self.bcx.ins().stack_addr(self.pointer_type, stack_slot, 0); let misalign_offset = self.bcx.ins().urem_imm(base_ptr, i64::from(align)); diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 87c5da3b7c3..fd34ed88c0b 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -155,7 +155,7 @@ pub(crate) fn codegen_const_value<'tcx>( fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } } - GlobalAlloc::Function(instance) => { + GlobalAlloc::Function { instance, .. } => { let func_id = crate::abi::import_function(fx.tcx, fx.module, instance); let local_func_id = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); @@ -351,7 +351,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant TodoItem::Alloc(alloc_id) => { let alloc = match tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => alloc, - GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => { + GlobalAlloc::Function { .. } + | GlobalAlloc::Static(_) + | GlobalAlloc::VTable(..) => { unreachable!() } }; @@ -415,7 +417,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let reloc_target_alloc = tcx.global_alloc(alloc_id); let data_id = match reloc_target_alloc { - GlobalAlloc::Function(instance) => { + GlobalAlloc::Function { instance, .. } => { assert_eq!(addend, 0); let func_id = crate::abi::import_function(tcx, module, instance.polymorphize(tcx)); @@ -565,6 +567,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } + TerminatorKind::TailCall { .. } => return None, TerminatorKind::Call { .. } => {} } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index dcafac21bc7..763d9a48407 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -26,6 +26,7 @@ use rustc_session::Session; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; use crate::debuginfo::TypeDebugContext; use crate::global_asm::GlobalAsmConfig; +use crate::unwind_module::UnwindModule; use crate::{prelude::*, BackendConfig}; struct ModuleCodegenResult { @@ -318,7 +319,11 @@ fn produce_final_output_artifacts( // These are used in linking steps and will be cleaned up afterward. } -fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) -> ObjectModule { +fn make_module( + sess: &Session, + backend_config: &BackendConfig, + name: String, +) -> UnwindModule<ObjectModule> { let isa = crate::build_isa(sess, backend_config); let mut builder = @@ -327,16 +332,15 @@ fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) -> // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections // can easily double the amount of time necessary to perform linking. builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false)); - ObjectModule::new(builder) + UnwindModule::new(ObjectModule::new(builder), true) } fn emit_cgu( output_filenames: &OutputFilenames, prof: &SelfProfilerRef, name: String, - module: ObjectModule, + module: UnwindModule<ObjectModule>, debug: Option<DebugContext>, - unwind_context: UnwindContext, global_asm_object_file: Option<PathBuf>, producer: &str, ) -> Result<ModuleCodegenResult, String> { @@ -346,8 +350,6 @@ fn emit_cgu( debug.emit(&mut product); } - unwind_context.emit(&mut product); - let module_regular = emit_module( output_filenames, prof, @@ -494,7 +496,6 @@ fn module_codegen( let mut cx = crate::CodegenCx::new( tcx, - backend_config.clone(), module.isa(), tcx.sess.opts.debuginfo != DebugInfo::None, cgu_name, @@ -531,13 +532,7 @@ fn module_codegen( } } } - crate::main_shim::maybe_create_entry_wrapper( - tcx, - &mut module, - &mut cx.unwind_context, - false, - cgu.is_primary(), - ); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, false, cgu.is_primary()); let cgu_name = cgu.name().as_str().to_owned(); @@ -571,7 +566,6 @@ fn module_codegen( cgu_name, module, cx.debug_context, - cx.unwind_context, global_asm_object_file, &producer, ) @@ -665,13 +659,10 @@ pub(crate) fn run_aot( }); let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string()); - let mut allocator_unwind_context = UnwindContext::new(allocator_module.isa(), true); - let created_alloc_shim = - crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); + let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); let allocator_module = if created_alloc_shim { - let mut product = allocator_module.finish(); - allocator_unwind_context.emit(&mut product); + let product = allocator_module.finish(); match emit_module( tcx.output_filenames(()), diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 4b149131b61..dfee8e714e6 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -14,12 +14,12 @@ use rustc_session::Session; use rustc_span::Symbol; use crate::debuginfo::TypeDebugContext; +use crate::unwind_module::UnwindModule; use crate::{prelude::*, BackendConfig}; use crate::{CodegenCx, CodegenMode}; struct JitState { - backend_config: BackendConfig, - jit_module: JITModule, + jit_module: UnwindModule<JITModule>, } thread_local! { @@ -63,7 +63,7 @@ fn create_jit_module( tcx: TyCtxt<'_>, backend_config: &BackendConfig, hotswap: bool, -) -> (JITModule, CodegenCx) { +) -> (UnwindModule<JITModule>, CodegenCx) { let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string()); let isa = crate::build_isa(tcx.sess, backend_config); @@ -72,17 +72,11 @@ fn create_jit_module( crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); - let mut jit_module = JITModule::new(jit_builder); + let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); - let mut cx = crate::CodegenCx::new( - tcx, - backend_config.clone(), - jit_module.isa(), - false, - Symbol::intern("dummy_cgu_name"), - ); + let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, Symbol::intern("dummy_cgu_name")); - crate::allocator::codegen(tcx, &mut jit_module, &mut cx.unwind_context); + crate::allocator::codegen(tcx, &mut jit_module); (jit_module, cx) } @@ -128,7 +122,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { ); } CodegenMode::JitLazy => { - codegen_shim(tcx, &mut cx, &mut cached_context, &mut jit_module, inst) + codegen_shim(tcx, &mut cached_context, &mut jit_module, inst) } }, MonoItem::Static(def_id) => { @@ -146,18 +140,11 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { tcx.dcx().fatal("Inline asm is not supported in JIT mode"); } - crate::main_shim::maybe_create_entry_wrapper( - tcx, - &mut jit_module, - &mut cx.unwind_context, - true, - true, - ); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, true, true); tcx.dcx().abort_if_errors(); - jit_module.finalize_definitions().unwrap(); - unsafe { cx.unwind_context.register_jit(&jit_module) }; + jit_module.finalize_definitions(); println!( "Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed" @@ -177,12 +164,12 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { call_conv: jit_module.target_config().default_call_conv, }; let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap(); - let finalized_start: *const u8 = jit_module.get_finalized_function(start_func_id); + let finalized_start: *const u8 = jit_module.module.get_finalized_function(start_func_id); LAZY_JIT_STATE.with(|lazy_jit_state| { let mut lazy_jit_state = lazy_jit_state.borrow_mut(); assert!(lazy_jit_state.is_none()); - *lazy_jit_state = Some(JitState { backend_config, jit_module }); + *lazy_jit_state = Some(JitState { jit_module }); }); let f: extern "C" fn(c_int, *const *const c_char) -> c_int = @@ -268,7 +255,6 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> let mut lazy_jit_state = lazy_jit_state.borrow_mut(); let lazy_jit_state = lazy_jit_state.as_mut().unwrap(); let jit_module = &mut lazy_jit_state.jit_module; - let backend_config = lazy_jit_state.backend_config.clone(); let name = tcx.symbol_name(instance).name; let sig = crate::abi::get_function_sig( @@ -278,7 +264,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> ); let func_id = jit_module.declare_function(name, Linkage::Export, &sig).unwrap(); - let current_ptr = jit_module.read_got_entry(func_id); + let current_ptr = jit_module.module.read_got_entry(func_id); // If the function's GOT entry has already been updated to point at something other // than the shim trampoline, don't re-jit but just return the new pointer instead. @@ -288,11 +274,10 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> return current_ptr; } - jit_module.prepare_for_function_redefine(func_id).unwrap(); + jit_module.module.prepare_for_function_redefine(func_id).unwrap(); let mut cx = crate::CodegenCx::new( tcx, - backend_config, jit_module.isa(), false, Symbol::intern("dummy_cgu_name"), @@ -300,9 +285,8 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance); assert!(cx.global_asm.is_empty()); - jit_module.finalize_definitions().unwrap(); - unsafe { cx.unwind_context.register_jit(&jit_module) }; - jit_module.get_finalized_function(func_id) + jit_module.finalize_definitions(); + jit_module.module.get_finalized_function(func_id) }) }) } @@ -310,7 +294,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) -> fn dep_symbol_lookup_fn( sess: &Session, crate_info: CrateInfo, -) -> Box<dyn Fn(&str) -> Option<*const u8>> { +) -> Box<dyn Fn(&str) -> Option<*const u8> + Send> { use rustc_middle::middle::dependency_format::Linkage; let mut dylib_paths = Vec::new(); @@ -362,9 +346,8 @@ fn dep_symbol_lookup_fn( fn codegen_shim<'tcx>( tcx: TyCtxt<'tcx>, - cx: &mut CodegenCx, cached_context: &mut Context, - module: &mut JITModule, + module: &mut UnwindModule<JITModule>, inst: Instance<'tcx>, ) { let pointer_type = module.target_config().pointer_type(); @@ -413,5 +396,4 @@ fn codegen_shim<'tcx>( trampoline_builder.ins().return_(&ret_vals); module.define_function(func_id, context).unwrap(); - cx.unwind_context.add_function(func_id, context, module.isa()); } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index c6b26dd873b..c88230c9360 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -113,13 +113,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( ); let sig = get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance); - create_wrapper_function( - fx.module, - &mut fx.cx.unwind_context, - sig, - &wrapper_name, - symbol.name, - ); + create_wrapper_function(fx.module, sig, &wrapper_name, symbol.name); CInlineAsmOperand::Symbol { symbol: wrapper_name } } else { @@ -283,13 +277,7 @@ pub(crate) fn codegen_naked_asm<'tcx>( ); let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance); - create_wrapper_function( - module, - &mut cx.unwind_context, - sig, - &wrapper_name, - symbol.name, - ); + create_wrapper_function(module, sig, &wrapper_name, symbol.name); CInlineAsmOperand::Symbol { symbol: wrapper_name } } else { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index d454f3c1de7..399518e58d8 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -459,11 +459,20 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( intrinsic_args!(fx, args => (a); intrinsic); let a = a.load_scalar(fx); + let value = fx.bcx.ins().x86_cvtt2dq(types::I32X4, a); + let cvalue = CValue::by_val(value, ret.layout()); + ret.write_cvalue(fx, cvalue); + } + "llvm.x86.sse2.cvtps2dq" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_epi32 + intrinsic_args!(fx, args => (a); intrinsic); + let a = a.load_scalar(fx); + // Using inline asm instead of fcvt_to_sint_sat as unrepresentable values are turned // into 0x80000000 for which Cranelift doesn't have a native instruction. codegen_inline_asm_inner( fx, - &[InlineAsmTemplatePiece::String(format!("cvttps2dq xmm0, xmm0"))], + &[InlineAsmTemplatePiece::String(format!("cvtps2dq xmm0, xmm0"))], &[CInlineAsmOperand::InOut { reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), _late: true, @@ -1416,6 +1425,36 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( ret.write_cvalue(fx, res); } + "llvm.x86.rdtsc" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_rdtsc&ig_expand=5273 + + let res_place = CPlace::new_stack_slot( + fx, + fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u32, fx.tcx.types.u32])), + ); + let eax_place = res_place.place_field(fx, FieldIdx::new(0)); + let edx_place = res_place.place_field(fx, FieldIdx::new(1)); + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String("rdtsc".to_string())], + &[ + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)), + late: true, + place: Some(eax_place), + }, + CInlineAsmOperand::Out { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)), + late: true, + place: Some(edx_place), + }, + ], + InlineAsmOptions::NOSTACK | InlineAsmOptions::NOMEM, + ); + let res = res_place.to_cvalue(fx); + ret.write_cvalue_transmute(fx, res); + } + _ => { fx.tcx .dcx() diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 06ca52b3903..192e6c91ea3 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -79,6 +79,7 @@ mod pretty_clif; mod toolchain; mod trap; mod unsize; +mod unwind_module; mod value_and_place; mod vtable; @@ -130,22 +131,13 @@ struct CodegenCx { global_asm: String, inline_asm_index: Cell<usize>, debug_context: Option<DebugContext>, - unwind_context: UnwindContext, cgu_name: Symbol, } impl CodegenCx { - fn new( - tcx: TyCtxt<'_>, - backend_config: BackendConfig, - isa: &dyn TargetIsa, - debug_info: bool, - cgu_name: Symbol, - ) -> Self { + fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, debug_info: bool, cgu_name: Symbol) -> Self { assert_eq!(pointer_ty(tcx), isa.pointer_type()); - let unwind_context = - UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot)); let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows { Some(DebugContext::new(tcx, isa, cgu_name.as_str())) } else { @@ -158,7 +150,6 @@ impl CodegenCx { global_asm: String::new(), inline_asm_index: Cell::new(0), debug_context, - unwind_context, cgu_name, } } @@ -175,7 +166,7 @@ impl CodegenBackend for CraneliftCodegenBackend { } fn init(&self, sess: &Session) { - use rustc_session::config::Lto; + use rustc_session::config::{InstrumentCoverage, Lto}; match sess.lto() { Lto::No | Lto::ThinLocal => {} Lto::Thin | Lto::Fat => { @@ -183,6 +174,11 @@ impl CodegenBackend for CraneliftCodegenBackend { } } + if sess.opts.cg.instrument_coverage() != InstrumentCoverage::No { + sess.dcx() + .fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift"); + } + let mut config = self.config.borrow_mut(); if config.is_none() { let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args) diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index f9a729618a5..fe0a1551419 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -4,6 +4,7 @@ use rustc_middle::ty::AssocKind; use rustc_middle::ty::GenericArg; use rustc_session::config::{sigpipe, EntryFnType}; use rustc_span::symbol::Ident; +use rustc_span::DUMMY_SP; use crate::prelude::*; @@ -11,8 +12,7 @@ use crate::prelude::*; /// users main function. pub(crate) fn maybe_create_entry_wrapper( tcx: TyCtxt<'_>, - module: &mut impl Module, - unwind_context: &mut UnwindContext, + module: &mut dyn Module, is_jit: bool, is_primary_cgu: bool, ) { @@ -36,12 +36,11 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn, sigpipe); + create_entry_fn(tcx, module, main_def_id, is_jit, is_main_fn, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, - m: &mut impl Module, - unwind_context: &mut UnwindContext, + m: &mut dyn Module, rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, is_main_fn: bool, @@ -121,6 +120,7 @@ pub(crate) fn maybe_create_entry_wrapper( ParamEnv::reveal_all(), report.def_id, tcx.mk_args(&[GenericArg::from(main_ret_ty)]), + DUMMY_SP, ) .polymorphize(tcx); @@ -146,6 +146,7 @@ pub(crate) fn maybe_create_entry_wrapper( ParamEnv::reveal_all(), start_def_id, tcx.mk_args(&[main_ret_ty.into()]), + DUMMY_SP, ) .polymorphize(tcx); let start_func_id = import_function(tcx, m, start_instance); @@ -170,7 +171,5 @@ pub(crate) fn maybe_create_entry_wrapper( if let Err(err) = m.define_function(cmain_func_id, &mut ctx) { tcx.dcx().fatal(format!("entry symbol `{entry_name}` defined multiple times: {err}")); } - - unwind_context.add_function(cmain_func_id, &ctx, m.isa()); } } diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs new file mode 100644 index 00000000000..b950aaa29ce --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs @@ -0,0 +1,115 @@ +use cranelift_codegen::control::ControlPlane; +use cranelift_codegen::ir::{Function, Signature}; +use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; +use cranelift_codegen::{Context, FinalizedMachReloc}; +use cranelift_module::{ + DataDescription, DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleDeclarations, + ModuleResult, +}; +use cranelift_object::{ObjectModule, ObjectProduct}; + +use crate::UnwindContext; + +/// A wrapper around a [Module] which adds any defined function to the [UnwindContext]. +pub(crate) struct UnwindModule<T> { + pub(crate) module: T, + unwind_context: UnwindContext, +} + +impl<T: Module> UnwindModule<T> { + pub(crate) fn new(module: T, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(module.isa(), pic_eh_frame); + UnwindModule { module, unwind_context } + } +} + +impl UnwindModule<ObjectModule> { + pub(crate) fn finish(self) -> ObjectProduct { + let mut product = self.module.finish(); + self.unwind_context.emit(&mut product); + product + } +} + +#[cfg(feature = "jit")] +impl UnwindModule<cranelift_jit::JITModule> { + pub(crate) fn finalize_definitions(&mut self) { + self.module.finalize_definitions().unwrap(); + let prev_unwind_context = std::mem::replace( + &mut self.unwind_context, + UnwindContext::new(self.module.isa(), false), + ); + unsafe { prev_unwind_context.register_jit(&self.module) }; + } +} + +impl<T: Module> Module for UnwindModule<T> { + fn isa(&self) -> &dyn TargetIsa { + self.module.isa() + } + + fn declarations(&self) -> &ModuleDeclarations { + self.module.declarations() + } + + fn get_name(&self, name: &str) -> Option<FuncOrDataId> { + self.module.get_name(name) + } + + fn target_config(&self) -> TargetFrontendConfig { + self.module.target_config() + } + + fn declare_function( + &mut self, + name: &str, + linkage: Linkage, + signature: &Signature, + ) -> ModuleResult<FuncId> { + self.module.declare_function(name, linkage, signature) + } + + fn declare_anonymous_function(&mut self, signature: &Signature) -> ModuleResult<FuncId> { + self.module.declare_anonymous_function(signature) + } + + fn declare_data( + &mut self, + name: &str, + linkage: Linkage, + writable: bool, + tls: bool, + ) -> ModuleResult<DataId> { + self.module.declare_data(name, linkage, writable, tls) + } + + fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> { + self.module.declare_anonymous_data(writable, tls) + } + + fn define_function_with_control_plane( + &mut self, + func: FuncId, + ctx: &mut Context, + ctrl_plane: &mut ControlPlane, + ) -> ModuleResult<()> { + self.module.define_function_with_control_plane(func, ctx, ctrl_plane)?; + self.unwind_context.add_function(func, ctx, self.module.isa()); + Ok(()) + } + + fn define_function_bytes( + &mut self, + _func_id: FuncId, + _func: &Function, + _alignment: u64, + _bytes: &[u8], + _relocs: &[FinalizedMachReloc], + ) -> ModuleResult<()> { + unimplemented!() + } + + fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> { + self.module.define_data(data_id, data) + } +} diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 548c23cc794..fa8a1ec037c 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -28,6 +28,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { global // TODO(antoyo): set linkage. } + + pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { + if value.get_type() == self.bool_type.make_pointer() { + if let Some(pointee) = typ.get_pointee() { + if pointee.dyncast_vector().is_some() { + panic!() + } + } + } + // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some + // SIMD builtins require a constant value. + self.bitcast_if_needed(value, typ) + } } pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { @@ -207,7 +220,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } value } - GlobalAlloc::Function(fn_instance) => self.get_fn_addr(fn_instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), GlobalAlloc::VTable(ty, trait_ref) => { let alloc = self .tcx @@ -239,19 +252,6 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { const_alloc_to_gcc(self, alloc) } - fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { - if value.get_type() == self.bool_type.make_pointer() { - if let Some(pointee) = typ.get_pointee() { - if pointee.dyncast_vector().is_some() { - panic!() - } - } - } - // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some - // SIMD builtins require a constant value. - self.bitcast_if_needed(value, typ) - } - fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { self.context .new_array_access(None, base_addr, self.const_usize(offset.bytes())) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 4a1f5188a80..1d689c9ac0e 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; -use rustc_span::{source_map::respan, Span}; +use rustc_span::{source_map::respan, Span, DUMMY_SP}; use rustc_target::abi::{ call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx, }; @@ -27,7 +27,6 @@ use crate::callee::get_fn; use crate::common::SignType; pub struct CodegenCx<'gcc, 'tcx> { - pub check_overflow: bool, pub codegen_unit: &'tcx CodegenUnit<'tcx>, pub context: &'gcc Context<'gcc>, @@ -134,8 +133,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { tcx: TyCtxt<'tcx>, supports_128bit_integers: bool, ) -> Self { - let check_overflow = tcx.sess.overflow_checks(); - let create_type = |ctype, rust_type| { let layout = tcx.layout_of(ParamEnv::reveal_all().and(rust_type)).unwrap(); let align = layout.align.abi.bytes(); @@ -271,7 +268,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } let mut cx = Self { - check_overflow, codegen_unit, context, current_func: RefCell::new(None), @@ -483,6 +479,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ty::ParamEnv::reveal_all(), def_id, ty::List::empty(), + DUMMY_SP, ); let symbol_name = tcx.symbol_name(instance).name; @@ -511,10 +508,6 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { &self.tcx.sess } - fn check_overflow(&self) -> bool { - self.check_overflow - } - fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { self.codegen_unit } diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 68471b028be..4caff2e6310 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -89,13 +89,34 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { ty::FloatTy::F128 => self.type_f128(), } } -} -impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { - fn type_i1(&self) -> Type<'gcc> { + pub fn type_i1(&self) -> Type<'gcc> { self.bool_type } + pub fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> { + let types = fields.to_vec(); + if let Some(typ) = self.struct_types.borrow().get(fields) { + return *typ; + } + let fields: Vec<_> = fields + .iter() + .enumerate() + .map(|(index, field)| { + self.context.new_field(None, *field, format!("field{}_TODO", index)) + }) + .collect(); + let typ = self.context.new_struct_type(None, "struct", &fields).as_type(); + if packed { + #[cfg(feature = "master")] + typ.set_packed(); + } + self.struct_types.borrow_mut().insert(types, typ); + typ + } +} + +impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn type_i8(&self) -> Type<'gcc> { self.i8_type } @@ -131,7 +152,7 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn type_f64(&self) -> Type<'gcc> { self.double_type } - + fn type_f128(&self) -> Type<'gcc> { unimplemented!("f16_f128") } @@ -140,27 +161,6 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.context.new_function_pointer_type(None, return_type, params, false) } - fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> { - let types = fields.to_vec(); - if let Some(typ) = self.struct_types.borrow().get(fields) { - return *typ; - } - let fields: Vec<_> = fields - .iter() - .enumerate() - .map(|(index, field)| { - self.context.new_field(None, *field, format!("field{}_TODO", index)) - }) - .collect(); - let typ = self.context.new_struct_type(None, "struct", &fields).as_type(); - if packed { - #[cfg(feature = "master")] - typ.set_packed(); - } - self.struct_types.borrow_mut().insert(types, typ); - typ - } - fn type_kind(&self, typ: Type<'gcc>) -> TypeKind { if self.is_int_type_or_bool(typ) { TypeKind::Integer diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a6a3f0f9646..d034f9b5256 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -226,7 +226,8 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { // when passed by value, making it smaller. // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes // when passed by value, making it larger. - let copy_bytes = cmp::min(scratch_size.bytes(), self.layout.size.bytes()); + let copy_bytes = + cmp::min(cast.unaligned_size(bx).bytes(), self.layout.size.bytes()); // Allocate some scratch space... let llscratch = bx.alloca(scratch_size, scratch_align); bx.lifetime_start(llscratch, scratch_size); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 48693895da1..cd82894af18 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -2,7 +2,7 @@ use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{FunctionReturn, OptLevel}; use rustc_span::symbol::sym; @@ -53,6 +53,34 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll } } +#[inline] +fn patchable_function_entry_attrs<'ll>( + cx: &CodegenCx<'ll, '_>, + attr: Option<PatchableFunctionEntry>, +) -> SmallVec<[&'ll Attribute; 2]> { + let mut attrs = SmallVec::new(); + let patchable_spec = attr.unwrap_or_else(|| { + PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry) + }); + let entry = patchable_spec.entry(); + let prefix = patchable_spec.prefix(); + if entry > 0 { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-entry", + &format!("{}", entry), + )); + } + if prefix > 0 { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-prefix", + &format!("{}", prefix), + )); + } + attrs +} + /// Get LLVM sanitize attributes. #[inline] pub fn sanitize_attrs<'ll>( @@ -421,6 +449,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( llvm::set_alignment(llfn, align); } to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); + to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry)); // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 28a88dd2efe..b72636a6224 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -32,7 +32,7 @@ impl OwnedTargetMachine { unique_section_names: bool, trap_unreachable: bool, singletree: bool, - asm_comments: bool, + verbose_asm: bool, emit_stack_size_section: bool, relax_elf_relocations: bool, use_init_array: bool, @@ -64,7 +64,7 @@ impl OwnedTargetMachine { unique_section_names, trap_unreachable, singletree, - asm_comments, + verbose_asm, emit_stack_size_section, relax_elf_relocations, use_init_array, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5e481eb98f5..2fda19bf0c9 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -214,7 +214,7 @@ pub fn target_machine_factory( sess.opts.unstable_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable); let emit_stack_size_section = sess.opts.unstable_opts.emit_stack_sizes; - let asm_comments = sess.opts.unstable_opts.asm_comments; + let verbose_asm = sess.opts.unstable_opts.verbose_asm; let relax_elf_relocations = sess.opts.unstable_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations); @@ -289,7 +289,7 @@ pub fn target_machine_factory( funique_section_names, trap_unreachable, singlethread, - asm_comments, + verbose_asm, emit_stack_size_section, relax_elf_relocations, use_init_array, diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 4ffc92eb633..fe64649cf70 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -289,8 +289,8 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { (value, AddressSpace::DATA) } } - GlobalAlloc::Function(fn_instance) => ( - self.get_fn_addr(fn_instance.polymorphize(self.tcx)), + GlobalAlloc::Function { instance, .. } => ( + self.get_fn_addr(instance.polymorphize(self.tcx)), self.data_layout().instruction_address_space, ), GlobalAlloc::VTable(ty, trait_ref) => { @@ -329,10 +329,6 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { const_alloc_to_llvm(self, alloc, /*static*/ false) } - fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { - self.const_bitcast(val, ty) - } - fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { unsafe { llvm::LLVMConstInBoundsGEP2( diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 7d92888feee..77beb9a6bb3 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -3,6 +3,7 @@ use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::coverageinfo; use crate::debuginfo; +use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm; use crate::llvm_util; use crate::type_::Type; @@ -27,7 +28,7 @@ use rustc_session::config::{BranchProtection, CFGuard, CFProtection}; use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet}; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{call::FnAbi, HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; @@ -43,7 +44,6 @@ use std::str; /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. pub struct CodegenCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, - pub check_overflow: bool, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, @@ -441,8 +441,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { // start) and then strongly recommending static linkage on Windows! let use_dll_storage_attrs = tcx.sess.target.is_like_windows; - let check_overflow = tcx.sess.overflow_checks(); - let tls_model = to_llvm_tls_model(tcx.sess.tls_model()); let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); @@ -466,7 +464,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { CodegenCx { tcx, - check_overflow, use_dll_storage_attrs, tls_model, llmod, @@ -522,6 +519,15 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { &self.vtables } + fn apply_vcall_visibility_metadata( + &self, + ty: Ty<'tcx>, + poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + vtable: &'ll Value, + ) { + apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable); + } + fn get_fn(&self, instance: Instance<'tcx>) -> &'ll Value { get_fn(self, instance) } @@ -574,6 +580,7 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::ParamEnv::reveal_all(), def_id, ty::List::empty(), + DUMMY_SP, )), _ => { let name = name.unwrap_or("rust_eh_personality"); @@ -596,10 +603,6 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.tcx.sess } - fn check_overflow(&self) -> bool { - self.check_overflow - } - fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { self.codegen_unit } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 8de4e0effad..851a4c42e99 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -705,10 +705,12 @@ impl MsvcBasicName for ty::UintTy { impl MsvcBasicName for ty::FloatTy { fn msvc_basic_name(self) -> &'static str { - // FIXME: f16 and f128 have no MSVC representation. We could improve the debuginfo. - // See: <https://github.com/rust-lang/rust/pull/114607/files#r1454683264> + // FIXME(f16_f128): `f16` and `f128` have no MSVC representation. We could improve the + // debuginfo. See: <https://github.com/rust-lang/rust/issues/121837> match self { - ty::FloatTy::F16 => "half", + ty::FloatTy::F16 => { + bug!("`f16` should have been handled in `build_basic_type_di_node`") + } ty::FloatTy::F32 => "float", ty::FloatTy::F64 => "double", ty::FloatTy::F128 => "fp128", @@ -716,6 +718,38 @@ impl MsvcBasicName for ty::FloatTy { } } +fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreationResult<'ll> { + // MSVC has no native support for `f16`. Instead, emit `struct f16 { bits: u16 }` to allow the + // `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`. + let float_ty = cx.tcx.types.f16; + let bits_ty = cx.tcx.types.u16; + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + UniqueTypeId::for_ty(cx.tcx, float_ty), + "f16", + cx.size_and_align_of(float_ty), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + // Fields: + |cx, float_di_node| { + smallvec![build_field_di_node( + cx, + float_di_node, + "bits", + cx.size_and_align_of(bits_ty), + Size::ZERO, + DIFlags::FlagZero, + type_di_node(cx, bits_ty), + )] + }, + NO_GENERICS, + ) +} + fn build_basic_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, @@ -739,6 +773,9 @@ fn build_basic_type_di_node<'ll, 'tcx>( ty::Char => ("char", DW_ATE_UTF), ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed), ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned), + ty::Float(ty::FloatTy::F16) if cpp_like_debuginfo => { + return build_cpp_f16_di_node(cx); + } ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float), ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed), ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned), @@ -1449,12 +1486,18 @@ fn build_vtable_type_di_node<'ll, 'tcx>( .di_node } -fn vcall_visibility_metadata<'ll, 'tcx>( +pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, trait_ref: Option<PolyExistentialTraitRef<'tcx>>, vtable: &'ll Value, ) { + // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in + // LLVM at the moment. + if !cx.sess().opts.unstable_opts.virtual_function_elimination || cx.sess().lto() != Lto::Fat { + return; + } + enum VCallVisibility { Public = 0, LinkageUnit = 1, @@ -1531,12 +1574,6 @@ pub fn create_vtable_di_node<'ll, 'tcx>( poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, vtable: &'ll Value, ) { - // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in - // LLVM at the moment. - if cx.sess().opts.unstable_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat { - vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable); - } - if cx.dbg_cx.is_none() { return; } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b5b0086f740..e02c61cd296 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1121,8 +1121,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_select_bitmask { let (len, _) = require_simd!(arg_tys[1], SimdArgument); - let expected_int_bits = (len.max(8) - 1).next_power_of_two(); - let expected_bytes = len / 8 + ((len % 8 > 0) as u64); + let expected_int_bits = len.max(8).next_power_of_two(); + let expected_bytes = len.div_ceil(8); let mask_ty = arg_tys[0]; let mask = match mask_ty.kind() { @@ -1379,17 +1379,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_bitmask { - // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a - // vector mask and returns the most significant bit (MSB) of each lane in the form - // of either: + // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a vector mask and + // returns one bit for each lane (which must all be `0` or `!0`) in the form of either: // * an unsigned integer // * an array of `u8` // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits. // // The bit order of the result depends on the byte endianness, LSB-first for little // endian and MSB-first for big endian. - let expected_int_bits = in_len.max(8); - let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64); + let expected_int_bits = in_len.max(8).next_power_of_two(); + let expected_bytes = in_len.div_ceil(8); // Integer vector <i{in_bitwidth} x in_len>: let (i_xn, in_elem_bitwidth) = match in_elem.kind() { @@ -1409,7 +1408,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }), }; - // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. + // LLVM doesn't always know the inputs are `0` or `!0`, so we shift here so it optimizes to + // `pmovmskb` and similar on x86. let shift_indices = vec![ bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 4b7a2643007..ed0989a0ba4 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -274,10 +274,11 @@ impl CodegenBackend for LlvmCodegenBackend { |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) } - fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { + fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { + use std::fmt::Write; match req.kind { PrintKind::RelocationModels => { - writeln!(out, "Available relocation models:"); + writeln!(out, "Available relocation models:").unwrap(); for name in &[ "static", "pic", @@ -288,25 +289,25 @@ impl CodegenBackend for LlvmCodegenBackend { "ropi-rwpi", "default", ] { - writeln!(out, " {name}"); + writeln!(out, " {name}").unwrap(); } - writeln!(out); + writeln!(out).unwrap(); } PrintKind::CodeModels => { - writeln!(out, "Available code models:"); + writeln!(out, "Available code models:").unwrap(); for name in &["tiny", "small", "kernel", "medium", "large"] { - writeln!(out, " {name}"); + writeln!(out, " {name}").unwrap(); } - writeln!(out); + writeln!(out).unwrap(); } PrintKind::TlsModels => { - writeln!(out, "Available TLS models:"); + writeln!(out, "Available TLS models:").unwrap(); for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"] { - writeln!(out, " {name}"); + writeln!(out, " {name}").unwrap(); } - writeln!(out); + writeln!(out).unwrap(); } PrintKind::StackProtectorStrategies => { writeln!( @@ -332,7 +333,8 @@ impl CodegenBackend for LlvmCodegenBackend { none Do not generate stack canaries. "# - ); + ) + .unwrap(); } _other => llvm_util::print(req, out, sess), } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 132e1f9e8fd..08e9e312827 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2185,7 +2185,7 @@ extern "C" { UniqueSectionNames: bool, TrapUnreachable: bool, Singlethread: bool, - AsmComments: bool, + VerboseAsm: bool, EmitStackSizeSection: bool, RelaxELFRelocations: bool, UseInitArray: bool, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 7e0f264a4ae..0e89e66be49 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -6,7 +6,6 @@ use crate::errors::{ use crate::llvm; use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; -use rustc_codegen_ssa::traits::PrintBackendInfo; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; @@ -18,6 +17,7 @@ use rustc_target::spec::{MergeFunctions, PanicStrategy}; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use std::ffi::{c_char, c_void, CStr, CString}; +use std::fmt::Write; use std::path::Path; use std::ptr; use std::slice; @@ -372,7 +372,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { ret } -fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) { +fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMachine) { let mut llvm_target_features = llvm_target_features(tm); let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut rustc_target_features = sess @@ -412,24 +412,26 @@ fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &ll .max() .unwrap_or(0); - writeln!(out, "Features supported by rustc for this target:"); + writeln!(out, "Features supported by rustc for this target:").unwrap(); for (feature, desc) in &rustc_target_features { - writeln!(out, " {feature:max_feature_len$} - {desc}."); + writeln!(out, " {feature:max_feature_len$} - {desc}.").unwrap(); } - writeln!(out, "\nCode-generation features supported by LLVM for this target:"); + writeln!(out, "\nCode-generation features supported by LLVM for this target:").unwrap(); for (feature, desc) in &llvm_target_features { - writeln!(out, " {feature:max_feature_len$} - {desc}."); + writeln!(out, " {feature:max_feature_len$} - {desc}.").unwrap(); } if llvm_target_features.is_empty() { - writeln!(out, " Target features listing is not supported by this LLVM version."); + writeln!(out, " Target features listing is not supported by this LLVM version.") + .unwrap(); } - writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it."); - writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); - writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],"); - writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n"); + writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it.").unwrap(); + writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n") + .unwrap(); + writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],").unwrap(); + writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n").unwrap(); } -pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) { +pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); match req.kind { @@ -440,9 +442,9 @@ pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) { - let out = &mut *(out as *mut &mut dyn PrintBackendInfo); + let out = &mut *(out as *mut &mut String); let bytes = slice::from_raw_parts(string as *const u8, len); - write!(out, "{}", String::from_utf8_lossy(bytes)); + write!(out, "{}", String::from_utf8_lossy(bytes)).unwrap(); } unsafe { llvm::LLVMRustPrintTargetCPUs( diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index a00f09dc40d..f1141c57ced 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -127,13 +127,24 @@ impl<'ll> CodegenCx<'ll, '_> { pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } -} -impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { - fn type_i1(&self) -> &'ll Type { + pub(crate) fn type_i1(&self) -> &'ll Type { unsafe { llvm::LLVMInt1TypeInContext(self.llcx) } } + pub(crate) fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type { + unsafe { + llvm::LLVMStructTypeInContext( + self.llcx, + els.as_ptr(), + els.len() as c_uint, + packed as Bool, + ) + } + } +} + +impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn type_i8(&self) -> &'ll Type { unsafe { llvm::LLVMInt8TypeInContext(self.llcx) } } @@ -178,17 +189,6 @@ impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, False) } } - fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type { - unsafe { - llvm::LLVMStructTypeInContext( - self.llcx, - els.as_ptr(), - els.len() as c_uint, - packed as Bool, - ) - } - } - fn type_kind(&self, ty: &'ll Type) -> TypeKind { unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d509e4ce56d..1f627353d54 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -45,7 +45,7 @@ use tempfile::Builder as TempFileBuilder; use itertools::Itertools; use std::collections::BTreeSet; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fs::{read, File, OpenOptions}; use std::io::{BufWriter, Write}; use std::ops::Deref; @@ -1306,12 +1306,12 @@ fn link_sanitizer_runtime( let filename = format!("rustc{channel}_rt.{name}"); let path = find_sanitizer_runtime(sess, &filename); let rpath = path.to_str().expect("non-utf8 component in path"); - linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); + linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]); linker.link_dylib_by_name(&filename, false, true); } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" { // MSVC provides the `/INFERASANLIBS` argument to automatically find the // compatible ASAN library. - linker.arg("/INFERASANLIBS"); + linker.link_arg("/INFERASANLIBS"); } else { let filename = format!("librustc{channel}_rt.{name}.a"); let path = find_sanitizer_runtime(sess, &filename).join(&filename); @@ -1888,9 +1888,9 @@ fn add_post_link_objects( /// FIXME: Determine where exactly these args need to be inserted. fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { if let Some(args) = sess.target.pre_link_args.get(&flavor) { - cmd.args(args.iter().map(Deref::deref)); + cmd.verbatim_args(args.iter().map(Deref::deref)); } - cmd.args(&sess.opts.unstable_opts.pre_link_args); + cmd.verbatim_args(&sess.opts.unstable_opts.pre_link_args); } /// Add a link script embedded in the target, if applicable. @@ -1908,8 +1908,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty sess.dcx().emit_fatal(errors::LinkScriptWriteFailure { path, error }); } - cmd.arg("--script"); - cmd.arg(path); + cmd.link_arg("--script").link_arg(path); } _ => {} } @@ -1918,7 +1917,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty /// Add arbitrary "user defined" args defined from command line. /// FIXME: Determine where exactly these args need to be inserted. fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) { - cmd.args(&sess.opts.cg.link_args); + cmd.verbatim_args(&sess.opts.cg.link_args); } /// Add arbitrary "late link" args defined by the target spec. @@ -1936,15 +1935,15 @@ fn add_late_link_args( }); if any_dynamic_crate { if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) { - cmd.args(args.iter().map(Deref::deref)); + cmd.verbatim_args(args.iter().map(Deref::deref)); } } else { if let Some(args) = sess.target.late_link_args_static.get(&flavor) { - cmd.args(args.iter().map(Deref::deref)); + cmd.verbatim_args(args.iter().map(Deref::deref)); } } if let Some(args) = sess.target.late_link_args.get(&flavor) { - cmd.args(args.iter().map(Deref::deref)); + cmd.verbatim_args(args.iter().map(Deref::deref)); } } @@ -1952,7 +1951,7 @@ fn add_late_link_args( /// FIXME: Determine where exactly these args need to be inserted. fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { if let Some(args) = sess.target.post_link_args.get(&flavor) { - cmd.args(args.iter().map(Deref::deref)); + cmd.verbatim_args(args.iter().map(Deref::deref)); } } @@ -2097,6 +2096,10 @@ fn add_rpath_args( codegen_results: &CodegenResults, out_filename: &Path, ) { + if !sess.target.has_rpath { + return; + } + // FIXME (#2397): At some point we want to rpath our guesses as to // where extern libraries might live, based on the // add_lib_search_paths @@ -2115,11 +2118,10 @@ fn add_rpath_args( let rpath_config = RPathConfig { libs: &*libs, out_filename: out_filename.to_path_buf(), - has_rpath: sess.target.has_rpath, is_like_osx: sess.target.is_like_osx, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; - cmd.args(&rpath::get_rpath_flags(&rpath_config)); + cmd.cc_args(&rpath::get_rpath_flags(&rpath_config)); } } @@ -2378,7 +2380,7 @@ fn add_order_independent_options( } else { "" }; - cmd.arg(format!("--dynamic-linker={prefix}ld.so.1")); + cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1")); } if sess.target.eh_frame_header { @@ -2393,8 +2395,7 @@ fn add_order_independent_options( } if sess.target.os == "emscripten" { - cmd.arg("-s"); - cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort { "DISABLE_EXCEPTION_CATCHING=1" } else { "DISABLE_EXCEPTION_CATCHING=0" @@ -2402,22 +2403,21 @@ fn add_order_independent_options( } if flavor == LinkerFlavor::Llbc { - cmd.arg("--target"); - cmd.arg(sess.target.llvm_target.as_ref()); - cmd.arg("--target-cpu"); - cmd.arg(&codegen_results.crate_info.target_cpu); + cmd.link_args(&[ + "--target", + sess.target.llvm_target.as_ref(), + "--target-cpu", + &codegen_results.crate_info.target_cpu, + ]); } else if flavor == LinkerFlavor::Ptx { - cmd.arg("--fallback-arch"); - cmd.arg(&codegen_results.crate_info.target_cpu); + cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]); } else if flavor == LinkerFlavor::Bpf { - cmd.arg("--cpu"); - cmd.arg(&codegen_results.crate_info.target_cpu); + cmd.link_args(&["--cpu", &codegen_results.crate_info.target_cpu]); if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features] .into_iter() .find(|feat| !feat.is_empty()) { - cmd.arg("--cpu-features"); - cmd.arg(feat); + cmd.link_args(&["--cpu-features", feat]); } } @@ -2618,7 +2618,11 @@ fn add_native_libs_from_crate( NativeLibKind::WasmImportModule => {} NativeLibKind::LinkArg => { if link_static { - cmd.linker_arg(OsStr::new(name), verbatim); + if verbatim { + cmd.verbatim_arg(name); + } else { + cmd.link_arg(name); + } } } } @@ -2813,6 +2817,15 @@ fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf { } } +fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf { + if let Some(dir) = path.parent() { + let file_name = path.file_name().expect("library path has no file name component"); + rehome_sysroot_lib_dir(sess, dir).join(file_name) + } else { + fix_windows_verbatim_for_gcc(path) + } +} + // Adds the static "rlib" versions of all crates to the command line. // There's a bit of magic which happens here specifically related to LTO, // namely that we remove upstream object files. @@ -2843,15 +2856,8 @@ fn add_static_crate( let src = &codegen_results.crate_info.used_crate_source[&cnum]; let cratepath = &src.rlib.as_ref().unwrap().0; - let mut link_upstream = |path: &Path| { - let rlib_path = if let Some(dir) = path.parent() { - let file_name = path.file_name().expect("rlib path has no file name path component"); - rehome_sysroot_lib_dir(sess, dir).join(file_name) - } else { - fix_windows_verbatim_for_gcc(path) - }; - cmd.link_staticlib_by_path(&rlib_path, false); - }; + let mut link_upstream = + |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false); if !are_upstream_rust_objects_already_included(sess) || ignored_for_lto(sess, &codegen_results.crate_info, cnum) @@ -2915,27 +2921,7 @@ fn add_static_crate( // Same thing as above, but for dynamic crates instead of static crates. fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { - // Just need to tell the linker about where the library lives and - // what its name is - let parent = cratepath.parent(); - // When producing a dll, the MSVC linker may not actually emit a - // `foo.lib` file if the dll doesn't actually export any symbols, so we - // check to see if the file is there and just omit linking to it if it's - // not present. - if sess.target.is_like_msvc && !cratepath.with_extension("dll.lib").exists() { - return; - } - if let Some(dir) = parent { - cmd.include_path(&rehome_sysroot_lib_dir(sess, dir)); - } - // "<dir>/name.dll -> name.dll" on windows-msvc - // "<dir>/name.dll -> name" on windows-gnu - // "<dir>/libname.<ext> -> name" elsewhere - let stem = if sess.target.is_like_msvc { cratepath.file_name() } else { cratepath.file_stem() }; - let stem = stem.unwrap().to_str().unwrap(); - // Convert library file-stem into a cc -l argument. - let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; - cmd.link_dylib_by_name(&stem[prefix..], false, true); + cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true); } fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { @@ -3012,10 +2998,10 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { // This is admittedly a bit strange, as on most targets // `-isysroot` only applies to include header files, but on Apple // targets this also applies to libraries and frameworks. - cmd.args(&["-isysroot", &sdk_root]); + cmd.cc_args(&["-isysroot", &sdk_root]); } LinkerFlavor::Darwin(Cc::No, _) => { - cmd.args(&["-syslibroot", &sdk_root]); + cmd.link_args(&["-syslibroot", &sdk_root]); } _ => unreachable!(), } @@ -3026,8 +3012,9 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { // search path. // The flags are called `-L` and `-F` both in Clang, ld64 and ldd. - cmd.arg(format!("-L{sdk_root}/System/iOSSupport/usr/lib")); - cmd.arg(format!("-F{sdk_root}/System/iOSSupport/System/Library/Frameworks")); + let sdk_root = Path::new(&sdk_root); + cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib")); + cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks")); } } @@ -3142,7 +3129,7 @@ fn add_lld_args( for path in sess.get_tools_search_paths(false) { let linker_path = path.join("gcc-ld"); linker_path_exists |= linker_path.exists(); - cmd.arg({ + cmd.cc_arg({ let mut arg = OsString::from("-B"); arg.push(linker_path); arg @@ -3162,7 +3149,7 @@ fn add_lld_args( // is to use LLD but the `wasm32-wasip2` target relies on a wrapper around // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { - cmd.arg("-fuse-ld=lld"); + cmd.cc_arg("-fuse-ld=lld"); } if !flavor.is_gnu() { @@ -3186,7 +3173,7 @@ fn add_lld_args( // targeting a different linker flavor on macOS, and that's also always // the case when targeting WASM. if sess.target.linker_flavor != sess.host.linker_flavor { - cmd.arg(format!("--target={}", sess.target.llvm_target)); + cmd.cc_arg(format!("--target={}", sess.target.llvm_target)); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a82478900b1..2bd5dfdce83 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -8,7 +8,7 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufWriter}; use std::path::{Path, PathBuf}; -use std::{env, mem, str}; +use std::{env, iter, mem, str}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; @@ -159,6 +159,102 @@ pub fn get_linker<'a>( } } +// Note: Ideally neither these helper function, nor the macro-generated inherent methods below +// would exist, and these functions would live in `trait Linker`. +// Unfortunately, adding these functions to `trait Linker` make it `dyn`-incompatible. +// If the methods are added to the trait with `where Self: Sized` bounds, then even a separate +// implementation of them for `dyn Linker {}` wouldn't work due to a conflict with those +// uncallable methods in the trait. + +/// Just pass the arguments to the linker as is. +/// It is assumed that they are correctly prepared in advance. +fn verbatim_args<L: Linker + ?Sized>( + l: &mut L, + args: impl IntoIterator<Item: AsRef<OsStr>>, +) -> &mut L { + for arg in args { + l.cmd().arg(arg); + } + l +} +/// Arguments for the underlying linker. +/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper. +fn link_args<L: Linker + ?Sized>( + l: &mut L, + args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>, +) -> &mut L { + let args = args.into_iter(); + if !l.is_cc() { + verbatim_args(l, args); + } else if args.len() != 0 { + // FIXME: Support arguments with commas, see `rpaths_to_flags` for the example. + let mut combined_arg = OsString::from("-Wl"); + for arg in args { + combined_arg.push(","); + combined_arg.push(arg); + } + l.cmd().arg(combined_arg); + } + l +} +/// Arguments for the cc wrapper specifically. +/// Check that it's indeed a cc wrapper and pass verbatim. +fn cc_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L { + assert!(l.is_cc()); + verbatim_args(l, args) +} +/// Arguments supported by both underlying linker and cc wrapper, pass verbatim. +fn link_or_cc_args<L: Linker + ?Sized>( + l: &mut L, + args: impl IntoIterator<Item: AsRef<OsStr>>, +) -> &mut L { + verbatim_args(l, args) +} + +macro_rules! generate_arg_methods { + ($($ty:ty)*) => { $( + impl $ty { + pub fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + verbatim_args(self, args) + } + pub fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + verbatim_args(self, iter::once(arg)) + } + pub fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self { + link_args(self, args) + } + pub fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + link_args(self, iter::once(arg)) + } + pub fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + cc_args(self, args) + } + pub fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + cc_args(self, iter::once(arg)) + } + pub fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + link_or_cc_args(self, args) + } + pub fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + link_or_cc_args(self, iter::once(arg)) + } + } + )* } +} + +generate_arg_methods! { + GccLinker<'_> + MsvcLinker<'_> + EmLinker<'_> + WasmLd<'_> + L4Bender<'_> + AixLinker<'_> + LlbcLinker<'_> + PtxLinker<'_> + BpfLinker<'_> + dyn Linker + '_ +} + /// Linker abstraction used by `back::link` to build up the command to invoke a /// linker. /// @@ -168,17 +264,33 @@ pub fn get_linker<'a>( /// MSVC linker (e.g., `link.exe`) is being used. pub trait Linker { fn cmd(&mut self) -> &mut Command; + fn is_cc(&self) -> bool { + false + } fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path); - fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool); + fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { + bug!("dylib linked with unsupported linker") + } + fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) { + bug!("dylib linked with unsupported linker") + } fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { bug!("framework linked with unsupported linker") } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool); fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool); - fn include_path(&mut self, path: &Path); - fn framework_path(&mut self, path: &Path); - fn output_filename(&mut self, path: &Path); - fn add_object(&mut self, path: &Path); + fn include_path(&mut self, path: &Path) { + link_or_cc_args(link_or_cc_args(self, &["-L"]), &[path]); + } + fn framework_path(&mut self, _path: &Path) { + bug!("framework path set with unsupported linker") + } + fn output_filename(&mut self, path: &Path) { + link_or_cc_args(link_or_cc_args(self, &["-o"]), &[path]); + } + fn add_object(&mut self, path: &Path) { + link_or_cc_args(self, &[path]); + } fn gc_sections(&mut self, keep_metadata: bool); fn no_gc_sections(&mut self); fn full_relro(&mut self); @@ -198,25 +310,9 @@ pub trait Linker { fn add_no_exec(&mut self) {} fn add_as_needed(&mut self) {} fn reset_per_library_state(&mut self) {} - fn linker_arg(&mut self, arg: &OsStr, verbatim: bool) { - self.linker_args(&[arg], verbatim); - } - fn linker_args(&mut self, args: &[&OsStr], _verbatim: bool) { - args.into_iter().for_each(|a| { - self.cmd().arg(a); - }); - } } impl dyn Linker + '_ { - pub fn arg(&mut self, arg: impl AsRef<OsStr>) { - self.cmd().arg(arg); - } - - pub fn args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) { - self.cmd().args(args); - } - pub fn take_cmd(&mut self) -> Command { mem::replace(self.cmd(), Command::new("")) } @@ -233,14 +329,6 @@ pub struct GccLinker<'a> { } impl<'a> GccLinker<'a> { - fn linker_arg(&mut self, arg: impl AsRef<OsStr>) { - Linker::linker_arg(self, arg.as_ref(), false); - } - fn linker_args(&mut self, args: &[impl AsRef<OsStr>]) { - let args_vec: Vec<&OsStr> = args.iter().map(|x| x.as_ref()).collect(); - Linker::linker_args(self, &args_vec, false); - } - fn takes_hints(&self) -> bool { // Really this function only returns true if the underlying linker // configured for a compiler is binutils `ld.bfd` and `ld.gold`. We @@ -262,7 +350,7 @@ impl<'a> GccLinker<'a> { return; } if self.hinted_static != Some(true) { - self.linker_arg("-Bstatic"); + self.link_arg("-Bstatic"); self.hinted_static = Some(true); } } @@ -272,7 +360,7 @@ impl<'a> GccLinker<'a> { return; } if self.hinted_static != Some(false) { - self.linker_arg("-Bdynamic"); + self.link_arg("-Bdynamic"); self.hinted_static = Some(false); } } @@ -281,7 +369,7 @@ impl<'a> GccLinker<'a> { if let Some(plugin_path) = plugin_path { let mut arg = OsString::from("-plugin="); arg.push(plugin_path); - self.linker_arg(&arg); + self.link_arg(&arg); } let opt_level = match self.sess.opts.optimize { @@ -292,9 +380,9 @@ impl<'a> GccLinker<'a> { }; if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use { - self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display())); + self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; - self.linker_args(&[ + self.link_args(&[ &format!("-plugin-opt={opt_level}"), &format!("-plugin-opt=mcpu={}", self.target_cpu), ]); @@ -304,10 +392,10 @@ impl<'a> GccLinker<'a> { // On mac we need to tell the linker to let this library be rpathed if self.sess.target.is_like_osx { if !self.is_ld { - self.cmd.arg("-dynamiclib"); + self.cc_arg("-dynamiclib"); } - self.linker_arg("-dylib"); + self.link_arg("-dylib"); // Note that the `osx_rpath_install_name` option here is a hack // purely to support rustbuild right now, we should get a more @@ -316,105 +404,110 @@ impl<'a> GccLinker<'a> { if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name { let mut rpath = OsString::from("@rpath/"); rpath.push(out_filename.file_name().unwrap()); - self.linker_args(&[OsString::from("-install_name"), rpath]); + self.link_arg("-install_name").link_arg(rpath); } } else { - self.cmd.arg("-shared"); - if self.sess.target.is_like_windows { - // The output filename already contains `dll_suffix` so - // the resulting import library will have a name in the - // form of libfoo.dll.a - let implib_name = - out_filename.file_name().and_then(|file| file.to_str()).map(|file| { - format!( - "{}{}{}", - self.sess.target.staticlib_prefix, - file, - self.sess.target.staticlib_suffix - ) - }); - if let Some(implib_name) = implib_name { - let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); - if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap())); - } + self.link_or_cc_arg("-shared"); + if let Some(name) = out_filename.file_name() { + if self.sess.target.is_like_windows { + // The output filename already contains `dll_suffix` so + // the resulting import library will have a name in the + // form of libfoo.dll.a + let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix); + implib_name.push(name); + implib_name.push(&*self.sess.target.staticlib_suffix); + let mut out_implib = OsString::from("--out-implib="); + out_implib.push(out_filename.with_file_name(implib_name)); + self.link_arg(out_implib); + } else { + // When dylibs are linked by a full path this value will get into `DT_NEEDED` + // instead of the full path, so the library can be later found in some other + // location than that specific path. + let mut soname = OsString::from("-soname="); + soname.push(name); + self.link_arg(soname); } } } } -} -impl<'a> Linker for GccLinker<'a> { - /// Passes a series of arguments directly to the linker. - /// - /// When the linker is ld-like, the arguments are simply appended to the command. When the - /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by - /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a - /// single argument is appended to the command to ensure that the order of the arguments is - /// preserved by the compiler. - fn linker_args(&mut self, args: &[&OsStr], verbatim: bool) { - if self.is_ld || verbatim { - args.into_iter().for_each(|a| { - self.cmd.arg(a); - }); - } else { - if !args.is_empty() { - let mut s = OsString::from("-Wl"); - for a in args { - s.push(","); - s.push(a); - } - self.cmd.arg(s); + fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) { + if !as_needed { + if self.sess.target.is_like_osx { + // FIXME(81490): ld64 doesn't support these flags but macOS 11 + // has -needed-l{} / -needed_library {} + // but we have no way to detect that here. + self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); + } else if self.is_gnu && !self.sess.target.is_like_windows { + self.link_arg("--no-as-needed"); + } else { + self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier); + } + } + + f(self); + + if !as_needed { + if self.sess.target.is_like_osx { + // See above FIXME comment + } else if self.is_gnu && !self.sess.target.is_like_windows { + self.link_arg("--as-needed"); } } } +} +impl<'a> Linker for GccLinker<'a> { fn cmd(&mut self) -> &mut Command { &mut self.cmd } + fn is_cc(&self) -> bool { + !self.is_ld + } + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { match output_kind { LinkOutputKind::DynamicNoPicExe => { if !self.is_ld && self.is_gnu { - self.cmd.arg("-no-pie"); + self.cc_arg("-no-pie"); } } LinkOutputKind::DynamicPicExe => { // noop on windows w/ gcc & ld, error w/ lld if !self.sess.target.is_like_windows { // `-pie` works for both gcc wrapper and ld. - self.cmd.arg("-pie"); + self.link_or_cc_arg("-pie"); } } LinkOutputKind::StaticNoPicExe => { // `-static` works for both gcc wrapper and ld. - self.cmd.arg("-static"); + self.link_or_cc_arg("-static"); if !self.is_ld && self.is_gnu { - self.cmd.arg("-no-pie"); + self.cc_arg("-no-pie"); } } LinkOutputKind::StaticPicExe => { if !self.is_ld { // Note that combination `-static -pie` doesn't work as expected // for the gcc wrapper, `-static` in that case suppresses `-pie`. - self.cmd.arg("-static-pie"); + self.cc_arg("-static-pie"); } else { // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing // a static pie, but currently passed because gcc and clang pass them. // The former suppresses the `INTERP` ELF header specifying dynamic linker, // which is otherwise implicitly injected by ld (but not lld). // The latter doesn't change anything, only ensures that everything is pic. - self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]); + self.link_args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]); } } LinkOutputKind::DynamicDylib => self.build_dylib(out_filename), LinkOutputKind::StaticDylib => { - self.cmd.arg("-static"); + self.link_or_cc_arg("-static"); self.build_dylib(out_filename); } LinkOutputKind::WasiReactorExe => { - self.linker_args(&["--entry", "_initialize"]); + self.link_args(&["--entry", "_initialize"]); } } // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, @@ -430,7 +523,7 @@ impl<'a> Linker for GccLinker<'a> { | LinkOutputKind::StaticDylib ) { - self.cmd.arg("--static-crt"); + self.cc_arg("--static-crt"); } } @@ -443,27 +536,18 @@ impl<'a> Linker for GccLinker<'a> { // to the linker. return; } - if !as_needed { - if self.sess.target.is_like_osx { - // FIXME(81490): ld64 doesn't support these flags but macOS 11 - // has -needed-l{} / -needed_library {} - // but we have no way to detect that here. - self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); - } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--no-as-needed"); - } else { - self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier); - } - } self.hint_dynamic(); - self.cmd.arg(format!("-l{}{name}", if verbatim && self.is_gnu { ":" } else { "" },)); - if !as_needed { - if self.sess.target.is_like_osx { - // See above FIXME comment - } else if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--as-needed"); - } - } + self.with_as_needed(as_needed, |this| { + let colon = if verbatim && this.is_gnu { ":" } else { "" }; + this.link_or_cc_arg(format!("-l{colon}{name}")); + }); + } + + fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) { + self.hint_dynamic(); + self.with_as_needed(as_needed, |this| { + this.link_or_cc_arg(path); + }) } fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) { @@ -471,63 +555,51 @@ impl<'a> Linker for GccLinker<'a> { if !as_needed { // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework // flag but we have no way to detect that here. - // self.cmd.arg("-needed_framework").arg(name); + // self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name); self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); } - self.cmd.arg("-framework").arg(name); + self.link_or_cc_args(&["-framework", name]); } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); let colon = if verbatim && self.is_gnu { ":" } else { "" }; if !whole_archive { - self.cmd.arg(format!("-l{colon}{name}")); + self.link_or_cc_arg(format!("-l{colon}{name}")); } else if self.sess.target.is_like_osx { // -force_load is the macOS equivalent of --whole-archive, but it // involves passing the full path to the library to link. - self.linker_arg("-force_load"); - self.linker_arg(find_native_static_library(name, verbatim, self.sess)); + self.link_arg("-force_load"); + self.link_arg(find_native_static_library(name, verbatim, self.sess)); } else { - self.linker_arg("--whole-archive"); - self.cmd.arg(format!("-l{colon}{name}")); - self.linker_arg("--no-whole-archive"); + self.link_arg("--whole-archive") + .link_or_cc_arg(format!("-l{colon}{name}")) + .link_arg("--no-whole-archive"); } } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.cmd.arg(path); + self.link_or_cc_arg(path); } else if self.sess.target.is_like_osx { - self.linker_arg("-force_load"); - self.linker_arg(path); + self.link_arg("-force_load").link_arg(path); } else { - self.linker_arg("--whole-archive"); - self.linker_arg(path); - self.linker_arg("--no-whole-archive"); + self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive"); } } - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } fn framework_path(&mut self, path: &Path) { - self.cmd.arg("-F").arg(path); - } - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.link_or_cc_arg("-F").link_or_cc_arg(path); } fn full_relro(&mut self) { - self.linker_args(&["-z", "relro", "-z", "now"]); + self.link_args(&["-z", "relro", "-z", "now"]); } fn partial_relro(&mut self) { - self.linker_args(&["-z", "relro"]); + self.link_args(&["-z", "relro"]); } fn no_relro(&mut self) { - self.linker_args(&["-z", "norelro"]); + self.link_args(&["-z", "norelro"]); } fn gc_sections(&mut self, keep_metadata: bool) { @@ -546,7 +618,7 @@ impl<'a> Linker for GccLinker<'a> { // for partial linking when using multiple codegen units (-r). So we // insert it here. if self.sess.target.is_like_osx { - self.linker_arg("-dead_strip"); + self.link_arg("-dead_strip"); // If we're building a dylib, we don't use --gc-sections because LLVM // has already done the best it can do, and we also don't want to @@ -554,13 +626,13 @@ impl<'a> Linker for GccLinker<'a> { // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% // reduction. } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata { - self.linker_arg("--gc-sections"); + self.link_arg("--gc-sections"); } } fn no_gc_sections(&mut self) { if self.is_gnu || self.sess.target.is_like_wasm { - self.linker_arg("--no-gc-sections"); + self.link_arg("--no-gc-sections"); } } @@ -574,7 +646,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.opts.optimize == config::OptLevel::Default || self.sess.opts.optimize == config::OptLevel::Aggressive { - self.linker_arg("-O1"); + self.link_arg("-O1"); } } @@ -594,8 +666,7 @@ impl<'a> Linker for GccLinker<'a> { // // Though it may be worth to try to revert those changes upstream, since // the overhead of the initialization should be minor. - self.cmd.arg("-u"); - self.cmd.arg("__llvm_profile_runtime"); + self.link_or_cc_args(&["-u", "__llvm_profile_runtime"]); } fn control_flow_guard(&mut self) {} @@ -616,33 +687,33 @@ impl<'a> Linker for GccLinker<'a> { // The --strip-debug case is handled by running an external // `strip` utility as a separate step after linking. if !self.sess.target.is_like_solaris { - self.linker_arg("--strip-debug"); + self.link_arg("--strip-debug"); } } Strip::Symbols => { - self.linker_arg("--strip-all"); + self.link_arg("--strip-all"); } } match self.sess.opts.unstable_opts.debuginfo_compression { config::DebugInfoCompression::None => {} config::DebugInfoCompression::Zlib => { - self.linker_arg("--compress-debug-sections=zlib"); + self.link_arg("--compress-debug-sections=zlib"); } config::DebugInfoCompression::Zstd => { - self.linker_arg("--compress-debug-sections=zstd"); + self.link_arg("--compress-debug-sections=zstd"); } } } fn no_crt_objects(&mut self) { if !self.is_ld { - self.cmd.arg("-nostartfiles"); + self.cc_arg("-nostartfiles"); } } fn no_default_libraries(&mut self) { if !self.is_ld { - self.cmd.arg("-nodefaultlibs"); + self.cc_arg("-nodefaultlibs"); } } @@ -718,24 +789,22 @@ impl<'a> Linker for GccLinker<'a> { } if self.sess.target.is_like_osx { - self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]); + self.link_arg("-exported_symbols_list").link_arg(path); } else if self.sess.target.is_like_solaris { - self.linker_args(&[OsString::from("-M"), path.into()]); + self.link_arg("-M").link_arg(path); } else { if is_windows { - self.linker_arg(path); + self.link_arg(path); } else { let mut arg = OsString::from("--version-script="); arg.push(path); - self.linker_arg(arg); - self.linker_arg("--no-undefined-version"); + self.link_arg(arg).link_arg("--no-undefined-version"); } } } fn subsystem(&mut self, subsystem: &str) { - self.linker_arg("--subsystem"); - self.linker_arg(&subsystem); + self.link_args(&["--subsystem", subsystem]); } fn reset_per_library_state(&mut self) { @@ -760,23 +829,23 @@ impl<'a> Linker for GccLinker<'a> { // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't, // so we just always add it. fn add_eh_frame_header(&mut self) { - self.linker_arg("--eh-frame-hdr"); + self.link_arg("--eh-frame-hdr"); } fn add_no_exec(&mut self) { if self.sess.target.is_like_windows { - self.linker_arg("--nxcompat"); + self.link_arg("--nxcompat"); } else if self.is_gnu { - self.linker_args(&["-z", "noexecstack"]); + self.link_args(&["-z", "noexecstack"]); } } fn add_as_needed(&mut self) { if self.is_gnu && !self.sess.target.is_like_windows { - self.linker_arg("--as-needed"); + self.link_arg("--as-needed"); } else if self.sess.target.is_like_solaris { // -z ignore is the Solaris equivalent to the GNU ld --as-needed option - self.linker_args(&["-z", "ignore"]); + self.link_args(&["-z", "ignore"]); } } } @@ -798,10 +867,10 @@ impl<'a> Linker for MsvcLinker<'a> { | LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {} LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { - self.cmd.arg("/DLL"); + self.link_arg("/DLL"); let mut arg: OsString = "/IMPLIB:".into(); arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); + self.link_arg(arg); } LinkOutputKind::WasiReactorExe => { panic!("can't link as reactor on non-wasi target"); @@ -810,44 +879,49 @@ impl<'a> Linker for MsvcLinker<'a> { } fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) { - self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" })); + self.link_arg(format!("{}{}", name, if verbatim { "" } else { ".lib" })); + } + + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + // When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export + // any symbols, so we skip linking if the implib file is not present. + let implib_path = path.with_extension("dll.lib"); + if implib_path.exists() { + self.link_or_cc_arg(implib_path); + } } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; let suffix = if verbatim { "" } else { ".lib" }; - self.cmd.arg(format!("{prefix}{name}{suffix}")); + self.link_arg(format!("{prefix}{name}{suffix}")); } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { if !whole_archive { - self.cmd.arg(path); + self.link_arg(path); } else { let mut arg = OsString::from("/WHOLEARCHIVE:"); arg.push(path); - self.cmd.arg(arg); + self.link_arg(arg); } } - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - fn gc_sections(&mut self, _keep_metadata: bool) { // MSVC's ICF (Identical COMDAT Folding) link optimization is // slow for Rust and thus we disable it by default when not in // optimization build. if self.sess.opts.optimize != config::OptLevel::No { - self.cmd.arg("/OPT:REF,ICF"); + self.link_arg("/OPT:REF,ICF"); } else { // It is necessary to specify NOICF here, because /OPT:REF // implies ICF by default. - self.cmd.arg("/OPT:REF,NOICF"); + self.link_arg("/OPT:REF,NOICF"); } } fn no_gc_sections(&mut self) { - self.cmd.arg("/OPT:NOREF,NOICF"); + self.link_arg("/OPT:NOREF,NOICF"); } fn full_relro(&mut self) { @@ -867,23 +941,19 @@ impl<'a> Linker for MsvcLinker<'a> { } fn no_default_libraries(&mut self) { - self.cmd.arg("/NODEFAULTLIB"); + self.link_arg("/NODEFAULTLIB"); } fn include_path(&mut self, path: &Path) { let mut arg = OsString::from("/LIBPATH:"); arg.push(path); - self.cmd.arg(&arg); + self.link_arg(&arg); } fn output_filename(&mut self, path: &Path) { let mut arg = OsString::from("/OUT:"); arg.push(path); - self.cmd.arg(&arg); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on windows") + self.link_arg(&arg); } fn optimize(&mut self) { @@ -895,19 +965,19 @@ impl<'a> Linker for MsvcLinker<'a> { } fn control_flow_guard(&mut self) { - self.cmd.arg("/guard:cf"); + self.link_arg("/guard:cf"); } fn ehcont_guard(&mut self) { if self.sess.target.pointer_width == 64 { - self.cmd.arg("/guard:ehcont"); + self.link_arg("/guard:ehcont"); } } fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { // This will cause the Microsoft linker to generate a PDB file // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); + self.link_arg("/DEBUG"); // Default to emitting only the file name of the PDB file into // the binary instead of the full path. Emitting the full path @@ -916,7 +986,7 @@ impl<'a> Linker for MsvcLinker<'a> { // // This default behavior can be overridden by explicitly passing // `-Clink-arg=/PDBALTPATH:...` to rustc. - self.cmd.arg("/PDBALTPATH:%_PDB%"); + self.link_arg("/PDBALTPATH:%_PDB%"); // This will cause the Microsoft linker to embed .natvis info into the PDB file let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); @@ -928,7 +998,7 @@ impl<'a> Linker for MsvcLinker<'a> { if path.extension() == Some("natvis".as_ref()) { let mut arg = OsString::from("/NATVIS:"); arg.push(path); - self.cmd.arg(arg); + self.link_arg(arg); } } Err(error) => { @@ -942,7 +1012,7 @@ impl<'a> Linker for MsvcLinker<'a> { for path in natvis_debugger_visualizers { let mut arg = OsString::from("/NATVIS:"); arg.push(path); - self.cmd.arg(arg); + self.link_arg(arg); } } @@ -986,13 +1056,13 @@ impl<'a> Linker for MsvcLinker<'a> { } let mut arg = OsString::from("/DEF:"); arg.push(path); - self.cmd.arg(&arg); + self.link_arg(&arg); } fn subsystem(&mut self, subsystem: &str) { // Note that previous passes of the compiler validated this subsystem, // so we just blindly pass it to the linker. - self.cmd.arg(&format!("/SUBSYSTEM:{subsystem}")); + self.link_arg(&format!("/SUBSYSTEM:{subsystem}")); // Windows has two subsystems we're interested in right now, the console // and windows subsystems. These both implicitly have different entry @@ -1009,7 +1079,7 @@ impl<'a> Linker for MsvcLinker<'a> { // // For more information see RFC #1665 if subsystem == "windows" { - self.cmd.arg("/ENTRY:mainCRTStartup"); + self.link_arg("/ENTRY:mainCRTStartup"); } } @@ -1018,7 +1088,7 @@ impl<'a> Linker for MsvcLinker<'a> { } fn add_no_exec(&mut self) { - self.cmd.arg("/NXCOMPAT"); + self.link_arg("/NXCOMPAT"); } } @@ -1032,31 +1102,27 @@ impl<'a> Linker for EmLinker<'a> { &mut self.cmd } + fn is_cc(&self) -> bool { + true + } + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) { // Emscripten always links statically - self.cmd.arg("-l").arg(name); + self.link_or_cc_args(&["-l", name]); } - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) { - self.cmd.arg("-l").arg(name); + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + self.link_or_cc_arg(path); } - fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { - self.cmd.arg(path); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); + fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) { + self.link_or_cc_args(&["-l", name]); } - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { + self.link_or_cc_arg(path); } fn full_relro(&mut self) { @@ -1071,10 +1137,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on Emscripten") - } - fn gc_sections(&mut self, _keep_metadata: bool) { // noop } @@ -1085,7 +1147,7 @@ impl<'a> Linker for EmLinker<'a> { fn optimize(&mut self) { // Emscripten performs own optimizations - self.cmd.arg(match self.sess.opts.optimize { + self.cc_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", OptLevel::Default => "-O2", @@ -1106,7 +1168,7 @@ impl<'a> Linker for EmLinker<'a> { fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { // Preserve names or generate source maps depending on debug info // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g - self.cmd.arg(match self.sess.opts.debuginfo { + self.cc_arg(match self.sess.opts.debuginfo { DebugInfo::None => "-g0", DebugInfo::Limited | DebugInfo::LineTablesOnly | DebugInfo::LineDirectivesOnly => { "--profiling-funcs" @@ -1118,13 +1180,13 @@ impl<'a> Linker for EmLinker<'a> { fn no_crt_objects(&mut self) {} fn no_default_libraries(&mut self) { - self.cmd.arg("-nodefaultlibs"); + self.cc_arg("-nodefaultlibs"); } fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { debug!("EXPORTED SYMBOLS:"); - self.cmd.arg("-s"); + self.cc_arg("-s"); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( @@ -1135,7 +1197,7 @@ impl<'a> Linker for EmLinker<'a> { arg.push(encoded); - self.cmd.arg(arg); + self.cc_arg(arg); } fn subsystem(&mut self, _subsystem: &str) { @@ -1153,7 +1215,7 @@ pub struct WasmLd<'a> { } impl<'a> WasmLd<'a> { - fn new(mut cmd: Command, sess: &'a Session) -> WasmLd<'a> { + fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> { // If the atomics feature is enabled for wasm then we need a whole bunch // of flags: // @@ -1172,18 +1234,19 @@ impl<'a> WasmLd<'a> { // On wasm32-unknown-unknown, we also export symbols for glue code to use: // * `--export=*tls*` - when `#[thread_local]` symbols are used these // symbols are how the TLS segments are initialized and configured. + let mut wasm_ld = WasmLd { cmd, sess }; if sess.target_features.contains(&sym::atomics) { - cmd.arg("--shared-memory"); - cmd.arg("--max-memory=1073741824"); - cmd.arg("--import-memory"); + wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]); if sess.target.os == "unknown" { - cmd.arg("--export=__wasm_init_tls"); - cmd.arg("--export=__tls_size"); - cmd.arg("--export=__tls_align"); - cmd.arg("--export=__tls_base"); + wasm_ld.link_args(&[ + "--export=__wasm_init_tls", + "--export=__tls_size", + "--export=__tls_align", + "--export=__tls_base", + ]); } } - WasmLd { cmd, sess } + wasm_ld } } @@ -1199,51 +1262,40 @@ impl<'a> Linker for WasmLd<'a> { | LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {} LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { - self.cmd.arg("--no-entry"); + self.link_arg("--no-entry"); } LinkOutputKind::WasiReactorExe => { - self.cmd.arg("--entry"); - self.cmd.arg("_initialize"); + self.link_args(&["--entry", "_initialize"]); } } } fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) { - self.cmd.arg("-l").arg(name); + self.link_or_cc_args(&["-l", name]); + } + + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + self.link_or_cc_arg(path); } fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { if !whole_archive { - self.cmd.arg("-l").arg(name); + self.link_or_cc_args(&["-l", name]); } else { - self.cmd.arg("--whole-archive").arg("-l").arg(name).arg("--no-whole-archive"); + self.link_arg("--whole-archive") + .link_or_cc_args(&["-l", name]) + .link_arg("--no-whole-archive"); } } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { if !whole_archive { - self.cmd.arg(path); + self.link_or_cc_arg(path); } else { - self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive"); + self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive"); } } - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} @@ -1251,17 +1303,17 @@ impl<'a> Linker for WasmLd<'a> { fn no_relro(&mut self) {} fn gc_sections(&mut self, _keep_metadata: bool) { - self.cmd.arg("--gc-sections"); + self.link_arg("--gc-sections"); } fn no_gc_sections(&mut self) { - self.cmd.arg("--no-gc-sections"); + self.link_arg("--no-gc-sections"); } fn optimize(&mut self) { // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and // only differentiates -O0 and -O1. It does not apply to LTO. - self.cmd.arg(match self.sess.opts.optimize { + self.link_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", OptLevel::Default => "-O2", @@ -1279,10 +1331,10 @@ impl<'a> Linker for WasmLd<'a> { match strip { Strip::None => {} Strip::Debuginfo => { - self.cmd.arg("--strip-debug"); + self.link_arg("--strip-debug"); } Strip::Symbols => { - self.cmd.arg("--strip-all"); + self.link_arg("--strip-all"); } } } @@ -1297,7 +1349,7 @@ impl<'a> Linker for WasmLd<'a> { fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { for sym in symbols { - self.cmd.arg("--export").arg(&sym); + self.link_args(&["--export", sym]); } // LLD will hide these otherwise-internal symbols since it only exports @@ -1305,8 +1357,7 @@ impl<'a> Linker for WasmLd<'a> { // others. Various bits and pieces of wasm32-unknown-unknown tooling use // this, so be sure these symbols make their way out of the linker as well. if self.sess.target.os == "unknown" { - self.cmd.arg("--export=__heap_base"); - self.cmd.arg("--export=__data_end"); + self.link_args(&["--export=__heap_base", "--export=__data_end"]); } } @@ -1337,7 +1388,7 @@ impl<'a> WasmLd<'a> { // wasm-ld only handles integer LTO opt levels. Use O2 config::OptLevel::Size | config::OptLevel::SizeMin => "O2", }; - self.cmd.arg(&format!("--lto-{opt_level}")); + self.link_arg(&format!("--lto-{opt_level}")); } } @@ -1355,63 +1406,46 @@ impl<'a> Linker for L4Bender<'a> { fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} - fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { - bug!("dylibs are not supported on L4Re"); - } - fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.cmd.arg(format!("-PC{name}")); + self.link_arg(format!("-PC{name}")); } else { - self.cmd.arg("--whole-archive").arg(format!("-l{name}")).arg("--no-whole-archive"); + self.link_arg("--whole-archive") + .link_or_cc_arg(format!("-l{name}")) + .link_arg("--no-whole-archive"); } } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.cmd.arg(path); + self.link_or_cc_arg(path); } else { - self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive"); + self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive"); } } - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - fn framework_path(&mut self, _: &Path) { - bug!("frameworks are not supported on L4Re"); - } - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - fn full_relro(&mut self) { - self.cmd.arg("-z").arg("relro"); - self.cmd.arg("-z").arg("now"); + self.link_args(&["-z", "relro", "-z", "now"]); } fn partial_relro(&mut self) { - self.cmd.arg("-z").arg("relro"); + self.link_args(&["-z", "relro"]); } fn no_relro(&mut self) { - self.cmd.arg("-z").arg("norelro"); + self.link_args(&["-z", "norelro"]); } fn gc_sections(&mut self, keep_metadata: bool) { if !keep_metadata { - self.cmd.arg("--gc-sections"); + self.link_arg("--gc-sections"); } } fn no_gc_sections(&mut self) { - self.cmd.arg("--no-gc-sections"); + self.link_arg("--no-gc-sections"); } fn optimize(&mut self) { @@ -1420,7 +1454,7 @@ impl<'a> Linker for L4Bender<'a> { if self.sess.opts.optimize == config::OptLevel::Default || self.sess.opts.optimize == config::OptLevel::Aggressive { - self.cmd.arg("-O1"); + self.link_arg("-O1"); } } @@ -1430,16 +1464,16 @@ impl<'a> Linker for L4Bender<'a> { match strip { Strip::None => {} Strip::Debuginfo => { - self.cmd().arg("--strip-debug"); + self.link_arg("--strip-debug"); } Strip::Symbols => { - self.cmd().arg("--strip-all"); + self.link_arg("--strip-all"); } } } fn no_default_libraries(&mut self) { - self.cmd.arg("-nostdlib"); + self.cc_arg("-nostdlib"); } fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) { @@ -1449,7 +1483,7 @@ impl<'a> Linker for L4Bender<'a> { } fn subsystem(&mut self, subsystem: &str) { - self.cmd.arg(&format!("--subsystem {subsystem}")); + self.link_arg(&format!("--subsystem {subsystem}")); } fn reset_per_library_state(&mut self) { @@ -1467,12 +1501,12 @@ impl<'a> Linker for L4Bender<'a> { impl<'a> L4Bender<'a> { pub fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> { - L4Bender { cmd: cmd, sess: sess, hinted_static: false } + L4Bender { cmd, sess: sess, hinted_static: false } } fn hint_static(&mut self) { if !self.hinted_static { - self.cmd.arg("-static"); + self.link_or_cc_arg("-static"); self.hinted_static = true; } } @@ -1487,29 +1521,28 @@ pub struct AixLinker<'a> { impl<'a> AixLinker<'a> { pub fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> { - AixLinker { cmd: cmd, sess: sess, hinted_static: None } + AixLinker { cmd, sess: sess, hinted_static: None } } fn hint_static(&mut self) { if self.hinted_static != Some(true) { - self.cmd.arg("-bstatic"); + self.link_arg("-bstatic"); self.hinted_static = Some(true); } } fn hint_dynamic(&mut self) { if self.hinted_static != Some(false) { - self.cmd.arg("-bdynamic"); + self.link_arg("-bdynamic"); self.hinted_static = Some(false); } } fn build_dylib(&mut self, _out_filename: &Path) { - self.cmd.arg("-bM:SRE"); - self.cmd.arg("-bnoentry"); + self.link_args(&["-bM:SRE", "-bnoentry"]); // FIXME: Use CreateExportList utility to create export list // and remove -bexpfull. - self.cmd.arg("-bexpfull"); + self.link_arg("-bexpfull"); } } @@ -1534,47 +1567,36 @@ impl<'a> Linker for AixLinker<'a> { fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) { self.hint_dynamic(); - self.cmd.arg(format!("-l{name}")); + self.link_or_cc_arg(format!("-l{name}")); + } + + fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) { + self.hint_dynamic(); + self.link_or_cc_arg(path); } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.cmd.arg(format!("-l{name}")); + self.link_or_cc_arg(format!("-l{name}")); } else { let mut arg = OsString::from("-bkeepfile:"); arg.push(find_native_static_library(name, verbatim, self.sess)); - self.cmd.arg(arg); + self.link_or_cc_arg(arg); } } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { self.hint_static(); if !whole_archive { - self.cmd.arg(path); + self.link_or_cc_arg(path); } else { let mut arg = OsString::from("-bkeepfile:"); arg.push(path); - self.cmd.arg(arg); + self.link_arg(arg); } } - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn framework_path(&mut self, _: &Path) { - bug!("frameworks are not supported on AIX"); - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} @@ -1582,17 +1604,17 @@ impl<'a> Linker for AixLinker<'a> { fn no_relro(&mut self) {} fn gc_sections(&mut self, _keep_metadata: bool) { - self.cmd.arg("-bgc"); + self.link_arg("-bgc"); } fn no_gc_sections(&mut self) { - self.cmd.arg("-bnogc"); + self.link_arg("-bnogc"); } fn optimize(&mut self) {} fn pgo_gen(&mut self) { - self.cmd.arg("-bdbg:namedsects:ss"); + self.link_arg("-bdbg:namedsects:ss"); } fn control_flow_guard(&mut self) {} @@ -1618,7 +1640,7 @@ impl<'a> Linker for AixLinker<'a> { if let Err(e) = res { self.sess.dcx().fatal(format!("failed to write export file: {e}")); } - self.cmd.arg(format!("-bE:{}", path.to_str().unwrap())); + self.link_arg(format!("-bE:{}", path.to_str().unwrap())); } fn subsystem(&mut self, _subsystem: &str) {} @@ -1738,48 +1760,32 @@ impl<'a> Linker for PtxLinker<'a> { fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} - fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { - panic!("external dylibs not supported") - } - fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { - self.cmd.arg("--rlib").arg(path); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); + self.link_arg("--rlib").link_arg(path); } fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { - self.cmd.arg("--debug"); + self.link_arg("--debug"); } fn add_object(&mut self, path: &Path) { - self.cmd.arg("--bitcode").arg(path); + self.link_arg("--bitcode").link_arg(path); } fn optimize(&mut self) { match self.sess.lto() { Lto::Thin | Lto::Fat | Lto::ThinLocal => { - self.cmd.arg("-Olto"); + self.link_arg("-Olto"); } Lto::No => {} } } - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} @@ -1820,28 +1826,16 @@ impl<'a> Linker for LlbcLinker<'a> { fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} - fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { - panic!("external dylibs not supported") - } - fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { - self.cmd.arg(path); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); + self.link_or_cc_arg(path); } fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { - self.cmd.arg("--debug"); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.link_arg("--debug"); } fn optimize(&mut self) { @@ -1855,14 +1849,6 @@ impl<'a> Linker for LlbcLinker<'a> { }; } - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} @@ -1887,7 +1873,7 @@ impl<'a> Linker for LlbcLinker<'a> { match _crate_type { CrateType::Cdylib => { for sym in symbols { - self.cmd.arg("--export-symbol").arg(sym); + self.link_args(&["--export-symbol", sym]); } } _ => (), @@ -1911,32 +1897,20 @@ impl<'a> Linker for BpfLinker<'a> { fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} - fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) { - panic!("external dylibs not supported") - } - fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { panic!("staticlibs not supported") } fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { - self.cmd.arg(path); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); + self.link_or_cc_arg(path); } fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { - self.cmd.arg("--debug"); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); + self.link_arg("--debug"); } fn optimize(&mut self) { - self.cmd.arg(match self.sess.opts.optimize { + self.link_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", OptLevel::Default => "-O2", @@ -1946,14 +1920,6 @@ impl<'a> Linker for BpfLinker<'a> { }); } - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} @@ -1985,7 +1951,7 @@ impl<'a> Linker for BpfLinker<'a> { if let Err(error) = res { self.sess.dcx().emit_fatal(errors::SymbolFileWriteFailure { error }); } else { - self.cmd.arg("--export-symbols").arg(&path); + self.link_arg("--export-symbols").link_arg(&path); } } diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index f499bbcf853..82070d4453b 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -9,16 +9,10 @@ pub struct RPathConfig<'a> { pub libs: &'a [&'a Path], pub out_filename: PathBuf, pub is_like_osx: bool, - pub has_rpath: bool, pub linker_is_gnu: bool, } pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> { - // No rpath on windows - if !config.has_rpath { - return Vec::new(); - } - debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index 0de90a1036e..c620e92db1f 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -37,7 +37,6 @@ fn test_rpath_relative() { if cfg!(target_os = "macos") { let config = &mut RPathConfig { libs: &[], - has_rpath: true, is_like_osx: true, linker_is_gnu: false, out_filename: PathBuf::from("bin/rustc"), @@ -48,7 +47,6 @@ fn test_rpath_relative() { let config = &mut RPathConfig { libs: &[], out_filename: PathBuf::from("bin/rustc"), - has_rpath: true, is_like_osx: false, linker_is_gnu: true, }; @@ -62,7 +60,6 @@ fn test_rpath_relative_issue_119571() { let config = &mut RPathConfig { libs: &[], out_filename: PathBuf::from("rustc"), - has_rpath: true, is_like_osx: false, linker_is_gnu: true, }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c18816533a2..137f14fe706 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -37,7 +37,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; -use rustc_span::Symbol; +use rustc_span::{Symbol, DUMMY_SP}; use rustc_target::abi::FIRST_VARIANT; use std::cmp; @@ -467,6 +467,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ty::ParamEnv::reveal_all(), start_def_id, cx.tcx().mk_args(&[main_ret_ty.into()]), + DUMMY_SP, ); let start_fn = cx.get_fn_addr(start_instance); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index d224695d1f2..56a893738df 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,11 +1,13 @@ use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; -use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_errors::{codes::*, struct_span_code_err, DiagMessage, SubdiagMessage}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem}; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::codegen_fn_attrs::{ + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, +}; use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; use rustc_middle::ty::{self as ty, TyCtxt}; @@ -447,6 +449,80 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { None }; } + sym::patchable_function_entry => { + codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { + let mut prefix = None; + let mut entry = None; + for item in l { + let Some(meta_item) = item.meta_item() else { + tcx.dcx().span_err(item.span(), "expected name value pair"); + continue; + }; + + let Some(name_value_lit) = meta_item.name_value_literal() else { + tcx.dcx().span_err(item.span(), "expected name value pair"); + continue; + }; + + fn emit_error_with_label( + tcx: TyCtxt<'_>, + span: Span, + error: impl Into<DiagMessage>, + label: impl Into<SubdiagMessage>, + ) { + let mut err: rustc_errors::Diag<'_, _> = + tcx.dcx().struct_span_err(span, error); + err.span_label(span, label); + err.emit(); + } + + let attrib_to_write = match meta_item.name_or_empty() { + sym::prefix_nops => &mut prefix, + sym::entry_nops => &mut entry, + _ => { + emit_error_with_label( + tcx, + item.span(), + "unexpected parameter name", + format!("expected {} or {}", sym::prefix_nops, sym::entry_nops), + ); + continue; + } + }; + + let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { + emit_error_with_label( + tcx, + name_value_lit.span, + "invalid literal value", + "value must be an integer between `0` and `255`", + ); + continue; + }; + + let Ok(val) = val.get().try_into() else { + emit_error_with_label( + tcx, + name_value_lit.span, + "integer value out of range", + "value must be between `0` and `255`", + ); + continue; + }; + + *attrib_to_write = Some(val); + } + + if let (None, None) = (prefix, entry) { + tcx.dcx().span_err(attr.span, "must specify at least one parameter"); + } + + Some(PatchableFunctionEntry::from_prefix_and_entry( + prefix.unwrap_or(0), + entry.unwrap_or(0), + )) + }) + } _ => {} } } diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index ddc6797388e..febc8ee2be2 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -133,6 +133,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); + cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); cx.create_vtable_debuginfo(ty, trait_ref, vtable); cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 0577ba32ffd..ac2b6ca4e95 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop | TerminatorKind::Unreachable | TerminatorKind::SwitchInt { .. } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 57138d3b9db..6a5525dc2b3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -403,7 +403,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // // Why only in unoptimized builds? // - In unoptimized builds LLVM uses FastISel which does not support switches, so it - // must fall back to the to the slower SelectionDAG isel. Therefore, using `br` gives + // must fall back to the slower SelectionDAG isel. Therefore, using `br` gives // significant compile time speedups for unoptimized builds. // - In optimized builds the above doesn't hold, and using `br` sometimes results in // worse generated code because LLVM can no longer tell that the value being switched @@ -658,7 +658,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // with #[rustc_inherit_overflow_checks] and inlined from // another crate (mostly core::num generic/#[inline] fns), // while the current crate doesn't use overflow checks. - if !bx.cx().check_overflow() && msg.is_optional_overflow_check() { + if !bx.sess().overflow_checks() && msg.is_optional_overflow_check() { const_cond = Some(expected); } @@ -751,7 +751,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut self, helper: &TerminatorCodegenHelper<'tcx>, bx: &mut Bx, - intrinsic: Option<ty::IntrinsicDef>, + intrinsic: ty::IntrinsicDef, instance: Option<Instance<'tcx>>, source_info: mir::SourceInfo, target: Option<mir::BasicBlock>, @@ -761,8 +761,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. - let panic_intrinsic = intrinsic.and_then(|i| ValidityRequirement::from_intrinsic(i.name)); - if let Some(requirement) = panic_intrinsic { + if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { let ty = instance.unwrap().args.type_at(0); let do_panic = !bx @@ -843,6 +842,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::ParamEnv::reveal_all(), def_id, args, + fn_span, ) .polymorphize(bx.tcx()), ), @@ -869,12 +869,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let sig = callee.layout.ty.fn_sig(bx.tcx()); let abi = sig.abi(); - // Handle intrinsics old codegen wants Expr's for, ourselves. - let intrinsic = match def { - Some(ty::InstanceKind::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()), - _ => None, - }; - let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| { let op_ty = op_arg.node.ty(self.mir, bx.tcx()); @@ -886,50 +880,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => bx.fn_abi_of_fn_ptr(sig, extra_args), }; - if let Some(merging_succ) = self.codegen_panic_intrinsic( - &helper, - bx, - intrinsic, - instance, - source_info, - target, - unwind, - mergeable_succ, - ) { - return merging_succ; - } - // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; - if matches!(intrinsic, Some(ty::IntrinsicDef { name: sym::caller_location, .. })) { - return if let Some(target) = target { - let location = - self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); - - let mut llargs = Vec::with_capacity(arg_count); - let ret_dest = self.make_return_dest( + let instance = match def { + Some(ty::InstanceKind::Intrinsic(def_id)) => { + let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); + if let Some(merging_succ) = self.codegen_panic_intrinsic( + &helper, bx, - destination, - &fn_abi.ret, - &mut llargs, intrinsic, - Some(target), - ); - assert_eq!(llargs, []); - if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { - location.val.store(bx, tmp); + instance, + source_info, + target, + unwind, + mergeable_succ, + ) { + return merging_succ; } - self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); - helper.funclet_br(self, bx, target, mergeable_succ) - } else { - MergingSucc::False - }; - } - let instance = match intrinsic { - None => instance, - Some(intrinsic) => { let mut llargs = Vec::with_capacity(1); let ret_dest = self.make_return_dest( bx, @@ -971,6 +940,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) .collect(); + if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) { + let location = self + .get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); + + assert_eq!(llargs, []); + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + location.val.store(bx, tmp); + } + self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); + return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); + } + let instance = *instance.as_ref().unwrap(); match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) { Ok(()) => { @@ -997,6 +978,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } + _ => instance, }; let mut llargs = Vec::with_capacity(arg_count); @@ -1407,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span, mergeable_succ(), ), + mir::TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): implement tail calls in ssa backend + span_bug!( + terminator.source_info.span, + "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`" + ) + } mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => { bug!("coroutine ops in codegen") } @@ -1540,7 +1529,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // when passed by value, making it smaller. // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes // when passed by value, making it larger. - let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes()); + let copy_bytes = cmp::min(cast.unaligned_size(bx).bytes(), arg.layout.size.bytes()); // Allocate some scratch space... let llscratch = bx.alloca(scratch_size, scratch_align); bx.lifetime_start(llscratch, scratch_size); diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs index a6c873e195e..5190021c005 100644 --- a/compiler/rustc_codegen_ssa/src/mir/locals.rs +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let expected_ty = self.monomorphize(self.mir.local_decls[local].ty); if expected_ty != op.layout.ty { warn!( - "Unexpected initial operand type: expected {expected_ty:?}, found {:?}.\ + "Unexpected initial operand type:\nexpected {expected_ty:?},\nfound {:?}.\n\ See <https://github.com/rust-lang/rust/issues/114858>.", op.layout.ty ); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index e8da9842882..61f57c9030a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -230,10 +230,20 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let layout = start_bx.layout_of(fx.monomorphize(decl.ty)); assert!(!layout.ty.has_erasable_regions()); - if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() { - debug!("alloc: {:?} (return place) -> place", local); - let llretptr = start_bx.get_param(0); - return LocalRef::Place(PlaceRef::new_sized(llretptr, layout)); + if local == mir::RETURN_PLACE { + match fx.fn_abi.ret.mode { + PassMode::Indirect { .. } => { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = start_bx.get_param(0); + return LocalRef::Place(PlaceRef::new_sized(llretptr, layout)); + } + PassMode::Cast { ref cast, .. } => { + debug!("alloc: {:?} (return place) -> place", local); + let size = cast.size(&start_bx); + return LocalRef::Place(PlaceRef::alloca_size(&mut start_bx, size, layout)); + } + _ => {} + }; } if memory_locals.contains(local) { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 449fd9ae0db..97d5bb83128 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -109,8 +109,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { + Self::alloca_size(bx, layout.size, layout) + } + + pub fn alloca_size<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + bx: &mut Bx, + size: Size, + layout: TyAndLayout<'tcx>, + ) -> Self { assert!(layout.is_sized(), "tried to statically allocate unsized place"); - PlaceValue::alloca(bx, layout.size, layout.align.abi).with_type(layout) + PlaceValue::alloca(bx, size, layout.align.abi).with_type(layout) } /// Returns a place for an indirect reference to an unsized place. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e8b9490d401..3770bd11cf9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -22,8 +22,6 @@ use rustc_session::{ use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; -use std::fmt; - pub trait BackendTypes { type Value: CodegenObject; type Function: CodegenObject; @@ -62,7 +60,7 @@ pub trait CodegenBackend { fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} - fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {} + fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } @@ -150,19 +148,3 @@ pub trait ExtraBackendMethods: std::thread::Builder::new().name(name).spawn(f) } } - -pub trait PrintBackendInfo { - fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>); -} - -impl PrintBackendInfo for String { - fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) { - fmt::Write::write_fmt(self, args).unwrap(); - } -} - -impl dyn PrintBackendInfo + '_ { - pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) { - self.infallible_write_fmt(args); - } -} diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 8cb17a5b37a..3da732602c5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -37,6 +37,5 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; - fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 04e2b8796c4..0ace28ed3ba 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -9,7 +9,13 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn vtables( &self, ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>; - fn check_overflow(&self) -> bool; + fn apply_vcall_visibility_metadata( + &self, + _ty: Ty<'tcx>, + _poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + _vtable: Self::Value, + ) { + } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; fn eh_personality(&self) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 728c2bc8c49..8cb58bd4c70 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -30,9 +30,7 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{ - Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo, -}; +pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 403f6a73277..b1bad6cfa6f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -12,7 +12,6 @@ use rustc_target::abi::{AddressSpace, Float, Integer}; // This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use // `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves. pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { - fn type_i1(&self) -> Self::Type; fn type_i8(&self) -> Self::Type; fn type_i16(&self) -> Self::Type; fn type_i32(&self) -> Self::Type; @@ -27,7 +26,6 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type; fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type; - fn type_struct(&self, els: &[Self::Type], packed: bool) -> Self::Type; fn type_kind(&self, ty: Self::Type) -> TypeKind; fn type_ptr(&self) -> Self::Type; fn type_ptr_ext(&self, address_space: AddressSpace) -> Self::Type; @@ -115,8 +113,8 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { /// The backend type used for a rust type when it's in an SSA register. /// /// For nearly all types this is the same as the [`Self::backend_type`], however - /// `bool` (and other `0`-or-`1` values) are kept as [`BaseTypeMethods::type_i1`] - /// in registers but as [`BaseTypeMethods::type_i8`] in memory. + /// `bool` (and other `0`-or-`1` values) are kept as `i1` in registers but as + /// [`BaseTypeMethods::type_i8`] in memory. /// /// Converting values between the two different backend types is done using /// [`from_immediate`](super::BuilderMethods::from_immediate) and diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ab60cc37920..523d55fe2d0 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; use rustc_middle::ty::{Instance, InstanceKind, TypeVisitableExt}; use rustc_mir_dataflow::Analysis; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor}; @@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { ccx: &'mir ConstCx<'mir, 'tcx>, tainted_by_errors: Option<ErrorGuaranteed>, ) -> ConstQualifs { + // FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything + // Find the `Return` terminator if one exists. // // If no `Return` terminator exists, this MIR is divergent. Just return the conservative @@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, fn_span, call_source, .. } => { + TerminatorKind::Call { func, args, fn_span, .. } + | TerminatorKind::TailCall { func, args, fn_span, .. } => { + let call_source = match terminator.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id(); @@ -768,7 +777,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { is_trait = true; if let Ok(Some(instance)) = - Instance::resolve(tcx, param_env, callee, fn_args) + Instance::try_resolve(tcx, param_env, callee, fn_args) && let InstanceKind::Item(def) = instance.def { // Resolve a trait method call to its concrete implementation, which may be in a @@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: Some(if tcx.features().const_trait_impl { sym::effects } else { @@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: None, }); return; diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index f0763f1e490..f5e745454ab 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { mir::TerminatorKind::UnwindTerminate(_) | mir::TerminatorKind::Call { .. } + | mir::TerminatorKind::TailCall { .. } | mir::TerminatorKind::Assert { .. } | mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 8c66888d100..7acd08e0cce 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -42,10 +42,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => { hir::Constness::Const } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => tcx - .generics_of(def_id) - .host_effect_index - .map_or(hir::Constness::NotConst, |_| hir::Constness::Const), + hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness, hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other // foreign items cannot be evaluated at compile-time. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 99276bac035..17e1d8566c2 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -253,6 +253,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { ty::ParamEnv::reveal_all(), const_def_id, instance.args, + self.cur_span(), ); return Ok(Some(new_instance)); diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index a13630ce084..83b61ab1749 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -1,6 +1,6 @@ use std::assert_matches::assert_matches; -use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::mir::CastKind; @@ -187,10 +187,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { bug!("FloatToFloat/FloatToInt cast: source type {} is not a float type", src.layout.ty) }; let val = match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => self.cast_from_float(src.to_scalar().to_f16()?, cast_to.ty), FloatTy::F32 => self.cast_from_float(src.to_scalar().to_f32()?, cast_to.ty), FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty), }; Ok(ImmTy::from_scalar(val, cast_to)) } @@ -296,18 +296,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Float(fty) if signed => { let v = v as i128; match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => Scalar::from_f16(Half::from_i128(v).value), FloatTy::F32 => Scalar::from_f32(Single::from_i128(v).value), FloatTy::F64 => Scalar::from_f64(Double::from_i128(v).value), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => Scalar::from_f128(Quad::from_i128(v).value), } } // unsigned int -> float Float(fty) => match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => Scalar::from_f16(Half::from_u128(v).value), FloatTy::F32 => Scalar::from_f32(Single::from_u128(v).value), FloatTy::F64 => Scalar::from_f64(Double::from_u128(v).value), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => Scalar::from_f128(Quad::from_u128(v).value), }, // u8 -> char @@ -321,7 +321,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Low-level cast helper function. Converts an apfloat `f` into int or float types. fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance> where - F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>, + F: Float + + Into<Scalar<M::Provenance>> + + FloatConvert<Half> + + FloatConvert<Single> + + FloatConvert<Double> + + FloatConvert<Quad>, { use rustc_type_ir::TyKind::*; @@ -358,10 +363,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // float -> float Float(fty) => match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => Scalar::from_f16(adjust_nan(self, f, f.convert(&mut false).value)), FloatTy::F32 => Scalar::from_f32(adjust_nan(self, f, f.convert(&mut false).value)), FloatTy::F64 => Scalar::from_f64(adjust_nan(self, f, f.convert(&mut false).value)), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => { + Scalar::from_f128(adjust_nan(self, f, f.convert(&mut false).value)) + } }, // That's it. _ => span_bug!(self.cur_span(), "invalid float to {} cast", dest_ty), diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index b3a139d553a..181c7115386 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -245,7 +245,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The tag of a `Single` enum is like the tag of the niched // variant: there's no tag as the discriminant is encoded // entirely implicitly. If `write_discriminant` ever hits this - // case, we do a "validation read" to ensure the the right + // case, we do a "validation read" to ensure the right // discriminant is encoded implicitly, so any attempt to write // the wrong discriminant for a `Single` enum will reliably // result in UB. diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 67eeb1b3b87..6d3e5ea1031 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -26,8 +26,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou use super::{ err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, - OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar, - StackPopJump, + OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, + ReturnAction, Scalar, }; use crate::errors; use crate::util; @@ -159,6 +159,19 @@ pub enum StackPopCleanup { Root { cleanup: bool }, } +/// Return type of [`InterpCx::pop_stack_frame`]. +pub struct StackPopInfo<'tcx, Prov: Provenance> { + /// Additional information about the action to be performed when returning from the popped + /// stack frame. + pub return_action: ReturnAction, + + /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. + pub return_to_block: StackPopCleanup, + + /// [`return_place`](Frame::return_place) of the popped stack frame. + pub return_place: MPlaceTy<'tcx, Prov>, +} + /// State of a local variable including a memoized layout #[derive(Clone)] pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { @@ -618,7 +631,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("resolve: {:?}, {:#?}", def, args); trace!("param_env: {:#?}", self.param_env); trace!("args: {:#?}", args); - match ty::Instance::resolve(*self.tcx, self.param_env, def, args) { + match ty::Instance::try_resolve(*self.tcx, self.param_env, def, args) { Ok(Some(instance)) => Ok(instance), Ok(None) => throw_inval!(TooGeneric), @@ -803,14 +816,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); + + // First push a stack frame so we have access to the local args + self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?; + + self.after_stack_frame_push(instance, body)?; + + Ok(()) + } + + /// Creates a new stack frame, initializes it and pushes it onto the stack. + /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). + fn push_new_stack_frame( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + return_to_block: StackPopCleanup, + return_place: MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; let locals = IndexVec::from_elem(dead_local, &body.local_decls); - // First push a stack frame so we have access to the local args let pre_frame = Frame { body, loc: Right(body.span), // Span used for errors caused during preamble. return_to_block, - return_place: return_place.clone(), + return_place, locals, instance, tracing_span: SpanGuard::new(), @@ -819,6 +849,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let frame = M::init_frame(self, pre_frame)?; self.stack_mut().push(frame); + Ok(()) + } + + /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). + fn after_stack_frame_push( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + ) -> InterpResult<'tcx> { // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). for &const_ in &body.required_consts { let c = @@ -839,6 +878,61 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } + /// Pops a stack frame from the stack and returns some information about it. + /// + /// This also deallocates locals, if necessary. + /// + /// [`M::before_stack_pop`] should be called before calling this function. + /// [`M::after_stack_pop`] is called by this function automatically. + /// + /// [`M::before_stack_pop`]: Machine::before_stack_pop + /// [`M::after_stack_pop`]: Machine::after_stack_pop + pub fn pop_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { + let cleanup = self.cleanup_current_frame_locals()?; + + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + + let return_to_block = frame.return_to_block; + let return_place = frame.return_place.clone(); + + let return_action; + if cleanup { + return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + } else { + return_action = ReturnAction::NoCleanup; + }; + + Ok(StackPopInfo { return_action, return_to_block, return_place }) + } + + /// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame). + /// Returns `true` if cleanup has been done, `false` otherwise. + fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { + // Cleanup: deallocate locals. + // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. + // We do this while the frame is still on the stack, so errors point to the callee. + let return_to_block = self.frame().return_to_block; + let cleanup = match return_to_block { + StackPopCleanup::Goto { .. } => true, + StackPopCleanup::Root { cleanup, .. } => cleanup, + }; + + if cleanup { + // We need to take the locals out, since we need to mutate while iterating. + let locals = mem::take(&mut self.frame_mut().locals); + for local in &locals { + self.deallocate_local(local.value)?; + } + } + + Ok(cleanup) + } + /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { @@ -886,7 +980,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } /// Pops the current frame from the stack, deallocating the - /// memory for allocated locals. + /// memory for allocated locals, and jumps to an appropriate place. /// /// If `unwinding` is `false`, then we are performing a normal return /// from a function. In this case, we jump back into the frame of the caller, @@ -899,7 +993,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// The cleanup block ends with a special `Resume` terminator, which will /// cause us to continue unwinding. #[instrument(skip(self), level = "debug")] - pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { + pub(super) fn return_from_current_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx> { info!( "popping stack frame ({})", if unwinding { "during unwinding" } else { "returning from function" } @@ -947,45 +1044,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) }; - // Cleanup: deallocate locals. - // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. - // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; - let cleanup = match return_to_block { - StackPopCleanup::Goto { .. } => true, - StackPopCleanup::Root { cleanup, .. } => cleanup, - }; - if cleanup { - // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { - self.deallocate_local(local.value)?; - } - } - // All right, now it is time to actually pop the frame. - // Note that its locals are gone already, but that's fine. - let frame = - self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + let stack_pop_info = self.pop_stack_frame(unwinding)?; + // Report error from return value copy, if any. copy_ret_result?; - // If we are not doing cleanup, also skip everything else. - if !cleanup { - assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); - assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Skip machine hook. - return Ok(()); - } - if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump { - // The hook already did everything. - return Ok(()); + match stack_pop_info.return_action { + ReturnAction::Normal => {} + ReturnAction::NoJump => { + // The hook already did everything. + return Ok(()); + } + ReturnAction::NoCleanup => { + // If we are not doing cleanup, also skip everything else. + assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); + assert!(!unwinding, "tried to skip cleanup during unwinding"); + // Skip machine hook. + return Ok(()); + } } // Normal return, figure out where to jump. if unwinding { // Follow the unwind edge. - let unwind = match return_to_block { + let unwind = match stack_pop_info.return_to_block { StackPopCleanup::Goto { unwind, .. } => unwind, StackPopCleanup::Root { .. } => { panic!("encountered StackPopCleanup::Root when unwinding!") @@ -995,7 +1078,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.unwind_to_block(unwind) } else { // Follow the normal return edge. - match return_to_block { + match stack_pop_info.return_to_block { StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), StackPopCleanup::Root { .. } => { assert!( diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 1d54da267ee..d86f1a7a34f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -301,9 +301,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // The signed form of the intrinsic allows this. If we interpret the // difference as isize, we'll get the proper signed difference. If that - // seems *positive*, they were more than isize::MAX apart. + // seems *positive* or equal to isize::MIN, they were more than isize::MAX apart. let dist = val.to_target_isize(self)?; - if dist >= 0 { + if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() { throw_ub_custom!( fluent::const_eval_offset_from_underflow, name = intrinsic_name, diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e91ab7c1791..7f2e9ce06a5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -23,10 +23,11 @@ use super::{ MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, }; -/// Data returned by Machine::stack_pop, -/// to provide further control over the popping of the stack frame +/// Data returned by [`Machine::after_stack_pop`], and consumed by +/// [`InterpCx::return_from_current_stack_frame`] to determine what actions should be done when +/// returning from a stack frame. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum StackPopJump { +pub enum ReturnAction { /// Indicates that no special handling should be /// done - we'll either return normally or unwind /// based on the terminator for the function @@ -36,6 +37,9 @@ pub enum StackPopJump { /// Indicates that we should *not* jump to the return/unwind address, as the callback already /// took care of everything. NoJump, + + /// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done. + NoCleanup, } /// Whether this kind of memory is allowed to leak @@ -522,10 +526,10 @@ pub trait Machine<'tcx>: Sized { _ecx: &mut InterpCx<'tcx, Self>, _frame: Frame<'tcx, Self::Provenance, Self::FrameExtra>, unwinding: bool, - ) -> InterpResult<'tcx, StackPopJump> { + ) -> InterpResult<'tcx, ReturnAction> { // By default, we do not support unwinding from panics assert!(!unwinding); - Ok(StackPopJump::Normal) + Ok(ReturnAction::Normal) } /// Called immediately after actual memory was allocated for a local diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 9d0c4908225..36fe8dfdd29 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -308,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else { // Deallocating global memory -- always an error return Err(match self.tcx.try_get_global_alloc(alloc_id) { - Some(GlobalAlloc::Function(..)) => { + Some(GlobalAlloc::Function { .. }) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, alloc_id = alloc_id, @@ -555,7 +555,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Memory of a constant or promoted or anonymous memory referenced by a static. (mem, None) } - Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), + Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)), Some(GlobalAlloc::Static(def_id)) => { @@ -828,7 +828,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let alloc = alloc.inner(); (alloc.size(), alloc.align, AllocKind::LiveData) } - Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"), + Some(GlobalAlloc::Function { .. }) => { + bug!("We already checked function pointers above") + } Some(GlobalAlloc::VTable(..)) => { // No data to be accessed here. But vtables are pointer-aligned. return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable); @@ -865,7 +867,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Some(FnVal::Other(*extra)) } else { match self.tcx.try_get_global_alloc(id) { - Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)), + Some(GlobalAlloc::Function { instance, .. }) => Some(FnVal::Instance(instance)), _ => None, } } @@ -1056,8 +1058,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { alloc.inner(), )?; } - Some(GlobalAlloc::Function(func)) => { - write!(fmt, " (fn: {func})")?; + Some(GlobalAlloc::Function { instance, .. }) => { + write!(fmt, " (fn: {instance})")?; } Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 7d7b421f869..f703c6fbe3e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -26,7 +26,7 @@ pub use self::intern::{ intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind, InternResult, }; -pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; +pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, ReturnAction}; pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; pub use self::operand::{ImmTy, Immediate, OpTy, Readable}; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 1baf62baa81..b3124dfdfbc 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -32,7 +32,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We are unwinding and this fn has no cleanup code. // Just go on unwinding. trace!("unwinding: skipping frame"); - self.pop_stack_frame(/* unwinding */ true)?; + self.return_from_current_stack_frame(/* unwinding */ true)?; return Ok(true); }; let basic_block = &self.body().basic_blocks[loc.block]; diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 74521d0f493..25f6bd64055 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -4,9 +4,8 @@ use either::Either; use rustc_middle::ty::TyCtxt; use tracing::trace; -use rustc_middle::span_bug; use rustc_middle::{ - mir, + bug, mir, span_bug, ty::{ self, layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}, @@ -26,7 +25,10 @@ use super::{ InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar, StackPopCleanup, }; -use crate::fluent_generated as fluent; +use crate::{ + fluent_generated as fluent, + interpret::{eval_context::StackPopInfo, ReturnAction}, +}; /// An argment passed to a function. #[derive(Clone, Debug)] @@ -47,6 +49,15 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> { } } +struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { + callee: FnVal<'tcx, M::ExtraFnVal>, + args: Vec<FnArg<'tcx, M::Provenance>>, + fn_sig: ty::FnSig<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) + with_caller_location: bool, +} + impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. @@ -84,7 +95,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::TerminatorKind::*; match terminator.kind { Return => { - self.pop_stack_frame(/* unwinding */ false)? + self.return_from_current_stack_frame(/* unwinding */ false)? } Goto { target } => self.go_to_block(target), @@ -124,40 +135,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } => { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; - let func = self.eval_operand(func, None)?; - let args = self.eval_fn_call_arguments(args)?; - - let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); - let fn_sig = - self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); - let extra_args = &args[fn_sig.inputs().len()..]; - let extra_args = - self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); - - let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() { - ty::FnPtr(_sig) => { - let fn_ptr = self.read_pointer(&func)?; - let fn_val = self.get_ptr_fn(fn_ptr)?; - (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) - } - ty::FnDef(def_id, args) => { - let instance = self.resolve(def_id, args)?; - ( - FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, - instance.def.requires_caller_location(*self.tcx), - ) - } - _ => span_bug!( - terminator.source_info.span, - "invalid callee of type {}", - func.layout.ty - ), - }; + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; let destination = self.force_allocation(&self.eval_place(destination)?)?; self.eval_fn_call( - fn_val, + callee, (fn_sig.abi, fn_abi), &args, with_caller_location, @@ -172,6 +156,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + TailCall { ref func, ref args, fn_span: _ } => { + let old_frame_idx = self.frame_idx(); + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; + + if self.frame_idx() != old_frame_idx { + span_bug!( + terminator.source_info.span, + "evaluating this tail call pushed a new stack frame" + ); + } + } + Drop { place, target, unwind, replace: _ } => { let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); @@ -209,7 +209,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("unwinding: resuming from cleanup"); // By definition, a Resume terminator means // that we're unwinding - self.pop_stack_frame(/* unwinding */ true)?; + self.return_from_current_stack_frame(/* unwinding */ true)?; return Ok(()); } @@ -514,6 +514,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } + /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the + /// necessary information about callee and arguments to make a call. + fn eval_callee_and_args( + &self, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &[Spanned<mir::Operand<'tcx>>], + ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { + let func = self.eval_operand(func, None)?; + let args = self.eval_fn_call_arguments(args)?; + + let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = + self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); + + let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { + ty::FnPtr(_sig) => { + let fn_ptr = self.read_pointer(&func)?; + let fn_val = self.get_ptr_fn(fn_ptr)?; + (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) + } + ty::FnDef(def_id, args) => { + let instance = self.resolve(def_id, args)?; + ( + FnVal::Instance(instance), + self.fn_abi_of_instance(instance, extra_args)?, + instance.def.requires_caller_location(*self.tcx), + ) + } + _ => { + span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + }; + + Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + } + /// Call this function -- pushing the stack frame and initializing the arguments. /// /// `caller_fn_abi` is used to determine if all the arguments are passed the proper way. @@ -883,13 +922,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); - let concrete_method = Instance::resolve_for_vtable( + let concrete_method = Instance::expect_resolve_for_vtable( tcx, self.param_env, def_id, instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args), - ) - .unwrap(); + self.cur_span(), + ); assert_eq!(fn_inst, concrete_method); } @@ -924,6 +963,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + pub(crate) fn eval_fn_tail_call( + &mut self, + fn_val: FnVal<'tcx, M::ExtraFnVal>, + (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx, M::Provenance>], + with_caller_location: bool, + ) -> InterpResult<'tcx> { + trace!("eval_fn_call: {:#?}", fn_val); + + // This is the "canonical" implementation of tails calls, + // a pop of the current stack frame, followed by a normal call + // which pushes a new stack frame, with the return address from + // the popped stack frame. + // + // Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`, + // as the latter "executes" the goto to the return block, but we don't want to, + // only the tail called function should return to the current return block. + M::before_stack_pop(self, self.frame())?; + + let StackPopInfo { return_action, return_to_block, return_place } = + self.pop_stack_frame(false)?; + + assert_eq!(return_action, ReturnAction::Normal); + + let StackPopCleanup::Goto { ret, unwind } = return_to_block else { + bug!("can't tailcall as root"); + }; + + // FIXME(explicit_tail_calls): + // we should check if both caller&callee can/n't unwind, + // see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803> + + self.eval_fn_call( + fn_val, + (caller_abi, caller_fn_abi), + args, + with_caller_location, + &return_place, + ret, + unwind, + ) + } + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index add48e1b186..7fea0617666 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -745,7 +745,7 @@ fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => { + GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { // These are immutable, we better don't allow mutable pointers here. Mutability::Not } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f525510030b..c4b2e067bbe 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -50,7 +50,7 @@ libc = "0.2" memmap2 = "0.2.1" # tidy-alphabetical-end -[target.'cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))'.dependencies] +[target.'cfg(not(target_has_atomic = "64"))'.dependencies] portable-atomic = "1.5.1" [features] diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 32fad0de1aa..83fdaff515b 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -147,14 +147,13 @@ cfg_match! { [crate::owned_slice::OwnedSlice] ); - // MIPS, PowerPC and SPARC platforms with 32-bit pointers do not - // have AtomicU64 type. - #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc", target_arch = "sparc")))] + // Use portable AtomicU64 for targets without native 64-bit atomics + #[cfg(target_has_atomic = "64")] already_sync!( [std::sync::atomic::AtomicU64] ); - #[cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))] + #[cfg(not(target_has_atomic = "64"))] already_sync!( [portable_atomic::AtomicU64] ); diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index fed23df10dc..812ed410a94 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -70,18 +70,19 @@ macro_rules! compress { ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }}; ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{ $v0 = $v0.wrapping_add($v1); + $v2 = $v2.wrapping_add($v3); $v1 = $v1.rotate_left(13); $v1 ^= $v0; - $v0 = $v0.rotate_left(32); - $v2 = $v2.wrapping_add($v3); $v3 = $v3.rotate_left(16); $v3 ^= $v2; - $v0 = $v0.wrapping_add($v3); - $v3 = $v3.rotate_left(21); - $v3 ^= $v0; + $v0 = $v0.rotate_left(32); + $v2 = $v2.wrapping_add($v1); + $v0 = $v0.wrapping_add($v3); $v1 = $v1.rotate_left(17); $v1 ^= $v2; + $v3 = $v3.rotate_left(21); + $v3 ^= $v0; $v2 = $v2.rotate_left(32); }}; } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 5ae79ca988f..058a675c40d 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -35,11 +35,11 @@ //! | | | | //! | `ParallelIterator` | `Iterator` | `rayon::iter::ParallelIterator` | //! -//! [^1] `MTLock` is similar to `Lock`, but the serial version avoids the cost +//! [^1]: `MTLock` is similar to `Lock`, but the serial version avoids the cost //! of a `RefCell`. This is appropriate when interior mutability is not //! required. //! -//! [^2] `MTRef`, `MTLockRef` are type aliases. +//! [^2]: `MTRef`, `MTLockRef` are type aliases. pub use crate::marker::*; use std::collections::HashMap; @@ -270,12 +270,11 @@ cfg_match! { pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32}; - // MIPS, PowerPC and SPARC platforms with 32-bit pointers do not - // have AtomicU64 type. - #[cfg(not(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc")))] + // Use portable AtomicU64 for targets without native 64-bit atomics + #[cfg(target_has_atomic = "64")] pub use std::sync::atomic::AtomicU64; - #[cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))] + #[cfg(not(target_has_atomic = "64"))] pub use portable_atomic::AtomicU64; pub use std::sync::Arc as Lrc; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index bbe9741bf44..ad2acb03b3f 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -30,7 +30,7 @@ use rustc_errors::{ }; use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, get_codegen_backend}; -use rustc_interface::{interface, passes, Queries}; +use rustc_interface::{interface, passes, Linker, Queries}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; @@ -41,7 +41,6 @@ use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; use rustc_session::output::collect_crate_types; use rustc_session::{config, filesearch, EarlyDiagCtxt, Session}; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::FileLoader; use rustc_span::symbol::sym; use rustc_span::FileName; @@ -448,21 +447,9 @@ fn run_compiler( return early_exit(); } - let linker = queries.codegen_and_build_linker()?; - - // This must run after monomorphization so that all generic types - // have been instantiated. - if sess.opts.unstable_opts.print_type_sizes { - sess.code_stats.print_type_sizes(); - } - - if sess.opts.unstable_opts.print_vtable_sizes { - let crate_name = queries.global_ctxt()?.enter(|tcx| tcx.crate_name(LOCAL_CRATE)); - - sess.code_stats.print_vtable_sizes(crate_name); - } - - Ok(Some(linker)) + queries.global_ctxt()?.enter(|tcx| { + Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?)) + }) })?; // Linking is done outside the `compiler.enter()` so that the diff --git a/compiler/rustc_error_codes/src/error_codes/E0502.md b/compiler/rustc_error_codes/src/error_codes/E0502.md index dc3ffdfddd9..85f38b9286f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0502.md +++ b/compiler/rustc_error_codes/src/error_codes/E0502.md @@ -1,4 +1,5 @@ -A variable already borrowed as immutable was borrowed as mutable. +A variable already borrowed with a certain mutability (either mutable or +immutable) was borrowed again with a different mutability. Erroneous code example: @@ -13,7 +14,7 @@ fn foo(a: &mut i32) { ``` To fix this error, ensure that you don't have any other references to the -variable before trying to access it mutably: +variable before trying to access it with a different mutability: ``` fn bar(x: &mut i32) {} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e580910af77..d500f6d88a0 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -510,7 +510,7 @@ pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> { // would be bad. impl<G> !Clone for Diag<'_, G> {} -rustc_data_structures::static_assert_size!(Diag<'_, ()>, 2 * std::mem::size_of::<usize>()); +rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * std::mem::size_of::<usize>()); impl<G: EmissionGuarantee> Deref for Diag<'_, G> { type Target = DiagInner; @@ -582,6 +582,11 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { Self::new_diagnostic(dcx, DiagInner::new(level, message)) } + /// Allow moving diagnostics between different error tainting contexts + pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> { + Diag { dcx, diag: self.diag.take(), _marker: PhantomData } + } + /// Creates a new `Diag` with an already constructed diagnostic. #[track_caller] pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self { diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 0af80bc5c67..e6ca1bf7bc4 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -298,15 +298,21 @@ impl IntoDiagArg for hir::def::Namespace { } #[derive(Clone)] -pub struct DiagSymbolList(Vec<Symbol>); +pub struct DiagSymbolList<S = Symbol>(Vec<S>); -impl From<Vec<Symbol>> for DiagSymbolList { - fn from(v: Vec<Symbol>) -> Self { +impl<S> From<Vec<S>> for DiagSymbolList<S> { + fn from(v: Vec<S>) -> Self { DiagSymbolList(v) } } -impl IntoDiagArg for DiagSymbolList { +impl<S> FromIterator<S> for DiagSymbolList<S> { + fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self { + iter.into_iter().collect::<Vec<_>>().into() + } +} + +impl<S: std::fmt::Display> IntoDiagArg for DiagSymbolList<S> { fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::StrListSepByAnd( self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(), diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 45118bcc58a..aa47ca16676 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2273,9 +2273,26 @@ impl HumanEmitter { &normalize_whitespace(last_line), Style::NoStyle, ); - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); - buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); - buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); + if !line_to_add.trim().is_empty() { + // Check if after the removal, the line is left with only whitespace. If so, we + // will not show an "addition" line, as removing the whole line is what the user + // would really want. + // For example, for the following: + // | + // 2 - .await + // 2 + (note the left over whitepsace) + // | + // We really want + // | + // 2 - .await + // | + // *row_num -= 1; + buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); + buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); + } else { + *row_num -= 1; + } } else { *row_num -= 2; } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 91112a57277..2086d4030f9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -63,6 +63,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::{Loc, Span, DUMMY_SP}; use std::backtrace::{Backtrace, BacktraceStatus}; use std::borrow::Cow; +use std::cell::Cell; use std::error::Report; use std::fmt; use std::hash::Hash; @@ -98,9 +99,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); +rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24); #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); +rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { @@ -417,6 +418,9 @@ pub struct DiagCtxt { #[derive(Copy, Clone)] pub struct DiagCtxtHandle<'a> { dcx: &'a DiagCtxt, + /// Some contexts create `DiagCtxtHandle` with this field set, and thus all + /// errors emitted with it will automatically taint when emitting errors. + tainted_with_errors: Option<&'a Cell<Option<ErrorGuaranteed>>>, } impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { @@ -752,7 +756,17 @@ impl DiagCtxt { } pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> { - DiagCtxtHandle { dcx: self } + DiagCtxtHandle { dcx: self, tainted_with_errors: None } + } + + /// Link this to a taintable context so that emitting errors will automatically set + /// the `Option<ErrorGuaranteed>` instead of having to do that manually at every error + /// emission site. + pub fn taintable_handle<'a>( + &'a self, + tainted_with_errors: &'a Cell<Option<ErrorGuaranteed>>, + ) -> DiagCtxtHandle<'a> { + DiagCtxtHandle { dcx: self, tainted_with_errors: Some(tainted_with_errors) } } } @@ -795,7 +809,9 @@ impl<'a> DiagCtxtHandle<'a> { // can be used to create a backtrace at the stashing site insted of whenever the // diagnostic context is dropped and thus delayed bugs are emitted. Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))), - DelayedBug => return self.inner.borrow_mut().emit_diagnostic(diag), + DelayedBug => { + return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors); + } ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow | Expect(_) => None, }; @@ -947,16 +963,19 @@ impl<'a> DiagCtxtHandle<'a> { (0, _) => { // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a // configuration like `--cap-lints allow --force-warn bare_trait_objects`. - inner.emit_diagnostic(DiagInner::new( - ForceWarning(None), - DiagMessage::Str(warnings), - )); + inner.emit_diagnostic( + DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)), + None, + ); } (_, 0) => { - inner.emit_diagnostic(DiagInner::new(Error, errors)); + inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors); } (_, _) => { - inner.emit_diagnostic(DiagInner::new(Error, format!("{errors}; {warnings}"))); + inner.emit_diagnostic( + DiagInner::new(Error, format!("{errors}; {warnings}")), + self.tainted_with_errors, + ); } } @@ -987,14 +1006,14 @@ impl<'a> DiagCtxtHandle<'a> { "For more information about an error, try `rustc --explain {}`.", &error_codes[0] ); - inner.emit_diagnostic(DiagInner::new(FailureNote, msg1)); - inner.emit_diagnostic(DiagInner::new(FailureNote, msg2)); + inner.emit_diagnostic(DiagInner::new(FailureNote, msg1), None); + inner.emit_diagnostic(DiagInner::new(FailureNote, msg2), None); } else { let msg = format!( "For more information about this error, try `rustc --explain {}`.", &error_codes[0] ); - inner.emit_diagnostic(DiagInner::new(FailureNote, msg)); + inner.emit_diagnostic(DiagInner::new(FailureNote, msg), None); } } } @@ -1020,7 +1039,7 @@ impl<'a> DiagCtxtHandle<'a> { } pub fn emit_diagnostic(&self, diagnostic: DiagInner) -> Option<ErrorGuaranteed> { - self.inner.borrow_mut().emit_diagnostic(diagnostic) + self.inner.borrow_mut().emit_diagnostic(diagnostic, self.tainted_with_errors) } pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { @@ -1080,7 +1099,7 @@ impl<'a> DiagCtxtHandle<'a> { // Here the diagnostic is given back to `emit_diagnostic` where it was first // intercepted. Now it should be processed as usual, since the unstable expectation // id is now stable. - inner.emit_diagnostic(diag); + inner.emit_diagnostic(diag, self.tainted_with_errors); } } @@ -1430,13 +1449,17 @@ impl DiagCtxtInner { continue; } } - guar = guar.or(self.emit_diagnostic(diag)); + guar = guar.or(self.emit_diagnostic(diag, None)); } guar } // Return value is only `Some` if the level is `Error` or `DelayedBug`. - fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option<ErrorGuaranteed> { + fn emit_diagnostic( + &mut self, + mut diagnostic: DiagInner, + taint: Option<&Cell<Option<ErrorGuaranteed>>>, + ) -> Option<ErrorGuaranteed> { match diagnostic.level { Expect(expect_id) | ForceWarning(Some(expect_id)) => { // The `LintExpectationId` can be stable or unstable depending on when it was @@ -1609,6 +1632,9 @@ impl DiagCtxtInner { if is_lint { self.lint_err_guars.push(guar); } else { + if let Some(taint) = taint { + taint.set(Some(guar)); + } self.err_guars.push(guar); } self.panic_if_treat_err_as_bug(); @@ -1718,8 +1744,8 @@ impl DiagCtxtInner { // `-Ztreat-err-as-bug`, which we don't want. let note1 = "no errors encountered even though delayed bugs were created"; let note2 = "those delayed bugs will now be shown as internal compiler errors"; - self.emit_diagnostic(DiagInner::new(Note, note1)); - self.emit_diagnostic(DiagInner::new(Note, note2)); + self.emit_diagnostic(DiagInner::new(Note, note1), None); + self.emit_diagnostic(DiagInner::new(Note, note2), None); for bug in bugs { if let Some(out) = &mut out { @@ -1752,7 +1778,7 @@ impl DiagCtxtInner { } bug.level = Bug; - self.emit_diagnostic(bug); + self.emit_diagnostic(bug, None); } // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages. diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 56cbb54fcec..40e16b45115 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -172,7 +172,7 @@ impl<'a> StripUnconfigured<'a> { fn configure_tokens(&self, stream: &AttrTokenStream) -> AttrTokenStream { fn can_skip(stream: &AttrTokenStream) -> bool { stream.0.iter().all(|tree| match tree { - AttrTokenTree::Attributes(_) => false, + AttrTokenTree::AttrsTarget(_) => false, AttrTokenTree::Token(..) => true, AttrTokenTree::Delimited(.., inner) => can_skip(inner), }) @@ -185,22 +185,22 @@ impl<'a> StripUnconfigured<'a> { let trees: Vec<_> = stream .0 .iter() - .flat_map(|tree| match tree.clone() { - AttrTokenTree::Attributes(mut data) => { - data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); + .filter_map(|tree| match tree.clone() { + AttrTokenTree::AttrsTarget(mut target) => { + target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); - if self.in_cfg(&data.attrs) { - data.tokens = LazyAttrTokenStream::new( - self.configure_tokens(&data.tokens.to_attr_token_stream()), + if self.in_cfg(&target.attrs) { + target.tokens = LazyAttrTokenStream::new( + self.configure_tokens(&target.tokens.to_attr_token_stream()), ); - Some(AttrTokenTree::Attributes(data)).into_iter() + Some(AttrTokenTree::AttrsTarget(target)) } else { - None.into_iter() + None } } AttrTokenTree::Delimited(sp, spacing, delim, mut inner) => { inner = self.configure_tokens(&inner); - Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)).into_iter() + Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)) } AttrTokenTree::Token( Token { @@ -220,9 +220,7 @@ impl<'a> StripUnconfigured<'a> { ) => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree); } - AttrTokenTree::Token(token, spacing) => { - Some(AttrTokenTree::Token(token, spacing)).into_iter() - } + AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)), }) .collect(); AttrTokenStream::new(trees) @@ -294,7 +292,7 @@ impl<'a> StripUnconfigured<'a> { attr: &Attribute, (item, item_span): (ast::AttrItem, Span), ) -> Attribute { - let orig_tokens = attr.tokens(); + let orig_tokens = attr.get_tokens(); // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` // and producing an attribute of the form `#[attr]`. We @@ -310,12 +308,11 @@ impl<'a> StripUnconfigured<'a> { else { panic!("Bad tokens for attribute {attr:?}"); }; - let pound_span = pound_token.span; // We don't really have a good span to use for the synthesized `[]` // in `#[attr]`, so just use the span of the `#` token. let bracket_group = AttrTokenTree::Delimited( - DelimSpan::from_single(pound_span), + DelimSpan::from_single(pound_token.span), DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), Delimiter::Bracket, item.tokens diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index bf475c1dc96..2df8b8f00f8 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -120,21 +120,21 @@ struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> { struct BestFailure { token: Token, - position_in_tokenstream: usize, + position_in_tokenstream: u32, msg: &'static str, remaining_matcher: MatcherLoc, } impl BestFailure { - fn is_better_position(&self, position: usize) -> bool { + fn is_better_position(&self, position: u32) -> bool { position > self.position_in_tokenstream } } impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> { - type Failure = (Token, usize, &'static str); + type Failure = (Token, u32, &'static str); - fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure { + fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { (tok, position, msg) } @@ -211,9 +211,9 @@ impl<'matcher> FailureForwarder<'matcher> { } impl<'matcher> Tracker<'matcher> for FailureForwarder<'matcher> { - type Failure = (Token, usize, &'static str); + type Failure = (Token, u32, &'static str); - fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure { + fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { (tok, position, msg) } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 2fbd09fd9ae..99a9d4f8912 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -452,7 +452,7 @@ impl TtParser { &mut self, matcher: &'matcher [MatcherLoc], token: &Token, - approx_position: usize, + approx_position: u32, track: &mut T, ) -> Option<NamedParseResult<T::Failure>> { // Matcher positions that would be valid if the macro invocation was over now. Only diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e43ba7c3a5a..88ec3d83664 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -153,7 +153,7 @@ pub(super) trait Tracker<'matcher> { /// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected /// end of macro invocation. Otherwise, it indicates that no rules expected the given token. /// The usize is the approximate position of the token in the input token stream. - fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure; + fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure; /// This is called before trying to match next MatcherLoc on the current token. fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {} @@ -182,7 +182,7 @@ pub(super) struct NoopTracker; impl<'matcher> Tracker<'matcher> for NoopTracker { type Failure = (); - fn build_failure(_tok: Token, _position: usize, _msg: &'static str) -> Self::Failure {} + fn build_failure(_tok: Token, _position: u32, _msg: &'static str) -> Self::Failure {} fn description() -> &'static str { "none" diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 25958e03028..dbbd948fd70 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind}; use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; @@ -6,9 +6,10 @@ use rustc_errors::{Applicability, PResult}; use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; +pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. #[derive(Debug, PartialEq, Encodable, Decodable)] @@ -51,11 +52,26 @@ impl MetaVarExpr { let mut result = Vec::new(); loop { let is_var = try_eat_dollar(&mut iter); - let element_ident = parse_ident(&mut iter, psess, outer_span)?; + let token = parse_token(&mut iter, psess, outer_span)?; let element = if is_var { - MetaVarExprConcatElem::Var(element_ident) + MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?) + } else if let TokenKind::Literal(Lit { + kind: token::LitKind::Str, + symbol, + suffix: None, + }) = token.kind + { + MetaVarExprConcatElem::Literal(symbol) } else { - MetaVarExprConcatElem::Ident(element_ident) + match parse_ident_from_token(psess, token) { + Err(err) => { + err.cancel(); + return Err(psess + .dcx() + .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR)); + } + Ok(elem) => MetaVarExprConcatElem::Ident(elem), + } }; result.push(element); if iter.look_ahead(0).is_none() { @@ -105,11 +121,13 @@ impl MetaVarExpr { #[derive(Debug, Decodable, Encodable, PartialEq)] pub(crate) enum MetaVarExprConcatElem { - /// There is NO preceding dollar sign, which means that this identifier should be interpreted - /// as a literal. + /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be + /// interpreted as a literal. Ident(Ident), - /// There is a preceding dollar sign, which means that this identifier should be expanded - /// and interpreted as a variable. + /// For example, a number or a string. + Literal(Symbol), + /// Identifier WITH a preceding dollar sign, which means that this identifier should be + /// expanded and interpreted as a variable. Var(Ident), } @@ -158,7 +176,7 @@ fn parse_depth<'psess>( span: Span, ) -> PResult<'psess, usize> { let Some(tt) = iter.next() else { return Ok(0) }; - let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else { + let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else { return Err(psess .dcx() .struct_span_err(span, "meta-variable expression depth must be a literal")); @@ -180,12 +198,14 @@ fn parse_ident<'psess>( psess: &'psess ParseSess, fallback_span: Span, ) -> PResult<'psess, Ident> { - let Some(tt) = iter.next() else { - return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier")); - }; - let TokenTree::Token(token, _) = tt else { - return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier")); - }; + let token = parse_token(iter, psess, fallback_span)?; + parse_ident_from_token(psess, token) +} + +fn parse_ident_from_token<'psess>( + psess: &'psess ParseSess, + token: &Token, +) -> PResult<'psess, Ident> { if let Some((elem, is_raw)) = token.ident() { if let IdentIsRaw::Yes = is_raw { return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR)); @@ -205,10 +225,24 @@ fn parse_ident<'psess>( Err(err) } +fn parse_token<'psess, 't>( + iter: &mut RefTokenTreeCursor<'t>, + psess: &'psess ParseSess, + fallback_span: Span, +) -> PResult<'psess, &'t Token> { + let Some(tt) = iter.next() else { + return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR)); + }; + let TokenTree::Token(token, _) = tt else { + return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR)); + }; + Ok(token) +} + /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// iterator is not modified and the result is `false`. fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { + if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return true; } @@ -218,8 +252,7 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the /// iterator is not modified and the result is `false`. fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) - { + if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return true; } @@ -232,8 +265,7 @@ fn eat_dollar<'psess>( psess: &'psess ParseSess, span: Span, ) -> PResult<'psess, ()> { - if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) - { + if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return Ok(()); } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f935f1b77e0..9b4dc13c703 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -11,11 +11,13 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult}; +use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; use rustc_session::parse::ParseSess; +use rustc_session::parse::SymbolGallery; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext}; +use rustc_span::{with_metavar_spans, Span, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -312,7 +314,16 @@ pub(super) fn transcribe<'a>( // Replace meta-variable expressions with the result of their expansion. mbe::TokenTree::MetaVarExpr(sp, expr) => { - transcribe_metavar_expr(dcx, expr, interp, &mut marker, &repeats, &mut result, sp)?; + transcribe_metavar_expr( + dcx, + expr, + interp, + &mut marker, + &repeats, + &mut result, + sp, + &psess.symbol_gallery, + )?; } // If we are entering a new delimiter, we push its contents to the `stack` to be @@ -669,6 +680,7 @@ fn transcribe_metavar_expr<'a>( repeats: &[(usize, usize)], result: &mut Vec<TokenTree>, sp: &DelimSpan, + symbol_gallery: &SymbolGallery, ) -> PResult<'a, ()> { let mut visited_span = || { let mut span = sp.entire(); @@ -680,16 +692,26 @@ fn transcribe_metavar_expr<'a>( let mut concatenated = String::new(); for element in elements.into_iter() { let string = match element { - MetaVarExprConcatElem::Ident(ident) => ident.to_string(), - MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?, + MetaVarExprConcatElem::Ident(elem) => elem.to_string(), + MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(), + MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?, }; concatenated.push_str(&string); } + let symbol = nfc_normalize(&concatenated); + let concatenated_span = visited_span(); + if !rustc_lexer::is_ident(symbol.as_str()) { + return Err(dcx.struct_span_err( + concatenated_span, + "`${concat(..)}` is not generating a valid identifier", + )); + } + symbol_gallery.insert(symbol, concatenated_span); // The current implementation marks the span as coming from the macro regardless of // contexts of the concatenated identifiers but this behavior may change in the // future. result.push(TokenTree::Token( - Token::from_ast_ident(Ident::new(Symbol::intern(&concatenated), visited_span())), + Token::from_ast_ident(Ident::new(symbol, concatenated_span)), Spacing::Alone, )); } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 32a047a9363..7b27049a579 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -556,7 +556,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC 2632 gated!( - const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, const_trait_impl, + const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl, "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \ `impls` and all default bodies as `const`, which may be removed or renamed in the \ future." @@ -585,6 +585,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee) ), + // RFC 3543 + // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` + gated!( + patchable_function_entry, Normal, template!(List: "prefix_nops = m, entry_nops = n"), ErrorPreceding, + EncodeCrossCrate::Yes, experimental!(patchable_function_entry) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== @@ -826,6 +833,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_const_panic_str, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), + rustc_attr!( + rustc_runtime, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, INTERNAL_UNSTABLE + ), // ========================================================================== // Internal attributes, Layout related: @@ -1107,6 +1118,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( + TEST, rustc_dump_def_parents, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No ), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index f4e20328814..c05cac155b7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -563,6 +563,8 @@ declare_features! ( (unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), + /// Allows specifying nop padding on functions for dynamic patching. + (unstable, patchable_function_entry, "CURRENT_RUSTC_VERSION", Some(123115)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args. @@ -573,6 +575,8 @@ declare_features! ( (unstable, raw_ref_op, "1.41.0", Some(64490)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), + /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant + (incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 22a6c06bba3..d57fad6ba4c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -526,6 +526,7 @@ pub enum GenericParamKind<'hir> { /// Optional default value for the const generic param default: Option<&'hir AnonConst>, is_host_effect: bool, + synthetic: bool, }, } @@ -2413,7 +2414,7 @@ pub enum ImplItemKind<'hir> { /// * the `A: Bound` in `Trait<A: Bound>` /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`) -/// * the `f(): Bound` in `Trait<f(): Bound>` (feature `return_type_notation`) +/// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`) #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct AssocItemConstraint<'hir> { pub hir_id: HirId, @@ -3364,6 +3365,7 @@ pub enum ItemKind<'hir> { /// Refer to [`ImplItem`] for an associated item within an impl block. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Impl<'hir> { + pub constness: Constness, pub safety: Safety, pub polarity: ImplPolarity, pub defaultness: Defaultness, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 065ecc5d7b7..9bb3245ae05 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -543,6 +543,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_enum_def(enum_definition, item.hir_id())); } ItemKind::Impl(Impl { + constness: _, safety: _, defaultness: _, polarity: _, @@ -915,7 +916,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => { + GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => { try_visit!(visitor.visit_ty(ty)); if let Some(ref default) = default { try_visit!(visitor.visit_const_param_default(param.hir_id, default)); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 69461957f80..30c0e40206a 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -11,6 +11,7 @@ use crate::def_id::DefId; use crate::{MethodKind, Target}; use rustc_ast as ast; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -23,6 +24,7 @@ pub struct LanguageItems { /// Mappings from lang items to their possibly found [`DefId`]s. /// The index corresponds to the order in [`LangItem`]. items: [Option<DefId>; std::mem::variant_count::<LangItem>()], + reverse_items: FxIndexMap<DefId, LangItem>, /// Lang items that were not found during collection. pub missing: Vec<LangItem>, } @@ -30,7 +32,11 @@ pub struct LanguageItems { impl LanguageItems { /// Construct an empty collection of lang items and no missing ones. pub fn new() -> Self { - Self { items: [None; std::mem::variant_count::<LangItem>()], missing: Vec::new() } + Self { + items: [None; std::mem::variant_count::<LangItem>()], + reverse_items: FxIndexMap::default(), + missing: Vec::new(), + } } pub fn get(&self, item: LangItem) -> Option<DefId> { @@ -39,6 +45,11 @@ impl LanguageItems { pub fn set(&mut self, item: LangItem, def_id: DefId) { self.items[item as usize] = Some(def_id); + self.reverse_items.insert(def_id, item); + } + + pub fn from_def_id(&self, def_id: DefId) -> Option<LangItem> { + self.reverse_items.get(&def_id).copied() } pub fn iter(&self) -> impl Iterator<Item = (LangItem, DefId)> + '_ { @@ -393,6 +404,14 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + + EffectsRuntime, sym::EffectsRuntime, effects_runtime, Target::Struct, GenericRequirement::None; + EffectsNoRuntime, sym::EffectsNoRuntime, effects_no_runtime, Target::Struct, GenericRequirement::None; + EffectsMaybe, sym::EffectsMaybe, effects_maybe, Target::Struct, GenericRequirement::None; + EffectsIntersection, sym::EffectsIntersection, effects_intersection, Target::Trait, GenericRequirement::None; + EffectsIntersectionOutput, sym::EffectsIntersectionOutput, effects_intersection_output, Target::AssocTy, GenericRequirement::None; + EffectsCompat, sym::EffectsCompat, effects_compat, Target::Trait, GenericRequirement::Exact(1); + EffectsTyCompat, sym::EffectsTyCompat, effects_ty_compat, Target::Trait, GenericRequirement::Exact(1); } pub enum GenericRequirement { diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 7ed32fb9d9f..24c5377a3b1 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -55,8 +55,6 @@ hir_analysis_cannot_capture_late_bound_ty = cannot capture late-bound type parameter in {$what} .label = parameter defined here -hir_analysis_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}` - hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present .label = `for<...>` is here @@ -120,6 +118,10 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice .label = parameter captured again here +hir_analysis_effects_without_next_solver = using `#![feature(effects)]` without enabling next trait solver globally + .note = the next trait solver must be enabled globally for the effects feature to work correctly + .help = use `-Znext-solver` to enable + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl @@ -371,10 +373,6 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t hir_analysis_parenthesized_fn_trait_expansion = parenthesized trait syntax expands to `{$expanded_type}` -hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function - .suggestion = cast the value to `{$cast_ty}` - .help = cast the value to `{$cast_ty}` - hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types .label = this type is the same as the inner type without a pattern diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 7f0d72b3a8d..c30a6f1eeb9 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -1,8 +1,12 @@ //! Bounds are restrictions applied to some types after they've been lowered from the HIR to the //! [`rustc_middle::ty`] form. +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir::def::DefKind; use rustc_hir::LangItem; +use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; +use rustc_span::def_id::DefId; use rustc_span::Span; /// Collects together a list of type bounds. These lists of bounds occur in many places @@ -24,6 +28,7 @@ use rustc_span::Span; #[derive(Default, PartialEq, Eq, Clone, Debug)] pub struct Bounds<'tcx> { clauses: Vec<(ty::Clause<'tcx>, Span)>, + effects_min_tys: FxIndexMap<Ty<'tcx>, Span>, } impl<'tcx> Bounds<'tcx> { @@ -40,12 +45,14 @@ impl<'tcx> Bounds<'tcx> { pub fn push_trait_bound( &mut self, tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + defining_def_id: DefId, + bound_trait_ref: ty::PolyTraitRef<'tcx>, span: Span, polarity: ty::PredicatePolarity, + constness: ty::BoundConstness, ) { let clause = ( - trait_ref + bound_trait_ref .map_bound(|trait_ref| { ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity }) }) @@ -53,11 +60,98 @@ impl<'tcx> Bounds<'tcx> { span, ); // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands. - if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { + if tcx.is_lang_item(bound_trait_ref.def_id(), LangItem::Sized) { self.clauses.insert(0, clause); } else { self.clauses.push(clause); } + + if !tcx.features().effects { + return; + } + // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the + // associated type of `<T as Tr>` and make sure that the effect is compatible. + let compat_val = match (tcx.def_kind(defining_def_id), constness) { + // FIXME(effects): revisit the correctness of this + (_, ty::BoundConstness::Const) => tcx.consts.false_, + // body owners that can have trait bounds + (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => { + tcx.expected_host_effect_param_for_body(defining_def_id) + } + + (_, ty::BoundConstness::NotConst) => { + if !tcx.is_const_trait(bound_trait_ref.def_id()) { + return; + } + tcx.consts.true_ + } + + ( + DefKind::Trait | DefKind::Impl { of_trait: true }, + ty::BoundConstness::ConstIfConst, + ) => { + // this is either a where clause on an impl/trait header or on a trait. + // push `<T as Tr>::Effects` into the set for the `Min` bound. + let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { + tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc"); + return; + }; + + let ty = bound_trait_ref + .map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args)); + + // When the user has written `for<'a, T> X<'a, T>: ~const Foo`, replace the + // binders to dummy ones i.e. `X<'static, ()>` so they can be referenced in + // the `Min` associated type properly (which doesn't allow using `for<>`) + // This should work for any bound variables as long as they don't have any + // bounds e.g. `for<T: Trait>`. + // FIXME(effects) reconsider this approach to allow compatibility with `for<T: Tr>` + let ty = tcx.replace_bound_vars_uncached( + ty, + FnMutDelegate { + regions: &mut |_| tcx.lifetimes.re_static, + types: &mut |_| tcx.types.unit, + consts: &mut |_| unimplemented!("`~const` does not support const binders"), + }, + ); + + self.effects_min_tys.insert(ty, span); + return; + } + // for + // ``` + // trait Foo { type Bar: ~const Trait } + // ``` + // ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`. + // + // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor + // that uses a `Bar` that implements `Trait` with `Maybe` effects. + (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => { + // FIXME(effects): implement this + return; + } + // probably illegal in this position. + (_, ty::BoundConstness::ConstIfConst) => { + tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered"); + return; + } + }; + // create a new projection type `<T as Tr>::Effects` + let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { + tcx.dcx().span_delayed_bug( + span, + "`~const` trait bound has no effect assoc yet no errors encountered?", + ); + return; + }; + let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args); + // make `<T as Tr>::Effects: Compat<runtime>` + let new_trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::EffectsCompat, Some(span)), + [ty::GenericArg::from(self_ty), compat_val.into()], + ); + self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span)); } pub fn push_projection_bound( @@ -79,7 +173,15 @@ impl<'tcx> Bounds<'tcx> { self.clauses.insert(0, (trait_ref.upcast(tcx), span)); } - pub fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ { + pub fn clauses( + &self, + // FIXME(effects): remove tcx + _tcx: TyCtxt<'tcx>, + ) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ { self.clauses.iter().cloned() } + + pub fn effects_min_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ { + self.effects_min_tys.keys().copied() + } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e13ea1a1935..bf8ef18c04f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -25,9 +25,9 @@ use rustc_middle::ty::{ }; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; @@ -879,7 +879,8 @@ pub(super) fn check_specialization_validity<'tcx>( let result = opt_result.unwrap_or(Ok(())); if let Err(parent_impl) = result { - if !tcx.is_impl_trait_in_trait(impl_item) { + // FIXME(effects) the associated type from effects could be specialized + if !tcx.is_impl_trait_in_trait(impl_item) && !tcx.is_effects_desugared_assoc_ty(impl_item) { report_forbidden_specialization(tcx, impl_item, parent_impl); } else { tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 7fa5c96bc9a..6c53625b590 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -21,8 +21,9 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, @@ -981,7 +982,7 @@ fn report_trait_method_mismatch<'tcx>( .next() .unwrap_or(impl_err_span); - diag.span_suggestion( + diag.span_suggestion_verbose( span, "change the self-receiver type to match the trait", sugg, @@ -1005,12 +1006,12 @@ fn report_trait_method_mismatch<'tcx>( } hir::FnRetTy::Return(hir_ty) => { let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); + diag.span_suggestion_verbose(hir_ty.span, msg, sugg, ap); } }; }; } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( + diag.span_suggestion_verbose( impl_err_span, "change the parameter type to match the trait", trait_ty, @@ -1985,10 +1986,10 @@ pub(super) fn check_type_bounds<'tcx>( let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the - // span for an impl's associated type. Instead, for these, use the def_span for the synthesized - // associated type. - let impl_ty_span = if impl_ty.is_impl_trait_in_trait() { + // A synthetic impl Trait for RPITIT desugaring or assoc type for effects desugaring has no HIR, + // which we currently use to get the span for an impl's associated type. Instead, for these, + // use the def_span for the synthesized associated type. + let impl_ty_span = if impl_ty.is_impl_trait_in_trait() || impl_ty.is_effects_desugaring { tcx.def_span(impl_ty_def_id) } else { match tcx.hir_node_by_def_id(impl_ty_def_id) { diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index cc52a765802..ce921f64481 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -7,7 +7,7 @@ use rustc_session::config::EntryFnType; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use std::ops::Not; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f21aeb4c0b9..6282499883b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -26,15 +26,12 @@ fn equate_intrinsic_type<'tcx>( n_cts: usize, sig: ty::PolyFnSig<'tcx>, ) { - let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) { + let (generics, span) = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) | hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(.., generics, _), .. - }) => { - let own_counts = tcx.generics_of(def_id).own_counts(); - (own_counts, generics.span) - } + }) => (tcx.generics_of(def_id), generics.span), _ => { struct_span_code_err!(tcx.dcx(), span, E0622, "intrinsic must be a function") .with_span_label(span, "expected a function") @@ -42,6 +39,7 @@ fn equate_intrinsic_type<'tcx>( return; } }; + let own_counts = generics.own_counts(); let gen_count_ok = |found: usize, expected: usize, descr: &str| -> bool { if found != expected { @@ -57,9 +55,17 @@ fn equate_intrinsic_type<'tcx>( } }; + // the host effect param should be invisible as it shouldn't matter + // whether effects is enabled for the intrinsic provider crate. + let consts_count = if generics.host_effect_index.is_some() { + own_counts.consts - 1 + } else { + own_counts.consts + }; + if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime") && gen_count_ok(own_counts.types, n_tps, "type") - && gen_count_ok(own_counts.consts, n_cts, "const") + && gen_count_ok(consts_count, n_cts, "const") { let _ = check_function_signature( tcx, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 8469cbbbc7d..6a36938dd1d 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -96,10 +96,10 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::suggestions::{ +use rustc_trait_selection::error_reporting::traits::suggestions::{ ReturnsVisitor, TypeErrCtxtExt as _, }; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index a188c1b12ae..b2ef07d65c5 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -29,8 +29,8 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::misc::{ type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError, }; @@ -39,6 +39,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; +use rustc_type_ir::solve::NoSolution; use rustc_type_ir::TypeFlags; use std::cell::LazyCell; @@ -477,7 +478,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { param_env, item_def_id, tcx.explicit_item_bounds(item_def_id) - .instantiate_identity_iter_copied() + .iter_identity_copied() .collect::<Vec<_>>(), &FxIndexSet::default(), gat_def_id, @@ -913,7 +914,12 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()), // Const parameters are well formed if their type is structural match. - hir::GenericParamKind::Const { ty: hir_ty, default: _, is_host_effect: _ } => { + hir::GenericParamKind::Const { + ty: hir_ty, + default: _, + is_host_effect: _, + synthetic: _, + } => { let ty = tcx.type_of(param.def_id).instantiate_identity(); if tcx.features().adt_const_params { @@ -1199,17 +1205,16 @@ fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocIt let bounds = wfcx.tcx().explicit_item_bounds(item.def_id); debug!("check_associated_type_bounds: bounds={:?}", bounds); - let wf_obligations = - bounds.instantiate_identity_iter_copied().flat_map(|(bound, bound_span)| { - let normalized_bound = wfcx.normalize(span, None, bound); - traits::wf::clause_obligations( - wfcx.infcx, - wfcx.param_env, - wfcx.body_def_id, - normalized_bound, - bound_span, - ) - }); + let wf_obligations = bounds.iter_identity_copied().flat_map(|(bound, bound_span)| { + let normalized_bound = wfcx.normalize(span, None, bound); + traits::wf::clause_obligations( + wfcx.infcx, + wfcx.param_env, + wfcx.body_def_id, + normalized_bound, + bound_span, + ) + }); wfcx.register_obligations(wf_obligations); } @@ -1707,13 +1712,12 @@ fn receiver_is_valid<'tcx>( let cause = ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver); - let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty); - - // `self: Self` is always valid. - if can_eq_self(receiver_ty) { - if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, receiver_ty) { - infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); - } + // Special case `receiver == self_ty`, which doesn't necessarily require the `Receiver` lang item. + if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new(wfcx.infcx); + ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?; + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + }) { return true; } @@ -1724,58 +1728,51 @@ fn receiver_is_valid<'tcx>( autoderef = autoderef.include_raw_pointers(); } - // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. - autoderef.next(); - let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span)); // Keep dereferencing `receiver_ty` until we get to `self_ty`. - loop { - if let Some((potential_self_ty, _)) = autoderef.next() { - debug!( - "receiver_is_valid: potential self type `{:?}` to match `{:?}`", - potential_self_ty, self_ty - ); - - if can_eq_self(potential_self_ty) { - wfcx.register_obligations(autoderef.into_obligations()); + while let Some((potential_self_ty, _)) = autoderef.next() { + debug!( + "receiver_is_valid: potential self type `{:?}` to match `{:?}`", + potential_self_ty, self_ty + ); - if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty) { - infcx - .err_ctxt() - .report_mismatched_types(&cause, self_ty, potential_self_ty, err) - .emit(); - } + // Check if the self type unifies. If it does, then commit the result + // since it may have region side-effects. + if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new(wfcx.infcx); + ocx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty)?; + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + }) { + wfcx.register_obligations(autoderef.into_obligations()); + return true; + } + // Without `feature(arbitrary_self_types)`, we require that each step in the + // deref chain implement `receiver`. + if !arbitrary_self_types_enabled { + if !receiver_is_implemented( + wfcx, + receiver_trait_def_id, + cause.clone(), + potential_self_ty, + ) { + // We cannot proceed. break; - } else { - // Without `feature(arbitrary_self_types)`, we require that each step in the - // deref chain implement `receiver` - if !arbitrary_self_types_enabled - && !receiver_is_implemented( - wfcx, - receiver_trait_def_id, - cause.clone(), - potential_self_ty, - ) - { - return false; - } } - } else { - debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty); - return false; - } - } - // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`. - if !arbitrary_self_types_enabled - && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty) - { - return false; + // Register the bound, in case it has any region side-effects. + wfcx.register_bound( + cause.clone(), + wfcx.param_env, + potential_self_ty, + receiver_trait_def_id, + ); + } } - true + debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty); + false } fn receiver_is_implemented<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61adb7a3cba..2ecb170ec89 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::traits::misc::{ type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5cb91603fd0..16f72f38d60 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -286,7 +286,7 @@ fn orphan_check<'tcx>( tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, mode: OrphanCheckMode, -) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet<DefId>>> { +) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> { // We only accept this routine to be invoked on implementations // of a trait, not inherent implementations. let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -326,17 +326,16 @@ fn orphan_check<'tcx>( ty }; - Ok(ty) + Ok::<_, !>(ty) }; - let Ok(result) = traits::orphan_check_trait_ref::<!>( + let result = traits::orphan_check_trait_ref( &infcx, trait_ref, traits::InCrate::Local { mode }, lazily_normalize_ty, - ) else { - unreachable!() - }; + ) + .into_ok(); // (2) Try to map the remaining inference vars back to generic params. result.map_err(|err| match err { @@ -369,7 +368,7 @@ fn emit_orphan_check_error<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, impl_def_id: LocalDefId, - err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>, + err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>, ) -> ErrorGuaranteed { match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { @@ -482,7 +481,7 @@ fn emit_orphan_check_error<'tcx>( fn lint_uncovered_ty_params<'tcx>( tcx: TyCtxt<'tcx>, - UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>, + UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<TyCtxt<'tcx>, FxIndexSet<DefId>>, impl_def_id: LocalDefId, ) { let hir_id = tcx.local_def_id_to_hir_id(impl_def_id); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e7892f17660..632ab3120d0 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -18,7 +18,9 @@ use rustc_ast::Recovered; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228}; +use rustc_errors::{ + struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, StashKey, E0228, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, walk_generics, Visitor}; @@ -35,8 +37,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::iter; @@ -70,10 +72,10 @@ pub fn provide(providers: &mut Providers) { 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, - implied_predicates_of: predicates_of::implied_predicates_of, - super_predicates_that_define_assoc_item: - predicates_of::super_predicates_that_define_assoc_item, + explicit_super_predicates_of: predicates_of::explicit_super_predicates_of, + explicit_implied_predicates_of: predicates_of::explicit_implied_predicates_of, + explicit_supertraits_containing_assoc_item: + predicates_of::explicit_supertraits_containing_assoc_item, trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds, type_param_predicates: predicates_of::type_param_predicates, trait_def, @@ -161,7 +163,7 @@ pub struct CollectItemTypesVisitor<'tcx> { /// 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>, + cx: &dyn HirTyLowerer<'tcx>, generics: Option<&hir::Generics<'_>>, placeholder_types: Vec<Span>, suggest: bool, @@ -172,21 +174,21 @@ pub(crate) fn placeholder_type_error<'tcx>( return; } - placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind) + placeholder_type_error_diag(cx, generics, placeholder_types, vec![], suggest, hir_ty, kind) .emit(); } -pub(crate) fn placeholder_type_error_diag<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn placeholder_type_error_diag<'cx, 'tcx>( + cx: &'cx dyn HirTyLowerer<'tcx>, generics: Option<&hir::Generics<'_>>, placeholder_types: Vec<Span>, additional_spans: Vec<Span>, suggest: bool, hir_ty: Option<&hir::Ty<'_>>, kind: &'static str, -) -> Diag<'tcx> { +) -> Diag<'cx> { if placeholder_types.is_empty() { - return bad_placeholder(tcx, additional_spans, kind); + return bad_placeholder(cx, additional_spans, kind); } let params = generics.map(|g| g.params).unwrap_or_default(); @@ -210,7 +212,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>( } let mut err = - bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind); + bad_placeholder(cx, placeholder_types.into_iter().chain(additional_spans).collect(), kind); // Suggest, but only if it is not a function in const or static if suggest { @@ -224,7 +226,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>( // Check if parent is const or static is_const_or_static = matches!( - tcx.parent_hir_node(hir_ty.hir_id), + cx.tcx().parent_hir_node(hir_ty.hir_id), Node::Item(&hir::Item { kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..), .. @@ -267,7 +269,16 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(item); - placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr()); + let icx = ItemCtxt::new(tcx, item.owner_id.def_id); + + placeholder_type_error( + icx.lowerer(), + Some(generics), + visitor.0, + suggest, + None, + item.kind.descr(), + ); } impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { @@ -329,15 +340,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { /////////////////////////////////////////////////////////////////////////// // Utility types and common code for the above passes. -fn bad_placeholder<'tcx>( - tcx: TyCtxt<'tcx>, +fn bad_placeholder<'cx, 'tcx>( + cx: &'cx dyn HirTyLowerer<'tcx>, mut spans: Vec<Span>, kind: &'static str, -) -> Diag<'tcx> { +) -> Diag<'cx> { let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") }; spans.sort(); - tcx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind }) + cx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind }) } impl<'tcx> ItemCtxt<'tcx> { @@ -370,6 +381,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { self.tcx } + fn dcx(&self) -> DiagCtxtHandle<'_> { + self.tcx.dcx().taintable_handle(&self.tainted_by_errors) + } + fn item_def_id(&self) -> LocalDefId { self.item_def_id } @@ -377,14 +392,13 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { if let RegionInferReason::BorrowedObjectLifetimeDefault = reason { let e = struct_span_code_err!( - self.tcx().dcx(), + self.dcx(), span, E0228, "the lifetime bound for this object type cannot be deduced \ from context; please supply an explicit bound" ) .emit(); - self.set_tainted_by_errors(e); ty::Region::new_error(self.tcx(), e) } else { // This indicates an illegal lifetime in a non-assoc-trait position @@ -509,10 +523,6 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { None } - fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { - self.tainted_by_errors.set(Some(err)); - } - fn lower_fn_sig( &self, decl: &hir::FnDecl<'tcx>, @@ -570,7 +580,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. let mut diag = crate::collect::placeholder_type_error_diag( - tcx, + self, generics, visitor.0, infer_replacements.iter().map(|(s, _)| *s).collect(), @@ -590,7 +600,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { ); } - self.set_tainted_by_errors(diag.emit()); + diag.emit(); } (input_tys, output_ty) @@ -639,6 +649,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir().item(item_id); debug!(item = %it.ident, id = %it.hir_id()); let def_id = item_id.owner_id.def_id; + let icx = ItemCtxt::new(tcx, def_id); match &it.kind { // These don't define types. @@ -663,7 +674,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_foreign_item(item); placeholder_type_error( - tcx, + icx.lowerer(), None, visitor.0, false, @@ -691,14 +702,14 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { 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.at(it.span).explicit_super_predicates_of(def_id); tcx.ensure().predicates_of(def_id); tcx.ensure().associated_items(def_id); } hir::ItemKind::TraitAlias(..) => { tcx.ensure().generics_of(def_id); - tcx.at(it.span).implied_predicates_of(def_id); - tcx.at(it.span).super_predicates_of(def_id); + tcx.at(it.span).explicit_implied_predicates_of(def_id); + tcx.at(it.span).explicit_super_predicates_of(def_id); tcx.ensure().predicates_of(def_id); } hir::ItemKind::Struct(struct_def, _) | hir::ItemKind::Union(struct_def, _) => { @@ -742,7 +753,14 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { if !ty.is_suggestable_infer_ty() { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(it); - placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); + placeholder_type_error( + icx.lowerer(), + None, + visitor.0, + false, + None, + it.kind.descr(), + ); } } @@ -760,6 +778,7 @@ fn lower_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); + let icx = ItemCtxt::new(tcx, def_id.def_id); match trait_item.kind { hir::TraitItemKind::Fn(..) => { @@ -776,7 +795,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { // Account for `const C: _;`. let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); - placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant"); + placeholder_type_error( + icx.lowerer(), + None, + visitor.0, + false, + None, + "associated constant", + ); } } @@ -787,7 +813,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { // 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"); + placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); } hir::TraitItemKind::Type(_, None) => { @@ -798,7 +824,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); - placeholder_type_error(tcx, None, visitor.0, false, None, "associated type"); + placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); } }; @@ -811,6 +837,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); let impl_item = tcx.hir().impl_item(impl_item_id); + let icx = ItemCtxt::new(tcx, def_id.def_id); match impl_item.kind { hir::ImplItemKind::Fn(..) => { tcx.ensure().codegen_fn_attrs(def_id); @@ -821,14 +848,21 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); - placeholder_type_error(tcx, None, visitor.0, false, None, "associated type"); + placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); } hir::ImplItemKind::Const(ty, _) => { // Account for `const T: _ = ..;` if !ty.is_suggestable_infer_ty() { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); - placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant"); + placeholder_type_error( + icx.lowerer(), + None, + visitor.0, + false, + None, + "associated constant", + ); } } } @@ -1194,6 +1228,11 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), }; + let constness = if tcx.has_attr(def_id, sym::const_trait) { + hir::Constness::Const + } else { + hir::Constness::NotConst + }; let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar); if paren_sugar && !tcx.features().unboxed_closures { tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span }); @@ -1201,6 +1240,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let is_marker = tcx.has_attr(def_id, sym::marker); let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive); + let is_fundamental = tcx.has_attr(def_id, sym::fundamental); // FIXME: We could probably do way better attribute validation here. let mut skip_array_during_method_dispatch = false; @@ -1348,10 +1388,12 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { ty::TraitDef { def_id: def_id.to_def_id(), safety, + constness, paren_sugar, has_auto_impl: is_auto, is_marker, is_coinductive: rustc_coinductive || is_auto, + is_fundamental, skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch, specialization_kind, @@ -1377,7 +1419,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { - infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) } ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { @@ -1394,7 +1436,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn None, ) } else { - infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx) + infer_return_ty_for_fn_sig(sig, generics, def_id, &icx) } } @@ -1447,27 +1489,44 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn } fn infer_return_ty_for_fn_sig<'tcx>( - tcx: TyCtxt<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'_>, def_id: LocalDefId, icx: &ItemCtxt<'tcx>, ) -> ty::PolyFnSig<'tcx> { + let tcx = icx.tcx; let hir_id = tcx.local_def_id_to_hir_id(def_id); match sig.decl.output.get_infer_ret_ty() { 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`. + // This is a heuristic approach. If the scope has region paramters, + // we should change fn_sig's lifetime from `ReErased` to `ReError`, + // otherwise to `ReStatic`. + let has_region_params = generics.params.iter().any(|param| match param.kind { + GenericParamKind::Lifetime { .. } => true, + _ => false, + }); let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r { - ty::ReErased => tcx.lifetimes.re_static, + ty::ReErased => { + if has_region_params { + ty::Region::new_error_with_message( + tcx, + DUMMY_SP, + "erased region is not allowed here in return type", + ) + } else { + tcx.lifetimes.re_static + } + } _ => r, }); let mut visitor = HirPlaceholderCollector::default(); visitor.visit_ty(ty); - let mut diag = bad_placeholder(tcx, visitor.0, "return type"); + let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); let ret_ty = fn_sig.output(); // Don't leak types into signatures unless they're nameable! // For example, if a function returns itself, we don't want that @@ -1638,44 +1697,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai let icx = ItemCtxt::new(tcx, def_id); let item = tcx.hir().expect_item(def_id); let impl_ = item.expect_impl(); - impl_ - .of_trait - .as_ref() - .map(|hir_trait_ref| { - let self_ty = tcx.type_of(def_id).instantiate_identity(); + impl_.of_trait.as_ref().map(|ast_trait_ref| { + let selfty = tcx.type_of(def_id).instantiate_identity(); - let trait_ref = if let Some(ErrorGuaranteed { .. }) = check_impl_constness( - tcx, - tcx.is_const_trait_impl_raw(def_id.to_def_id()), - hir_trait_ref, - ) { - // we have a const impl, but for a trait without `#[const_trait]`, so - // without the host param. If we continue with the HIR trait ref, we get - // ICEs for generic arg count mismatch. We do a little HIR editing to - // make HIR ty lowering happy. - let mut path_segments = hir_trait_ref.path.segments.to_vec(); - let last_segment = path_segments.len() - 1; - let mut args = *path_segments[last_segment].args(); - let last_arg = args.args.len() - 1; - assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if anon_const.is_desugared_from_effects)); - args.args = &args.args[..args.args.len() - 1]; - path_segments[last_segment].args = Some(tcx.hir_arena.alloc(args)); - let path = hir::Path { - span: hir_trait_ref.path.span, - res: hir_trait_ref.path.res, - segments: tcx.hir_arena.alloc_slice(&path_segments), - }; - let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: hir_trait_ref.hir_ref_id }); - icx.lowerer().lower_impl_trait_ref(trait_ref, self_ty) - } else { - icx.lowerer().lower_impl_trait_ref(hir_trait_ref, self_ty) - }; - ty::ImplTraitHeader { - trait_ref: ty::EarlyBinder::bind(trait_ref), - safety: impl_.safety, - polarity: polarity_of_impl(tcx, def_id, impl_, item.span) - } - }) + check_impl_constness(tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), ast_trait_ref); + + let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty); + + ty::ImplTraitHeader { + trait_ref: ty::EarlyBinder::bind(trait_ref), + safety: impl_.safety, + polarity: polarity_of_impl(tcx, def_id, impl_, item.span), + } + }) } fn check_impl_constness( @@ -1688,7 +1722,7 @@ fn check_impl_constness( } let trait_def_id = hir_trait_ref.trait_def_id()?; - if tcx.has_attr(trait_def_id, sym::const_trait) { + if tcx.is_const_trait(trait_def_id) { return None; } diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 85e1c600d6d..c73d3a5390d 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,5 +1,7 @@ use rustc_hir::def::DefKind; -use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::intravisit; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::TyCtxt; use rustc_span::sym; @@ -41,3 +43,49 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) { } } } + +pub(crate) fn def_parents(tcx: TyCtxt<'_>) { + for did in tcx.hir().body_owners() { + if tcx.has_attr(did, sym::rustc_dump_def_parents) { + struct AnonConstFinder<'tcx> { + tcx: TyCtxt<'tcx>, + anon_consts: Vec<LocalDefId>, + } + + impl<'tcx> intravisit::Visitor<'tcx> for AnonConstFinder<'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_anon_const(&mut self, c: &'tcx rustc_hir::AnonConst) { + self.anon_consts.push(c.def_id); + intravisit::walk_anon_const(self, c) + } + } + + // Look for any anon consts inside of this body owner as there is no way to apply + // the `rustc_dump_def_parents` attribute to the anon const so it would not be possible + // to see what its def parent is. + let mut anon_ct_finder = AnonConstFinder { tcx, anon_consts: vec![] }; + intravisit::walk_expr(&mut anon_ct_finder, tcx.hir().body_owned_by(did).value); + + for did in [did].into_iter().chain(anon_ct_finder.anon_consts) { + let span = tcx.def_span(did); + + let mut diag = tcx.dcx().struct_span_err( + span, + format!("{}: {did:?}", sym::rustc_dump_def_parents.as_str()), + ); + + let mut current_did = did.to_def_id(); + while let Some(parent_did) = tcx.opt_parent(current_did) { + current_did = parent_did; + diag.span_note(tcx.def_span(parent_did), format!("{parent_did:?}")); + } + diag.emit(); + } + } + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 303fa23dbc1..22d465c8e62 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use crate::middle::resolve_bound_vars as rbv; use hir::{ intravisit::{self, Visitor}, @@ -87,14 +89,9 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { 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, - }; + let mut visitor = AnonConstInParamTyDetector { in_param_ty: false, ct: hir_id }; - visitor.visit_generics(generics); - in_param_ty = visitor.found_anon_const_in_param_ty; + in_param_ty = visitor.visit_generics(generics).is_break(); break; } } @@ -354,7 +351,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { kind, }) } - GenericParamKind::Const { ty: _, default, is_host_effect } => { + GenericParamKind::Const { ty: _, default, is_host_effect, synthetic } => { if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() // `host` effect params are allowed to have defaults. @@ -388,6 +385,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { kind: ty::GenericParamDefKind::Const { has_default: default.is_some(), is_host_effect, + synthetic, }, }) } @@ -459,50 +457,45 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S 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; - } + type Result = ControlFlow<Span>; + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow<Span> { match ty.kind { hir::TyKind::BareFn(..) => { self.outer_index.shift_in(1); - intravisit::walk_ty(self, ty); + let res = intravisit::walk_ty(self, ty); self.outer_index.shift_out(1); + res } _ => 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; - } + fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) -> ControlFlow<Span> { self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, tr); + let res = intravisit::walk_poly_trait_ref(self, tr); self.outer_index.shift_out(1); + res } - fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - if self.has_late_bound_regions.is_some() { - return; - } - + fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) -> ControlFlow<Span> { match self.tcx.named_bound_var(lt.hir_id) { - Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {} + Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => { + ControlFlow::Continue(()) + } Some(rbv::ResolvedArg::LateBound(debruijn, _, _)) - if debruijn < self.outer_index => {} + if debruijn < self.outer_index => + { + ControlFlow::Continue(()) + } Some( rbv::ResolvedArg::LateBound(..) | rbv::ResolvedArg::Free(..) | rbv::ResolvedArg::Error(_), ) - | None => { - self.has_late_bound_regions = Some(lt.ident.span); - } + | None => ControlFlow::Break(lt.ident.span), } } } @@ -512,11 +505,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S 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, - }; + let mut visitor = LateBoundRegionsDetector { tcx, outer_index: ty::INNERMOST }; for param in generics.params { if let GenericParamKind::Lifetime { .. } = param.kind { if tcx.is_late_bound(param.hir_id) { @@ -524,8 +513,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S } } } - visitor.visit_fn_decl(decl); - visitor.has_late_bound_regions + visitor.visit_fn_decl(decl).break_value() } let decl = node.fn_decl()?; @@ -535,25 +523,29 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S 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: _, is_host_effect: _ } = p.kind { + type Result = ControlFlow<()>; + + fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result { + if let GenericParamKind::Const { ty, default: _, is_host_effect: _, synthetic: _ } = p.kind + { let prev = self.in_param_ty; self.in_param_ty = true; - self.visit_ty(ty); + let res = self.visit_ty(ty); self.in_param_ty = prev; + res + } else { + ControlFlow::Continue(()) } } - fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { + fn visit_anon_const(&mut self, c: &'v hir::AnonConst) -> Self::Result { 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) + return ControlFlow::Break(()); } + intravisit::walk_anon_const(self, c) } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d084d3aefeb..c03e074c80b 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -8,6 +8,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFold use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; +use rustc_type_ir::Upcast; /// For associated types we include both bounds written on the type /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`. @@ -46,7 +47,7 @@ fn associated_type_bounds<'tcx>( } }); - let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent)); + let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent)); debug!( "associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id.to_def_id()), @@ -75,7 +76,7 @@ fn opaque_type_bounds<'tcx>( icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); debug!(?bounds); - tcx.arena.alloc_from_iter(bounds.clauses()) + tcx.arena.alloc_from_iter(bounds.clauses(tcx)) }) } @@ -124,6 +125,32 @@ pub(super) fn explicit_item_bounds_with_filter( None => {} } + if tcx.is_effects_desugared_assoc_ty(def_id.to_def_id()) { + let mut predicates = Vec::new(); + + let parent = tcx.local_parent(def_id); + + let preds = tcx.explicit_predicates_of(parent); + + if let ty::AssocItemContainer::TraitContainer = tcx.associated_item(def_id).container { + // for traits, emit `type Effects: TyCompat<<(T1::Effects, ..) as Min>::Output>` + let tup = Ty::new(tcx, ty::Tuple(preds.effects_min_tys)); + // FIXME(effects) span + let span = tcx.def_span(def_id); + let assoc = tcx.require_lang_item(hir::LangItem::EffectsIntersectionOutput, Some(span)); + let proj = Ty::new_projection(tcx, assoc, [tup]); + let self_proj = Ty::new_projection( + tcx, + def_id.to_def_id(), + ty::GenericArgs::identity_for_item(tcx, def_id), + ); + let trait_ = tcx.require_lang_item(hir::LangItem::EffectsTyCompat, Some(span)); + let trait_ref = ty::TraitRef::new(tcx, trait_, [self_proj, proj]); + predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span)); + } + return ty::EarlyBinder::bind(tcx.arena.alloc_from_iter(predicates)); + } + let bounds = match tcx.hir_node_by_def_id(def_id) { hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(bounds, _), diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 3421c8da4e9..b89d034fc2e 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -57,6 +57,7 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic #[instrument(level = "trace", skip(tcx), ret)] fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> { use rustc_hir::*; + use rustc_middle::ty::Ty; match tcx.opt_rpitit_info(def_id.to_def_id()) { Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => { @@ -84,6 +85,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen return ty::GenericPredicates { parent: Some(tcx.parent(def_id.to_def_id())), predicates: tcx.arena.alloc_from_iter(predicates), + effects_min_tys: ty::List::empty(), }; } @@ -105,6 +107,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen return ty::GenericPredicates { parent: Some(impl_def_id), predicates: tcx.arena.alloc_from_iter(impl_predicates), + effects_min_tys: ty::List::empty(), }; } @@ -124,6 +127,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // We use an `IndexSet` to preserve order of insertion. // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default(); + let mut effects_min_tys = Vec::new(); let hir_generics = node.generics().unwrap_or(NO_GENERICS); if let Node::Item(item) = node { @@ -150,11 +154,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // on a trait we must also consider the bounds that follow the trait's name, // like `trait Foo: A + B + C`. if let Some(self_bounds) = is_trait { - predicates.extend( - icx.lowerer() - .lower_mono_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All) - .clauses(), + let bounds = icx.lowerer().lower_mono_bounds( + tcx.types.self_param, + self_bounds, + PredicateFilter::All, ); + predicates.extend(bounds.clauses(tcx)); + effects_min_tys.extend(bounds.effects_min_tys()); } // In default impls, we can assume that the self type implements @@ -187,7 +193,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen param.span, ); trace!(?bounds); - predicates.extend(bounds.clauses()); + predicates.extend(bounds.clauses(tcx)); trace!(?predicates); } hir::GenericParamKind::Const { .. } => { @@ -238,7 +244,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen bound_vars, OnlySelfBounds(false), ); - predicates.extend(bounds.clauses()); + predicates.extend(bounds.clauses(tcx)); + effects_min_tys.extend(bounds.effects_min_tys()); } hir::WherePredicate::RegionPredicate(region_pred) => { @@ -297,7 +304,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // and the duplicated parameter, to ensure that they do not get out of sync. if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { let opaque_ty_node = tcx.parent_hir_node(hir_id); - let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else { + let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node + else { bug!("unexpected {opaque_ty_node:?}") }; debug!(?lifetimes); @@ -306,9 +314,30 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen debug!(?predicates); } + // add `Self::Effects: Compat<HOST>` to ensure non-const impls don't get called + // in const contexts. + if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) = node + && let Some(host_effect_index) = generics.host_effect_index + { + let parent = generics.parent.unwrap(); + let Some(assoc_def_id) = tcx.associated_type_for_effects(parent) else { + bug!("associated_type_for_effects returned None when there is host effect in generics"); + }; + let effects = + Ty::new_projection(tcx, assoc_def_id, ty::GenericArgs::identity_for_item(tcx, parent)); + let param = generics.param_at(host_effect_index, tcx); + let span = tcx.def_span(param.def_id); + let host = ty::Const::new_param(tcx, ty::ParamConst::for_def(param)); + let compat = tcx.require_lang_item(LangItem::EffectsCompat, Some(span)); + let trait_ref = + ty::TraitRef::new(tcx, compat, [ty::GenericArg::from(effects), host.into()]); + predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span)); + } + ty::GenericPredicates { parent: generics.parent, predicates: tcx.arena.alloc_from_iter(predicates), + effects_min_tys: tcx.mk_type_list(&effects_min_tys), } } @@ -459,6 +488,7 @@ pub(super) fn explicit_predicates_of<'tcx>( ty::GenericPredicates { parent: predicates_and_bounds.parent, predicates: tcx.arena.alloc_slice(&predicates), + effects_min_tys: predicates_and_bounds.effects_min_tys, } } } else { @@ -510,6 +540,7 @@ pub(super) fn explicit_predicates_of<'tcx>( return GenericPredicates { parent: parent_preds.parent, predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, + effects_min_tys: parent_preds.effects_min_tys, }; } gather_explicit_predicates_of(tcx, def_id) @@ -519,21 +550,21 @@ pub(super) fn explicit_predicates_of<'tcx>( /// Ensures that the super-predicates of the trait with a `DefId` /// of `trait_def_id` are lowered and stored. This also ensures that /// the transitive super-predicates are lowered. -pub(super) fn super_predicates_of( +pub(super) fn explicit_super_predicates_of( tcx: TyCtxt<'_>, trait_def_id: LocalDefId, ) -> ty::GenericPredicates<'_> { implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::SelfOnly) } -pub(super) fn super_predicates_that_define_assoc_item( +pub(super) fn explicit_supertraits_containing_assoc_item( tcx: TyCtxt<'_>, (trait_def_id, assoc_name): (DefId, Ident), ) -> ty::GenericPredicates<'_> { implied_predicates_with_filter(tcx, trait_def_id, PredicateFilter::SelfThatDefines(assoc_name)) } -pub(super) fn implied_predicates_of( +pub(super) fn explicit_implied_predicates_of( tcx: TyCtxt<'_>, trait_def_id: LocalDefId, ) -> ty::GenericPredicates<'_> { @@ -560,7 +591,7 @@ pub(super) fn implied_predicates_with_filter( // if `assoc_name` is None, then the query should've been redirected to an // external provider assert!(matches!(filter, PredicateFilter::SelfThatDefines(_))); - return tcx.super_predicates_of(trait_def_id); + return tcx.explicit_super_predicates_of(trait_def_id); }; let Node::Item(item) = tcx.hir_node_by_def_id(trait_def_id) else { @@ -587,7 +618,7 @@ pub(super) fn implied_predicates_with_filter( // Combine the two lists to form the complete set of superbounds: let implied_bounds = - &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match)); + &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match)); debug!(?implied_bounds); // Now require that immediate supertraits are lowered, which will, in @@ -601,7 +632,7 @@ pub(super) fn implied_predicates_with_filter( if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() && bound.polarity == ty::PredicatePolarity::Positive { - tcx.at(span).super_predicates_of(bound.def_id()); + tcx.at(span).explicit_super_predicates_of(bound.def_id()); } } } @@ -611,14 +642,18 @@ pub(super) fn implied_predicates_with_filter( if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() && bound.polarity == ty::PredicatePolarity::Positive { - tcx.at(span).implied_predicates_of(bound.def_id()); + tcx.at(span).explicit_implied_predicates_of(bound.def_id()); } } } _ => {} } - ty::GenericPredicates { parent: None, predicates: implied_bounds } + ty::GenericPredicates { + parent: None, + predicates: implied_bounds, + effects_min_tys: ty::List::empty(), + } } /// Returns the predicates defined on `item_def_id` of the form @@ -744,7 +779,7 @@ impl<'tcx> ItemCtxt<'tcx> { ); } - bounds.clauses().collect() + bounds.clauses(self.tcx).collect() } #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index abc3bb838db..cad7870a0a1 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -951,7 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.visit_ty(ty); } } - GenericParamKind::Const { ty, default, is_host_effect: _ } => { + GenericParamKind::Const { ty, default, .. } => { self.visit_ty(ty); if let Some(default) = default { self.visit_body(self.tcx.hir().body(default.body)); @@ -1760,7 +1760,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { if let Some(assoc_item) = trait_defines_associated_item_named(def_id) { break Some((bound_vars.into_iter().collect(), assoc_item)); } - let predicates = tcx.super_predicates_that_define_assoc_item((def_id, assoc_name)); + let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_name)); let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 1e2b0c43233..974dd415f46 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -12,6 +12,7 @@ use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; use crate::errors::TypeofReservedKeywordUsed; +use crate::hir_ty_lowering::HirTyLowerer; use super::bad_placeholder; use super::ItemCtxt; @@ -357,7 +358,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ .and_then(|body_id| { ty.is_suggestable_infer_ty().then(|| { infer_placeholder_type( - tcx, + icx.lowerer(), def_id, body_id, ty.span, @@ -381,7 +382,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ ImplItemKind::Const(ty, body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( - tcx, + icx.lowerer(), def_id, body_id, ty.span, @@ -405,7 +406,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ ItemKind::Static(ty, .., body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type( - tcx, + icx.lowerer(), def_id, body_id, ty.span, @@ -418,7 +419,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } ItemKind::Const(ty, _, body_id) => { if ty.is_suggestable_infer_ty() { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant") + infer_placeholder_type( + icx.lowerer(), + def_id, + body_id, + ty.span, + item.ident, + "constant", + ) } else { icx.lower_ty(ty) } @@ -559,21 +567,22 @@ pub(super) fn type_of_opaque( } } -fn infer_placeholder_type<'a>( - tcx: TyCtxt<'a>, +fn infer_placeholder_type<'tcx>( + cx: &dyn HirTyLowerer<'tcx>, def_id: LocalDefId, body_id: hir::BodyId, span: Span, item_ident: Ident, kind: &'static str, -) -> Ty<'a> { +) -> Ty<'tcx> { + let tcx = cx.tcx(); let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); // If this came from a free `const` or `static mut?` item, // then the user may have written e.g. `const A = 42;`. // In this case, the parser has stashed a diagnostic for // us to improve in typeck so we do that now. - let guar = tcx + let guar = cx .dcx() .try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| { if !ty.references_error() { @@ -602,7 +611,7 @@ fn infer_placeholder_type<'a>( } }) .unwrap_or_else(|| { - let mut diag = bad_placeholder(tcx, vec![span], kind); + let mut diag = bad_placeholder(cx, vec![span], kind); if !ty.references_error() { if let Some(ty) = ty.make_suggestable(tcx, false, None) { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 44025c3cd61..0ee87a13e9e 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; mod pattern_types; pub use pattern_types::*; +pub mod wrong_number_of_generic_args; mod precise_captures; pub(crate) use precise_captures::*; @@ -453,12 +454,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams { } 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, + err.span_suggestion_verbose( + self.span.shrink_to_hi(), fluent::hir_analysis_suggestion, format!( - "{}<{}>", - snippet, + "<{}>", self.missing_type_params .iter() .map(|n| n.to_string()) @@ -694,29 +694,6 @@ pub(crate) struct TypeOf<'tcx> { } #[derive(Diagnostic)] -#[diag(hir_analysis_pass_to_variadic_function, code = E0617)] -pub(crate) struct PassToVariadicFunction<'tcx, 'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'tcx>, - pub cast_ty: &'a str, - #[suggestion(code = "{replace}", applicability = "machine-applicable")] - pub sugg_span: Option<Span>, - pub replace: String, - #[help] - pub help: Option<()>, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_cast_thin_pointer_to_fat_pointer, code = E0607)] -pub(crate) struct CastThinPointerToFatPointer<'tcx> { - #[primary_span] - pub span: Span, - pub expr_ty: Ty<'tcx>, - pub cast_ty: String, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_invalid_union_field, code = E0740)] pub(crate) struct InvalidUnionField { #[primary_span] @@ -1699,3 +1676,9 @@ pub struct InvalidReceiverTy<'tcx> { pub span: Span, pub receiver_ty: Ty<'tcx>, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_effects_without_next_solver)] +#[note] +#[help] +pub struct EffectsWithoutNextSolver; diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 5d435a8edf9..6426ad9dc18 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -1,8 +1,8 @@ -use crate::structured_errors::StructuredDiag; -use rustc_errors::{codes::*, pluralize, Applicability, Diag, MultiSpan}; +use rustc_errors::{ + codes::*, pluralize, Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan, +}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; -use rustc_session::Session; use rustc_span::def_id::DefId; use std::iter; @@ -541,14 +541,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn start_diagnostics(&self) -> Diag<'tcx> { - let span = self.path_segment.ident.span; - let msg = self.create_error_message(); - self.tcx.dcx().struct_span_err(span, msg).with_code(self.code()) - } - /// Builds the `expected 1 type argument / supplied 2 type arguments` message. - fn notify(&self, err: &mut Diag<'_>) { + fn notify(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { let (quantifier, bound) = self.get_quantifier_and_bound(); let provided_args = self.num_provided_args(); @@ -600,7 +594,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest(&self, err: &mut Diag<'_>) { + fn suggest(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { debug!( "suggest(self.provided {:?}, self.gen_args.span(): {:?})", self.num_provided_args(), @@ -628,7 +622,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap<String>; /// ``` - fn suggest_adding_args(&self, err: &mut Diag<'_>) { + fn suggest_adding_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { if self.gen_args.parenthesized != hir::GenericArgsParentheses::No { return; } @@ -647,7 +641,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_>) { + fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment); let num_missing_args = self.num_missing_lifetime_args(); let num_params_to_take = num_missing_args; @@ -701,7 +695,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } - fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_>) { + fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { let num_missing_args = self.num_missing_type_or_const_args(); let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); @@ -761,7 +755,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```compile_fail /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)` /// ``` - fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diag<'_>) { + fn suggest_moving_args_from_assoc_fn_to_trait( + &self, + err: &mut Diag<'_, impl EmissionGuarantee>, + ) { let trait_ = match self.tcx.trait_of_item(self.def_id) { Some(def_id) => def_id, None => return, @@ -817,7 +814,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( &self, - err: &mut Diag<'_>, + err: &mut Diag<'_, impl EmissionGuarantee>, qpath: &'tcx hir::QPath<'tcx>, msg: String, num_assoc_fn_excess_args: usize, @@ -850,7 +847,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( &self, - err: &mut Diag<'_>, + err: &mut Diag<'_, impl EmissionGuarantee>, trait_def_id: DefId, expr: &'tcx hir::Expr<'tcx>, msg: String, @@ -904,7 +901,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// ```text /// type Map = HashMap<String, String, String, String>; /// ``` - fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_>) { + fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { let num_provided_lt_args = self.num_provided_lifetime_args(); let num_provided_type_const_args = self.num_provided_type_or_const_args(); let unbound_types = self.get_unbound_associated_types(); @@ -922,7 +919,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let provided_args_matches_unbound_traits = unbound_types.len() == num_redundant_type_or_const_args; - let remove_lifetime_args = |err: &mut Diag<'_>| { + let remove_lifetime_args = |err: &mut Diag<'_, _>| { let mut lt_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -963,7 +960,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { ); }; - let remove_type_or_const_args = |err: &mut Diag<'_>| { + let remove_type_or_const_args = |err: &mut Diag<'_, _>| { let mut gen_arg_spans = Vec::new(); let mut found_redundant = false; for arg in self.gen_args.args { @@ -1060,7 +1057,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Builds the `type defined here` message. - fn show_definition(&self, err: &mut Diag<'_>) { + fn show_definition(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) { if self.tcx.sess.source_map().is_span_accessible(def_span) { def_span.into() @@ -1111,7 +1108,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } /// Add note if `impl Trait` is explicitly specified. - fn note_synth_provided(&self, err: &mut Diag<'_>) { + fn note_synth_provided(&self, err: &mut Diag<'_, impl EmissionGuarantee>) { if !self.is_synth_provided() { return; } @@ -1120,17 +1117,16 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } -impl<'tcx> StructuredDiag<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { - fn session(&self) -> &Session { - self.tcx.sess - } - - fn code(&self) -> ErrCode { - E0107 - } - - fn diagnostic_common(&self) -> Diag<'tcx> { - let mut err = self.start_diagnostics(); +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> { + fn into_diag( + self, + dcx: rustc_errors::DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> Diag<'a, G> { + let msg = self.create_error_message(); + let mut err = Diag::new(dcx, level, msg); + err.code(E0107); + err.span(self.path_segment.ident.span); self.notify(&mut err); self.suggest(&mut err); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 802215b2843..a1feef9e15b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -76,7 +76,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if unbounds.len() > 1 { - tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { + self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { spans: unbounds.iter().map(|ptr| ptr.span).collect(), }); } @@ -90,7 +90,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { continue; } // There was a `?Trait` bound, but it was not `?Sized`; warn. - tcx.dcx().span_warn( + self.dcx().span_warn( unbound.span, "relaxing a default bound only does something for `?Sized`; \ all other traits are not bound by default", @@ -310,7 +310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { duplicates .entry(assoc_item.def_id) .and_modify(|prev_span| { - tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { + self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { span: constraint.span, prev_span: *prev_span, item_name: constraint.ident, @@ -338,7 +338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .into(), ty::GenericParamDefKind::Type { .. } => { let guar = *emitted_bad_param_err.get_or_insert_with(|| { - tcx.dcx().emit_err( + self.dcx().emit_err( crate::errors::ReturnTypeNotationIllegalParam::Type { span: path_span, param_span: tcx.def_span(param.def_id), @@ -349,7 +349,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } ty::GenericParamDefKind::Const { .. } => { let guar = *emitted_bad_param_err.get_or_insert_with(|| { - tcx.dcx().emit_err( + self.dcx().emit_err( crate::errors::ReturnTypeNotationIllegalParam::Const { span: path_span, param_span: tcx.def_span(param.def_id), @@ -371,7 +371,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { alias_ty.into() } else { - return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { + return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { span: constraint.span, ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), fn_span: tcx.hir().span_if_local(assoc_item.def_id), @@ -417,7 +417,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let ty = alias_term .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); let ty = - check_assoc_const_binding_type(tcx, constraint.ident, ty, constraint.hir_id); + check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id); tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } @@ -426,7 +426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match constraint.kind { hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { - return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { + return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { span: constraint.span, })); } @@ -462,7 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { late_bound_in_term, |br_name| { struct_span_code_err!( - tcx.dcx(), + self.dcx(), constraint.span, E0582, "binding for associated type `{}` references {}, \ @@ -519,7 +519,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>. fn check_assoc_const_binding_type<'tcx>( - tcx: TyCtxt<'tcx>, + cx: &dyn HirTyLowerer<'tcx>, assoc_const: Ident, ty: ty::Binder<'tcx, Ty<'tcx>>, hir_id: hir::HirId, @@ -536,13 +536,14 @@ fn check_assoc_const_binding_type<'tcx>( } let mut collector = GenericParamAndBoundVarCollector { - tcx, + cx, params: Default::default(), vars: Default::default(), depth: ty::INNERMOST, }; let mut guar = ty.visit_with(&mut collector).break_value(); + let tcx = cx.tcx(); let ty_note = ty .make_suggestable(tcx, false, None) .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); @@ -556,7 +557,7 @@ fn check_assoc_const_binding_type<'tcx>( for index in collector.params { let param = generics.param_at(index as _, tcx); let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper; - guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { + guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { span: assoc_const.span, assoc_const, param_name: param.name, @@ -574,7 +575,7 @@ fn check_assoc_const_binding_type<'tcx>( })); } for (var_def_id, var_name) in collector.vars { - guar.get_or_insert(tcx.dcx().emit_err( + guar.get_or_insert(cx.dcx().emit_err( crate::errors::EscapingBoundVarInTyOfAssocConstBinding { span: assoc_const.span, assoc_const, @@ -590,14 +591,14 @@ fn check_assoc_const_binding_type<'tcx>( Ty::new_error(tcx, guar) } -struct GenericParamAndBoundVarCollector<'tcx> { - tcx: TyCtxt<'tcx>, +struct GenericParamAndBoundVarCollector<'a, 'tcx> { + cx: &'a dyn HirTyLowerer<'tcx>, params: FxIndexSet<u32>, vars: FxIndexSet<(DefId, Symbol)>, depth: ty::DebruijnIndex, } -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> { +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> { type Result = ControlFlow<ErrorGuaranteed>; fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( @@ -620,7 +621,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> ty::BoundTyKind::Param(def_id, name) => (def_id, name), ty::BoundTyKind::Anon => { let reported = self - .tcx + .cx .dcx() .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var)); return ControlFlow::Break(reported); @@ -643,7 +644,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> ty::BrNamed(def_id, name) => (def_id, name), ty::BrAnon | ty::BrEnv => { let guar = self - .tcx + .cx .dcx() .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind)); return ControlFlow::Break(guar); @@ -661,7 +662,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> self.params.insert(param.index); } ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => { - let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var"); + let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var"); return ControlFlow::Break(guar); } _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 2d240699105..ebd2a63d555 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -4,7 +4,6 @@ use crate::errors::{ }; use crate::fluent_generated as fluent; use crate::hir_ty_lowering::HirTyLowerer; -use crate::traits::error_reporting::report_object_safety_error; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; @@ -27,6 +26,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::BytePos; use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::error_reporting::traits::report_object_safety_error; use rustc_trait_selection::traits::FulfillmentError; use rustc_trait_selection::traits::{ object_safety_violations_for_assoc_item, TraitAliasExpansionInfo, @@ -46,7 +46,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return; } - self.tcx().dcx().emit_err(MissingTypeParams { + self.dcx().emit_err(MissingTypeParams { span, def_span: self.tcx().def_span(def_id), span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), @@ -109,7 +109,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if is_impl { let trait_name = self.tcx().def_path_str(trait_def_id); - self.tcx().dcx().emit_err(ManualImplementation { span, trait_name }); + self.dcx().emit_err(ManualImplementation { span, trait_name }); } } @@ -156,7 +156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if is_dummy { err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span }); - return tcx.dcx().emit_err(err); + return self.dcx().emit_err(err); } let all_candidate_names: Vec<_> = all_candidates() @@ -174,7 +174,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind: assoc_kind_str, suggested_name, }); - return tcx.dcx().emit_err(err); + return self.dcx().emit_err(err); } // If we didn't find a good item in the supertraits (or couldn't get @@ -239,10 +239,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind: assoc_kind_str, suggested_name, }); - return tcx.dcx().emit_err(err); + return self.dcx().emit_err(err); } - let mut err = tcx.dcx().create_err(err); + let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( tcx, generics, @@ -264,7 +264,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } return err.emit(); } - return tcx.dcx().emit_err(err); + return self.dcx().emit_err(err); } } @@ -291,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); } - tcx.dcx().emit_err(err) + self.dcx().emit_err(err) } fn complain_about_assoc_kind_mismatch( @@ -347,7 +347,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (ident.span, None, assoc_kind, assoc_item.kind) }; - tcx.dcx().emit_err(errors::AssocKindMismatch { + self.dcx().emit_err(errors::AssocKindMismatch { span, expected: super::assoc_kind_str(expected), got: super::assoc_kind_str(got), @@ -366,8 +366,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { traits: &[String], name: Symbol, ) -> ErrorGuaranteed { - let mut err = - struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type"); + let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type"); if self .tcx() .resolutions(()) @@ -463,9 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } } - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported + err.emit() } pub(crate) fn complain_about_ambiguous_inherent_assoc_ty( @@ -475,16 +472,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> ErrorGuaranteed { let mut err = struct_span_code_err!( - self.tcx().dcx(), + self.dcx(), name.span, E0034, "multiple applicable items in scope" ); err.span_label(name.span, format!("multiple `{name}` found")); self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span); - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported + err.emit() } // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. @@ -576,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), name.span, E0220, "associated type `{name}` not found for `{self_ty}` in the current scope" @@ -662,7 +657,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds.sort(); bounds.dedup(); - let mut err = tcx.dcx().struct_span_err( + let mut err = self.dcx().struct_span_err( name.span, format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") ); @@ -829,7 +824,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_bound_spans.sort(); let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), trait_bound_spans, E0191, "the value of the associated type{} {} must be specified", @@ -974,7 +969,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - self.set_tainted_by_errors(err.emit()); + err.emit(); } /// On ambiguous associated type, look for an associated function whose name matches the @@ -1011,17 +1006,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter_by_name_unhygienic(Symbol::intern(&name)) .next() { - let reported = - struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type") - .with_span_suggestion_verbose( - ident2.span.to(ident3.span), - format!("there is an associated function with a similar name: `{name}`"), - name, - Applicability::MaybeIncorrect, - ) - .emit(); - self.set_tainted_by_errors(reported); - Err(reported) + Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") + .with_span_suggestion_verbose( + ident2.span.to(ident3.span), + format!("there is an associated function with a similar name: `{name}`"), + name, + Applicability::MaybeIncorrect, + ) + .emit()) } else { Ok(()) } @@ -1120,7 +1112,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let last_span = *arg_spans.last().unwrap(); let span: MultiSpan = arg_spans.into(); let mut err = struct_span_code_err!( - self.tcx().dcx(), + self.dcx(), span, E0109, "{kind} arguments are not allowed on {this_type}", @@ -1130,20 +1122,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_label(span, format!("not allowed on {what}")); } generics_args_err_extend(self.tcx(), segments, &mut err, err_extend); - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported + err.emit() } pub fn report_trait_object_addition_traits_error( &self, regular_traits: &Vec<TraitAliasExpansionInfo<'_>>, ) -> ErrorGuaranteed { - let tcx = self.tcx(); let first_trait = ®ular_traits[0]; let additional_trait = ®ular_traits[1]; let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), additional_trait.bottom().1, E0225, "only auto traits can be used as additional traits in a trait object" @@ -1169,9 +1158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for more information on them, visit \ <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>", ); - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported + err.emit() } pub fn report_trait_object_with_no_traits_error( @@ -1185,20 +1172,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|&(trait_ref, _)| trait_ref.def_id()) .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) .map(|trait_ref| tcx.def_span(trait_ref)); - let reported = - tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - self.set_tainted_by_errors(reported); - reported + + self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }) } } /// Emit an error for the given associated item constraint. pub fn prohibit_assoc_item_constraint( - tcx: TyCtxt<'_>, + cx: &dyn HirTyLowerer<'_>, constraint: &hir::AssocItemConstraint<'_>, segment: Option<(DefId, &hir::PathSegment<'_>, Span)>, ) -> ErrorGuaranteed { - let mut err = tcx.dcx().create_err(AssocItemConstraintsNotAllowedHere { + let tcx = cx.tcx(); + let mut err = cx.dcx().create_err(AssocItemConstraintsNotAllowedHere { span: constraint.span, fn_trait_expansion: if let Some((_, segment, span)) = segment && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 3f888c4e272..b1c77db9f37 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,9 +1,9 @@ -use super::IsMethodCall; +use super::{HirTyLowerer, IsMethodCall}; +use crate::errors::wrong_number_of_generic_args::{GenericArgsInfo, WrongNumberOfGenericArgs}; use crate::hir_ty_lowering::{ errors::prohibit_assoc_item_constraint, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, GenericArgsLowerer, }; -use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{ codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, @@ -13,7 +13,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; use rustc_middle::ty::{ - self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, + self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::symbol::{kw, sym}; @@ -22,15 +22,16 @@ use smallvec::SmallVec; /// Report an error that a generic argument did not match the generic parameter that was /// expected. fn generic_arg_mismatch_err( - tcx: TyCtxt<'_>, + cx: &dyn HirTyLowerer<'_>, arg: &GenericArg<'_>, param: &GenericParamDef, possible_ordering_error: bool, help: Option<String>, ) -> ErrorGuaranteed { + let tcx = cx.tcx(); let sess = tcx.sess; let mut err = struct_span_code_err!( - tcx.dcx(), + cx.dcx(), arg.span(), E0747, "{} provided when a {} was expected", @@ -171,7 +172,7 @@ fn generic_arg_mismatch_err( /// - `inferred_kind`: if no parameter was provided, and inference /// is enabled, then creates a suitable inference variable. pub fn lower_generic_args<'tcx: 'a, 'a>( - tcx: TyCtxt<'tcx>, + cx: &dyn HirTyLowerer<'tcx>, def_id: DefId, parent_args: &[ty::GenericArg<'tcx>], has_self: bool, @@ -179,6 +180,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( arg_count: &GenericArgCountResult, ctx: &mut impl GenericArgsLowerer<'a, 'tcx>, ) -> GenericArgsRef<'tcx> { + let tcx = cx.tcx(); // Collect the segments of the path; we need to instantiate arguments // for parameters throughout the entire path (wherever there are // generic parameters). @@ -256,6 +258,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( | GenericParamDefKind::Lifetime, _, ) => { + // FIXME(effects): this should be removed // SPECIAL CASE FOR DESUGARED EFFECT PARAMS // This comes from the following example: // @@ -325,7 +328,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( param_types_present.dedup(); generic_arg_mismatch_err( - tcx, + cx, arg, param, !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), @@ -380,7 +383,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( assert_eq!(kind, "lifetime"); let (provided_arg, param) = force_infer_lt.expect("lifetimes ought to have been inferred"); - generic_arg_mismatch_err(tcx, provided_arg, param, false, None); + generic_arg_mismatch_err(cx, provided_arg, param, false, None); } break; @@ -404,7 +407,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( /// 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<'_>, + cx: &dyn HirTyLowerer<'_>, def_id: DefId, generics: &ty::Generics, seg: &hir::PathSegment<'_>, @@ -415,14 +418,14 @@ pub fn check_generic_arg_count_for_call( IsMethodCall::No => GenericArgPosition::Value, }; let has_self = generics.parent.is_none() && generics.has_self; - check_generic_arg_count(tcx, def_id, seg, generics, gen_pos, has_self) + check_generic_arg_count(cx, def_id, seg, generics, gen_pos, has_self) } /// 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")] +#[instrument(skip(cx, gen_pos), level = "debug")] pub(crate) fn check_generic_arg_count( - tcx: TyCtxt<'_>, + cx: &dyn HirTyLowerer<'_>, def_id: DefId, seg: &hir::PathSegment<'_>, gen_params: &ty::Generics, @@ -445,7 +448,7 @@ pub(crate) fn check_generic_arg_count( .own_params .iter() .filter(|param| { - matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. }) + matches!(param.kind, ty::GenericParamDefKind::Const { synthetic: true, .. }) }) .count(); let named_const_param_count = param_counts.consts - synth_const_param_count; @@ -455,11 +458,11 @@ pub(crate) fn check_generic_arg_count( if gen_pos != GenericArgPosition::Type && let Some(c) = gen_args.constraints.first() { - prohibit_assoc_item_constraint(tcx, c, None); + prohibit_assoc_item_constraint(cx, c, None); } let explicit_late_bound = - prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos); let mut invalid_args = vec![]; @@ -485,17 +488,15 @@ pub(crate) fn check_generic_arg_count( GenericArgsInfo::MissingLifetimes { num_missing_args } }; - let reported = WrongNumberOfGenericArgs::new( - tcx, + let reported = cx.dcx().emit_err(WrongNumberOfGenericArgs::new( + cx.tcx(), gen_args_info, seg, gen_params, has_self as usize, gen_args, def_id, - ) - .diagnostic() - .emit(); + )); Err(reported) }; @@ -572,17 +573,17 @@ pub(crate) fn check_generic_arg_count( debug!(?gen_args_info); let reported = gen_args.has_err().unwrap_or_else(|| { - WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(all_params_are_binded) + cx.dcx() + .create_err(WrongNumberOfGenericArgs::new( + cx.tcx(), + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + )) + .emit_unless(all_params_are_binded) }); Err(reported) @@ -622,7 +623,7 @@ pub(crate) fn check_generic_arg_count( /// 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<'_>, + cx: &dyn HirTyLowerer<'_>, def: &ty::Generics, args: &hir::GenericArgs<'_>, position: GenericArgPosition, @@ -643,13 +644,13 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( if position == GenericArgPosition::Value && args.num_lifetime_params() != param_counts.lifetimes { - struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg) + struct_span_code_err!(cx.dcx(), span, E0794, "{}", msg) .with_span_note(span_late, note) .emit(); } else { let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note); - tcx.node_span_lint( + cx.tcx().node_span_lint( LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].hir_id(), multispan, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 240a749de96..29c71c3fa50 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -4,7 +4,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use super::HirTyLowerer; @@ -60,7 +60,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let msg = "trait objects must include the `dyn` keyword"; let label = "add `dyn` keyword before this trait"; let mut diag = - rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg); + rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg); if self_ty.span.can_be_used_for_suggestions() && !self.maybe_suggest_impl_trait(self_ty, &mut diag) { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 24ea328889c..a665306f2c6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -28,7 +28,8 @@ use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, + codes::*, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, + FatalError, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; @@ -47,8 +48,9 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; @@ -101,6 +103,8 @@ pub enum RegionInferReason<'a> { pub trait HirTyLowerer<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; + fn dcx(&self) -> DiagCtxtHandle<'_>; + /// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered. fn item_def_id(&self) -> LocalDefId; @@ -176,12 +180,6 @@ pub trait HirTyLowerer<'tcx> { /// The inference context of the lowering context if applicable. fn infcx(&self) -> Option<&InferCtxt<'tcx>>; - /// Taint the context with errors. - /// - /// Invoke this when you encounter an error from some prior pass like name resolution. - /// This is used to help suppress derived errors typeck might otherwise report. - fn set_tainted_by_errors(&self, e: ErrorGuaranteed); - /// Convenience method for coercing the lowering context into a trait object type. /// /// Most lowering routines are defined on the trait object type directly @@ -322,7 +320,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::BoundConstness::NotConst, ); if let Some(c) = item_segment.args().constraints.first() { - prohibit_assoc_item_constraint(self.tcx(), c, Some((def_id, item_segment, span))); + prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span))); } args } @@ -393,7 +391,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let mut arg_count = check_generic_arg_count( - tcx, + self, def_id, segment, generics, @@ -401,10 +399,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty.is_some(), ); - if let Err(err) = &arg_count.correct { - self.set_tainted_by_errors(err.reported); - } - // 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 item constraints will be handled regardless of whether there are @@ -559,13 +553,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness && generics.has_self - && !tcx.has_attr(def_id, sym::const_trait) + && !tcx.is_const_trait(def_id) { - let reported = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { span, modifier: constness.as_str(), }); - self.set_tainted_by_errors(reported); arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] }); } @@ -578,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { incorrect_args: &arg_count.correct, }; let args = lower_generic_args( - tcx, + self, def_id, parent_args, self_ty.is_some(), @@ -608,7 +601,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::BoundConstness::NotConst, ); if let Some(c) = item_segment.args().constraints.first() { - prohibit_assoc_item_constraint(self.tcx(), c, Some((item_def_id, item_segment, span))); + prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span))); } args } @@ -698,7 +691,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); debug!(?poly_trait_ref); - bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); + bounds.push_trait_bound( + tcx, + self.item_def_id().to_def_id(), + poly_trait_ref, + span, + polarity, + constness, + ); let mut dup_constraints = FxIndexMap::default(); for constraint in trait_segment.args().constraints { @@ -707,7 +707,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // would not be well-formed! if polarity != ty::PredicatePolarity::Positive { assert!( - self.tcx().dcx().has_errors().is_some(), + self.dcx().has_errors().is_some(), "negative trait bounds should not have assoc item constraints", ); continue; @@ -753,11 +753,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { constness, ); if let Some(c) = trait_segment.args().constraints.first() { - prohibit_assoc_item_constraint( - self.tcx(), - c, - Some((trait_def_id, trait_segment, span)), - ); + prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span))); } ty::TraitRef::new_from_args(self.tcx(), trait_def_id, generic_args) } @@ -869,7 +865,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, constraint, ); - self.set_tainted_by_errors(reported); return Err(reported); }; debug!(?bound); @@ -879,7 +874,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let assoc_kind_str = assoc_kind_str(assoc_kind); let ty_param_name = &ty_param_name.to_string(); - let mut err = tcx.dcx().create_err(crate::errors::AmbiguousAssocItem { + let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, assoc_name, @@ -952,7 +947,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )); } let reported = err.emit(); - self.set_tainted_by_errors(reported); if !where_bounds.is_empty() { return Err(reported); } @@ -1051,7 +1045,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // trait reference. let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { // A cycle error occurred, most likely. - tcx.dcx().span_bug(span, "expected cycle error"); + self.dcx().span_bug(span, "expected cycle error"); }; self.probe_single_bound_for_assoc_item( @@ -1081,10 +1075,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let reported = if variant_resolution.is_some() { // Variant in type position let msg = format!("expected type, found variant `{assoc_ident}`"); - tcx.dcx().span_err(span, msg) + self.dcx().span_err(span, msg) } else if qself_ty.is_enum() { let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), assoc_ident.span, E0599, "no variant named `{}` found for enum `{}`", @@ -1125,7 +1119,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() { // `<impl Trait as OtherTrait>::Assoc` makes no sense. struct_span_code_err!( - tcx.dcx(), + self.dcx(), tcx.def_span(alias_ty.def_id), E0667, "`impl Trait` is not allowed in path parameters" @@ -1145,7 +1139,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_ident.name, ) }; - self.set_tainted_by_errors(reported); return Err(reported); } }; @@ -1396,13 +1389,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); if !tcx.visibility(item_def_id).is_accessible_from(scope, tcx) { - let reported = tcx.dcx().emit_err(crate::errors::AssocItemIsPrivate { + self.dcx().emit_err(crate::errors::AssocItemIsPrivate { span, kind: tcx.def_descr(item_def_id), name: ident, defined_here_label: tcx.def_span(item_def_id), }); - self.set_tainted_by_errors(reported); } tcx.check_stability(item_def_id, Some(block), span, None); @@ -1556,7 +1548,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(c) = segment.args().constraints.first() { - return Err(prohibit_assoc_item_constraint(self.tcx(), c, None)); + return Err(prohibit_assoc_item_constraint(self, c, None)); } } @@ -1816,7 +1808,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `AlwaysApplicable` impl needs a `T: ?Sized` bound for // this to compile if we were to normalize here. if forbid_generic && ty.has_param() { - let mut err = tcx.dcx().struct_span_err( + let mut err = self.dcx().struct_span_err( path.span, "generic `Self` types are currently not permitted in anonymous constants", ); @@ -1828,7 +1820,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_note(impl_.self_ty.span, "not a concrete type"); } let reported = err.emit(); - self.set_tainted_by_errors(reported); Ty::new_error(tcx, reported) } else { ty @@ -1840,19 +1831,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { path.segments[..path.segments.len() - 2].iter(), GenericsArgsErrExtend::None, ); - // 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.lower_qpath( span, opt_self_ty, def_id, &path.segments[path.segments.len() - 2], path.segments.last().unwrap(), - constness, + ty::BoundConstness::NotConst, ) } Res::PrimTy(prim_ty) => { @@ -1875,7 +1860,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .tcx() .dcx() .span_delayed_bug(path.span, "path with `Res::Err` but no error emitted"); - self.set_tainted_by_errors(e); Ty::new_error(self.tcx(), e) } Res::Def(..) => { @@ -1886,7 +1870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); Ty::new_error( self.tcx(), - self.tcx().dcx().span_delayed_bug(span, "incorrect resolution for `Self`"), + self.dcx().span_delayed_bug(span, "incorrect resolution for `Self`"), ) } _ => span_bug!(span, "unexpected resolution: {:?}", path.res), @@ -1959,7 +1943,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let sig_span = self.tcx().def_span(sig_id); let mut try_emit = |descr| { if emit { - self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation { + self.dcx().emit_err(crate::errors::NotSupportedDelegation { span, descr, callee_span: sig_span, @@ -2009,8 +1993,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Ty<'tcx> { if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output) { - let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case"); - self.set_tainted_by_errors(e); + let e = self.dcx().span_delayed_bug(span, "not supported delegation case"); return Ty::new_error(self.tcx(), e); }; let sig = self.tcx().fn_sig(sig_id); @@ -2175,7 +2158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let ty = self.lower_ty(ty); let pat_ty = match pat.kind { hir::PatKind::Wild => { - let err = tcx.dcx().emit_err(WildPatTy { span: pat.span }); + let err = self.dcx().emit_err(WildPatTy { span: pat.span }); Ty::new_error(tcx, err) } hir::PatKind::Range(start, end, include_end) => { @@ -2355,7 +2338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { struct_span_code_err!( - tcx.dcx(), + self.dcx(), decl.output.span(), E0581, "return type references {}, which is not constrained by the fn input types", @@ -2406,11 +2389,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } #[instrument(level = "trace", skip(self, generate_err))] - fn validate_late_bound_regions( - &self, + fn validate_late_bound_regions<'cx>( + &'cx self, constrained_regions: FxHashSet<ty::BoundRegionKind>, referenced_regions: FxHashSet<ty::BoundRegionKind>, - generate_err: impl Fn(&str) -> Diag<'tcx>, + generate_err: impl Fn(&str) -> Diag<'cx>, ) { for br in referenced_regions.difference(&constrained_regions) { let br_name = match *br { @@ -2435,7 +2418,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.note("consider introducing a named lifetime parameter"); } - self.set_tainted_by_errors(err.emit()); + err.emit(); } } @@ -2476,7 +2459,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // error. let r = derived_region_bounds[0]; if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span })); + self.dcx().emit_err(AmbiguousLifetimeBound { span }); } Some(r) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index 34924f09d09..aafadc7f9cb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{DynKind, Upcast}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; +use rustc_trait_selection::error_reporting::traits::report_object_safety_error; use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations}; use smallvec::{smallvec, SmallVec}; @@ -59,7 +59,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut trait_bounds = vec![]; let mut projection_bounds = vec![]; - for (pred, span) in bounds.clauses() { + for (pred, span) in bounds.clauses(tcx) { let bound_pred = pred.kind(); match bound_pred.skip_binder() { ty::ClauseKind::Trait(trait_pred) => { @@ -133,7 +133,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.associated_items(pred.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| !item.is_impl_trait_in_trait()) + .filter(|item| { + !item.is_impl_trait_in_trait() && !item.is_effects_desugaring + }) .map(|item| item.def_id), ); } @@ -234,7 +236,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_misc_error(tcx).into() } else if arg.walk().any(|arg| arg == dummy_self.into()) { references_self = true; - let guar = tcx.dcx().span_delayed_bug( + let guar = self.dcx().span_delayed_bug( span, "trait object trait bounds reference `Self`", ); @@ -260,8 +262,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if references_self { let def_id = i.bottom().0.def_id(); - let reported = struct_span_code_err!( - tcx.dcx(), + struct_span_code_err!( + self.dcx(), i.bottom().1, E0038, "the {} `{}` cannot be made into an object", @@ -273,7 +275,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .error_msg(), ) .emit(); - self.set_tainted_by_errors(reported); } ty::ExistentialTraitRef { def_id: trait_ref.def_id, args } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index f3ce3ab6655..5b8b6e98125 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -78,7 +78,7 @@ use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt}; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0428abcdf24..dd7fbba753b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -71,6 +71,7 @@ This API is completely unstable and subject to change. #![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] +#![feature(unwrap_infallible)] // tidy-alphabetical-end #[macro_use] @@ -91,7 +92,6 @@ mod errors; pub mod hir_wf_check; mod impl_wf_check; mod outlives; -pub mod structured_errors; mod variance; use rustc_hir as hir; @@ -151,6 +151,12 @@ pub fn provide(providers: &mut Providers) { pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); + // FIXME(effects): remove once effects is implemented in old trait solver + // or if the next solver is stabilized. + if tcx.features().effects && !tcx.next_trait_solver_globally() { + tcx.dcx().emit_err(errors::EffectsWithoutNextSolver); + } + tcx.sess.time("coherence_checking", || { tcx.hir().par_for_each_module(|module| { let _ = tcx.ensure().check_mod_type_wf(module); @@ -169,6 +175,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { tcx.sess.time("variance_dumping", || variance::dump::variances(tcx)); collect::dump::opaque_hidden_types(tcx); collect::dump::predicates_and_item_bounds(tcx); + collect::dump::def_parents(tcx); } // Make sure we evaluate all static and (non-associated) const items, even if unused. diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 5086c2af3f6..08015c28a26 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -1,9 +1,9 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_middle::ty::{self, Region, Ty, TyCtxt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_type_ir::outlives::{push_outlives_components, Component}; use smallvec::smallvec; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred diff --git a/compiler/rustc_hir_analysis/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs deleted file mode 100644 index 5abfd25dd95..00000000000 --- a/compiler/rustc_hir_analysis/src/structured_errors.rs +++ /dev/null @@ -1,36 +0,0 @@ -mod missing_cast_for_variadic_arg; -mod sized_unsized_cast; -mod wrong_number_of_generic_args; - -pub use self::{ - missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*, -}; - -use rustc_errors::{Diag, ErrCode}; -use rustc_session::Session; - -pub trait StructuredDiag<'tcx> { - fn session(&self) -> &Session; - - fn code(&self) -> ErrCode; - - fn diagnostic(&self) -> Diag<'tcx> { - let err = self.diagnostic_common(); - - if self.session().teach(self.code()) { - self.diagnostic_extended(err) - } else { - self.diagnostic_regular(err) - } - } - - fn diagnostic_common(&self) -> Diag<'tcx>; - - fn diagnostic_regular(&self, err: Diag<'tcx>) -> Diag<'tcx> { - err - } - - fn diagnostic_extended(&self, err: Diag<'tcx>) -> Diag<'tcx> { - err - } -} diff --git a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs deleted file mode 100644 index 0e78acbeae2..00000000000 --- a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{errors, structured_errors::StructuredDiag}; -use rustc_errors::{codes::*, Diag}; -use rustc_middle::ty::{Ty, TypeVisitableExt}; -use rustc_session::Session; -use rustc_span::Span; - -pub struct MissingCastForVariadicArg<'tcx, 's> { - pub sess: &'tcx Session, - pub span: Span, - pub ty: Ty<'tcx>, - pub cast_ty: &'s str, -} - -impl<'tcx> StructuredDiag<'tcx> for MissingCastForVariadicArg<'tcx, '_> { - fn session(&self) -> &Session { - self.sess - } - - fn code(&self) -> ErrCode { - E0617 - } - - fn diagnostic_common(&self) -> Diag<'tcx> { - let (sugg_span, replace, help) = - if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) { - (Some(self.span), format!("{} as {}", snippet, self.cast_ty), None) - } else { - (None, "".to_string(), Some(())) - }; - - let mut err = self.sess.dcx().create_err(errors::PassToVariadicFunction { - span: self.span, - ty: self.ty, - cast_ty: self.cast_ty, - help, - replace, - sugg_span, - }); - - if self.ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - fn diagnostic_extended(&self, mut err: Diag<'tcx>) -> Diag<'tcx> { - err.note(format!( - "certain types, like `{}`, must be casted before passing them to a \ - variadic function, because of arcane ABI rules dictated by the C \ - standard", - self.ty - )); - - err - } -} diff --git a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs deleted file mode 100644 index 9e871ff9af0..00000000000 --- a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::{errors, structured_errors::StructuredDiag}; -use rustc_errors::{codes::*, Diag}; -use rustc_middle::ty::{Ty, TypeVisitableExt}; -use rustc_session::Session; -use rustc_span::Span; - -pub struct SizedUnsizedCast<'tcx> { - pub sess: &'tcx Session, - pub span: Span, - pub expr_ty: Ty<'tcx>, - pub cast_ty: String, -} - -impl<'tcx> StructuredDiag<'tcx> for SizedUnsizedCast<'tcx> { - fn session(&self) -> &Session { - self.sess - } - - fn code(&self) -> ErrCode { - E0607 - } - - fn diagnostic_common(&self) -> Diag<'tcx> { - let mut err = self.sess.dcx().create_err(errors::CastThinPointerToFatPointer { - span: self.span, - expr_ty: self.expr_ty, - cast_ty: self.cast_ty.to_owned(), - }); - - if self.expr_ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - fn diagnostic_extended(&self, mut err: Diag<'tcx>) -> Diag<'tcx> { - err.help( - "Thin pointers are \"simple\" pointers: they are purely a reference to a -memory address. - -Fat pointers are pointers referencing \"Dynamically Sized Types\" (also -called DST). DST don't have a statically known size, therefore they can -only exist behind some kind of pointers that contain additional -information. Slices and trait objects are DSTs. In the case of slices, -the additional information the fat pointer holds is their size. - -To fix this error, don't try to cast directly between thin and fat -pointers. - -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions", - ); - err - } -} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 25b0cbdc026..5105d60ae18 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -585,6 +585,7 @@ impl<'a> State<'a> { self.print_struct(struct_def, generics, item.ident.name, item.span, true); } hir::ItemKind::Impl(&hir::Impl { + constness, safety, polarity, defaultness, @@ -599,6 +600,10 @@ impl<'a> State<'a> { self.print_safety(safety); self.word_nbsp("impl"); + if let hir::Constness::Const = constness { + self.word_nbsp("const"); + } + if !generics.params.is_empty() { self.print_generic_params(generics.params); self.space(); @@ -2144,7 +2149,7 @@ impl<'a> State<'a> { self.print_type(default); } } - GenericParamKind::Const { ty, ref default, is_host_effect: _ } => { + GenericParamKind::Const { ty, ref default, is_host_effect: _, synthetic: _ } => { self.word_space(":"); self.print_type(ty); if let Some(default) = default { diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index d6f3f4d640b..39d430cf73b 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -23,6 +23,22 @@ hir_typeck_cannot_cast_to_bool = cannot cast `{$expr_ty}` as `bool` hir_typeck_cast_enum_drop = cannot cast enum `{$expr_ty}` into integer `{$cast_ty}` because it implements `Drop` +hir_typeck_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}` + .teach_help = Thin pointers are "simple" pointers: they are purely a reference to a + memory address. + + Fat pointers are pointers referencing "Dynamically Sized Types" (also + called DST). DST don't have a statically known size, therefore they can + only exist behind some kind of pointers that contain additional + information. Slices and trait objects are DSTs. In the case of slices, + the additional information the fat pointer holds is their size. + + To fix this error, don't try to cast directly between thin and fat + pointers. + + For more information about casts, take a look at The Book: + https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions", + hir_typeck_cast_unknown_pointer = cannot cast {$to -> [true] to *[false] from @@ -123,6 +139,16 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` +hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function + .suggestion = cast the value to `{$cast_ty}` + .help = cast the value to `{$cast_ty}` + .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard + +hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len -> + [1] an auto trait {$traits} + *[other] auto traits {$traits} +} to a trait object in a pointer cast may cause UB later on + hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 3b199b7e3c2..0d2a55a9507 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -24,8 +24,8 @@ 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::error_reporting::traits::DefIdOrName; 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, slice}; diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 92f2d3254bb..341d533492d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,9 +32,9 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; -use hir::ExprKind; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::mir::Mutability; @@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::DUMMY_SP; @@ -73,7 +73,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - VTable(Option<DefId>), + VTable(&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>), /// Slice Length, /// The unsize info of this projection or opaque type @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -319,7 +319,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { errors::CannotCastToBoolHelp::Unsupported(self.span) }; - fcx.tcx.dcx().emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help }); + fcx.dcx().emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help }); } CastError::CastToChar => { let mut err = type_error_struct!( @@ -500,16 +500,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - use rustc_hir_analysis::structured_errors::{SizedUnsizedCast, StructuredDiag}; - - SizedUnsizedCast { - sess: fcx.tcx.sess, + fcx.dcx().emit_err(errors::CastThinPointerToFatPointer { span: self.span, expr_ty: self.expr_ty, cast_ty: fcx.ty_to_string(self.cast_ty), - } - .diagnostic() - .emit(); + teach: fcx.tcx.sess.teach(E0607).then_some(()), + }); } CastError::IntToFatCast(known_metadata) => { let expr_if_nightly = fcx.tcx.sess.is_nightly_build().then_some(self.expr_span); @@ -759,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(CastError::IllegalCast) } - // ptr -> * + // ptr -> ptr (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast @@ -803,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn check_ptr_ptr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, + m_src: ty::TypeAndMut<'tcx>, + m_dst: ty::TypeAndMut<'tcx>, ) -> Result<CastKind, CastError> { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); + debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}"); // ptr-ptr cast. vtables must match. - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; + let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); + let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?); - let Some(cast_kind) = cast_kind else { + match (src_kind, dst_kind) { // We can't cast if target pointer kind is unknown - return Err(CastError::UnknownCastPtrKind); - }; + (_, None) => Err(CastError::UnknownCastPtrKind), + // Cast to thin pointer is OK + (_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast), - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); - } - - let Some(expr_kind) = expr_kind else { // We can't cast to fat pointer if source pointer kind is unknown - return Err(CastError::UnknownExprPtrKind); - }; + (None, _) => Err(CastError::UnknownExprPtrKind), + + // thin -> fat? report invalid cast (don't complain about vtable kinds) + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + + // trait object -> trait object? need to do additional checks + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + match (src_tty.principal(), dst_tty.principal()) { + // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure + // - `Src` and `Dst` traits are the same + // - traits have the same generic arguments + // - `SrcAuto` is a superset of `DstAuto` + (Some(src_principal), Some(dst_principal)) => { + let tcx = fcx.tcx; + + // Check that the traits are actually the same. + // The `dyn Src = dyn Dst` check below would suffice, + // but this may produce a better diagnostic. + // + // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) + // and is unaffected by this check. + if src_principal.def_id() != dst_principal.def_id() { + return Err(CastError::DifferingKinds); + } - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } + // We need to reconstruct trait object types. + // `m_src` and `m_dst` won't work for us here because they will potentially + // contain wrappers, which we do not care about. + // + // e.g. we want to allow `dyn T -> (dyn T,)`, etc. + // + // We also need to skip auto traits to emit an FCW and not an error. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); - // vtable kinds must match - if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { - Ok(CastKind::PtrPtrCast) - } else { - Err(CastError::DifferingKinds) + // `dyn Src = dyn Dst`, this checks for matching traits/generics + fcx.demand_eqtype(self.span, src_obj, dst_obj); + + // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`. + // Emit an FCW otherwise. + let src_auto: FxHashSet<_> = src_tty + .auto_traits() + .chain( + tcx.supertrait_def_ids(src_principal.def_id()) + .filter(|def_id| tcx.trait_is_auto(*def_id)), + ) + .collect(); + + let added = dst_tty + .auto_traits() + .filter(|trait_did| !src_auto.contains(trait_did)) + .collect::<Vec<_>>(); + + if !added.is_empty() { + tcx.emit_node_span_lint( + lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT, + self.expr.hir_id, + self.span, + errors::PtrCastAddAutoToObject { + traits_len: added.len(), + traits: { + let mut traits: Vec<_> = added + .into_iter() + .map(|trait_did| tcx.def_path_str(trait_did)) + .collect(); + + traits.sort(); + traits.into() + }, + }, + ) + } + + Ok(CastKind::PtrPtrCast) + } + + // dyn Auto -> dyn Auto'? ok. + (None, None) => Ok(CastKind::PtrPtrCast), + + // dyn Trait -> dyn Auto? should be ok, but we used to not allow it. + // FIXME: allow this + (Some(_), None) => Err(CastError::DifferingKinds), + + // dyn Auto -> dyn Trait? not ok. + (None, Some(_)) => Err(CastError::DifferingKinds), + } + } + + // fat -> fat? metadata kinds must match + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast), + + (_, _) => Err(CastError::DifferingKinds), } } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index ac7ed3e26f9..08de871f6fa 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -17,9 +17,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::error_reporting::traits::ArgKind; +use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::ArgKind; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_type_ir::ClosureKind; use std::iter; use std::ops::ControlFlow; @@ -424,9 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id), - hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => { - self.tcx.async_fn_trait_kind_from_def_id(trait_def_id) - } + hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self + .tcx + .async_fn_trait_kind_from_def_id(trait_def_id) + .or_else(|| self.tcx.fn_trait_kind_from_def_id(trait_def_id)), _ => None, }; @@ -470,14 +471,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // for closures and async closures, respectively. match closure_kind { hir::ClosureKind::Closure - if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => {} + if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection(cause_span, projection) + } + hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) + if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection(cause_span, projection) + } + // It's possible we've passed the closure to a (somewhat out-of-fashion) + // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still + // guide inference here, since it's beneficial for the user. hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) - if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => {} - _ => return None, + if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection_and_future_bound(cause_span, projection) + } + _ => None, } + } + + /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args + /// and return type to infer a [`ty::PolyFnSig`] for the closure. + fn extract_sig_from_projection( + &self, + cause_span: Option<Span>, + projection: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<ExpectedSig<'tcx>> { + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); - let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); debug!(?arg_param_ty); let ty::Tuple(input_tys) = *arg_param_ty.kind() else { @@ -486,7 +510,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Since this is a return parameter type it is safe to unwrap. let ret_param_ty = projection.skip_binder().term.expect_type(); - 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( @@ -500,6 +523,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ExpectedSig { cause_span, sig }) } + /// When an async closure is passed to a function that has a "two-part" `Fn` + /// and `Future` trait bound, like: + /// + /// ```rust + /// use std::future::Future; + /// + /// fn not_exactly_an_async_closure<F, Fut>(_f: F) + /// where + /// F: FnOnce(String, u32) -> Fut, + /// Fut: Future<Output = i32>, + /// {} + /// ``` + /// + /// The we want to be able to extract the signature to guide inference in the async + /// closure. We will have two projection predicates registered in this case. First, + /// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is + /// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>` + /// projection. + fn extract_sig_from_projection_and_future_bound( + &self, + cause_span: Option<Span>, + projection: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<ExpectedSig<'tcx>> { + let projection = self.resolve_vars_if_possible(projection); + + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); + + let ty::Tuple(input_tys) = *arg_param_ty.kind() else { + return None; + }; + + // If the return type is a type variable, look for bounds on it. + // We could theoretically support other kinds of return types here, + // but none of them would be useful, since async closures return + // concrete anonymous future types, and their futures are not coerced + // into any other type within the body of the async closure. + let ty::Infer(ty::TyVar(return_vid)) = *projection.skip_binder().term.expect_type().kind() + else { + return None; + }; + + // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. + for bound in self.obligations_for_self_ty(return_vid) { + if let Some(ret_projection) = bound.predicate.as_projection_clause() + && let Some(ret_projection) = ret_projection.no_bound_vars() + && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) + { + let sig = projection.rebind(self.tcx.mk_fn_sig( + input_tys, + ret_projection.term.expect_type(), + false, + hir::Safety::Safe, + Abi::Rust, + )); + + return Some(ExpectedSig { cause_span, sig }); + } + } + + None + } + fn sig_of_closure( &self, expr_def_id: LocalDefId, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0551b9bc1f0..5e2a68e1f02 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -58,9 +58,9 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt, @@ -1752,10 +1752,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx - .item_super_predicates(rpit_def_id) - .instantiate_identity_iter() - .filter_map(|clause| { + fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( + |clause| { let predicate = clause .kind() .map_bound(|clause| match clause { @@ -1776,7 +1774,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.param_env, predicate, )) - }), + }, + ), ); ocx.select_where_possible().is_empty() }) @@ -1797,16 +1796,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }); } - fn report_return_mismatched_types<'a>( + fn report_return_mismatched_types<'infcx>( &self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, ty_err: TypeError<'tcx>, - fcx: &FnCtxt<'a, 'tcx>, + fcx: &'infcx FnCtxt<'_, 'tcx>, block_or_return_id: hir::HirId, expression: Option<&'tcx hir::Expr<'tcx>>, - ) -> Diag<'a> { + ) -> Diag<'infcx> { let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index f9720c9c307..4f1c2fdd922 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -172,21 +173,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn demand_suptype_diag( - &self, + &'a self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'a>> { self.demand_suptype_with_origin(&self.misc(sp), expected, actual) } #[instrument(skip(self), level = "debug")] pub fn demand_suptype_with_origin( - &self, + &'a self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'a>> { self.at(cause, self.param_env) .sup(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) @@ -200,20 +201,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn demand_eqtype_diag( - &self, + &'a self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'a>> { self.demand_eqtype_with_origin(&self.misc(sp), expected, actual) } pub fn demand_eqtype_with_origin( - &self, + &'a self, cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'a>> { self.at(cause, self.param_env) .eq(DefineOpaqueTypes::Yes, expected, actual) .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) @@ -247,13 +248,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// will be permitted if the diverges flag is currently "always". #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( - &self, + &'a self, mut expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, - ) -> Result<Ty<'tcx>, Diag<'tcx>> { + ) -> Result<Ty<'tcx>, Diag<'a>> { let expected = self.resolve_vars_with_obligations(expected); let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 98add86252c..9a38d6d4a71 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan, - SubdiagMessageOp, Subdiagnostic, + codes::*, Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg, + MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -253,6 +253,13 @@ pub struct LossyProvenanceInt2Ptr<'tcx> { pub sugg: LossyProvenanceInt2PtrSuggestion, } +#[derive(LintDiagnostic)] +#[diag(hir_typeck_ptr_cast_add_auto_to_object)] +pub struct PtrCastAddAutoToObject { + pub traits_len: usize, + pub traits: DiagSymbolList<String>, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")] pub struct LossyProvenanceInt2PtrSuggestion { @@ -501,6 +508,7 @@ pub enum SuggestBoxing { #[suggestion( hir_typeck_suggest_ptr_null_mut, applicability = "maybe-incorrect", + style = "verbose", code = "core::ptr::null_mut()" )] pub struct SuggestPtrNullMut { @@ -689,3 +697,30 @@ pub struct ReplaceWithName { pub span: Span, pub name: String, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_cast_thin_pointer_to_fat_pointer, code = E0607)] +pub(crate) struct CastThinPointerToFatPointer<'tcx> { + #[primary_span] + pub span: Span, + pub expr_ty: Ty<'tcx>, + pub cast_ty: String, + #[note(hir_typeck_teach_help)] + pub(crate) teach: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_pass_to_variadic_function, code = E0617)] +pub(crate) struct PassToVariadicFunction<'tcx, 'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'tcx>, + pub cast_ty: &'a str, + #[suggestion(code = "{replace}", applicability = "machine-applicable")] + pub sugg_span: Option<Span>, + pub replace: String, + #[help] + pub help: Option<()>, + #[note(hir_typeck_teach_help)] + pub(crate) teach: Option<()>, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f4e1e461953..432489330a6 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -53,9 +53,9 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -638,7 +638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Set expectation to error in that case and set tainted // by error (#114529) let coerce_to = opt_coerce_to.unwrap_or_else(|| { - let guar = tcx.dcx().span_delayed_bug( + let guar = self.dcx().span_delayed_bug( expr.span, "illegal break with value found but no error reported", ); @@ -708,7 +708,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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.dcx().has_errors().is_some()); + assert!(expr_opt.is_none() || self.tainted_by_errors().is_some()); } // If we encountered a `break`, then (no surprise) it may be possible to break from the @@ -1716,7 +1716,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { error_happened = true; let guar = if let Some(prev_span) = seen_fields.get(&ident) { - tcx.dcx().emit_err(FieldMultiplySpecifiedInInitializer { + self.dcx().emit_err(FieldMultiplySpecifiedInInitializer { span: field.ident.span, prev_span: *prev_span, ident, @@ -1757,7 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if adt_kind == AdtKind::Union { if hir_fields.len() != 1 { struct_span_code_err!( - tcx.dcx(), + self.dcx(), span, E0784, "union expressions should have exactly one field", @@ -2551,10 +2551,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *base_ty.peel_refs().kind() { ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len); + self.maybe_suggest_array_indexing(&mut err, base, ident, len); } ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, ident); + self.suggest_first_deref_field(&mut err, base, ident); } ty::Param(param_ty) => { err.span_label(ident.span, "unknown field"); @@ -2721,7 +2721,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn maybe_suggest_array_indexing( &self, err: &mut Diag<'_>, - expr: &hir::Expr<'_>, base: &hir::Expr<'_>, field: Ident, len: ty::Const<'tcx>, @@ -2729,32 +2728,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(field.span, "unknown field"); if let (Some(len), Ok(user_index)) = (len.try_eval_target_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); + err.multipart_suggestion( + help, + vec![ + (base.span.between(field.span), "[".to_string()), + (field.span.shrink_to_hi(), "]".to_string()), + ], + applicability, + ); } } - fn suggest_first_deref_field( - &self, - err: &mut Diag<'_>, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - ) { + fn suggest_first_deref_field(&self, err: &mut Diag<'_>, base: &hir::Expr<'_>, field: Ident) { err.span_label(field.span, "unknown field"); - 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); - } + let val = if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) + && base.len() < 20 + { + format!("`{base}`") + } else { + "the value".to_string() + }; + err.multipart_suggestion( + format!("{val} is a raw pointer; try dereferencing it"), + vec![ + (base.span.shrink_to_lo(), "(*".to_string()), + (base.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); } fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'_> { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 0ba4bd090f5..193dbbbcdf4 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -170,7 +170,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> { } fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error { - self.tcx.dcx().span_delayed_bug(span, msg.to_string()) + self.dcx().span_delayed_bug(span, msg.to_string()) } fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error> { @@ -734,7 +734,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // struct; however, when EUV is run during typeck, it // may not. This will generate an error earlier in typeck, // so we can just ignore it. - if self.cx.tcx().dcx().has_errors().is_none() { + if self.cx.tainted_by_errors().is_ok() { span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 061afd03062..ea9567f4e3d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -37,7 +37,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::abi::FieldIdx; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt, }; @@ -1139,7 +1139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parameter's value explicitly, so we have to do some error- // checking here. let arg_count = - check_generic_arg_count_for_call(tcx, def_id, generics, seg, IsMethodCall::No); + check_generic_arg_count_for_call(self, def_id, generics, seg, IsMethodCall::No); if let ExplicitLateBound::Yes = arg_count.explicit_late_bound { explicit_late_bound = ExplicitLateBound::Yes; @@ -1182,7 +1182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { name: self.tcx.item_name(def.did()).to_ident_string(), }); if ty.raw.has_param() { - let guar = self.tcx.dcx().emit_err(errors::SelfCtorFromOuterItem { + let guar = self.dcx().emit_err(errors::SelfCtorFromOuterItem { span: path_span, impl_span: tcx.def_span(impl_def_id), sugg, @@ -1207,7 +1207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check the visibility of the ctor. let vis = tcx.visibility(ctor_def_id); if !vis.is_accessible_from(tcx.parent_module(hir_id).to_def_id(), tcx) { - tcx.dcx() + self.dcx() .emit_err(CtorIsPrivate { span, def: tcx.def_path_str(adt_def.did()) }); } let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); @@ -1216,7 +1216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (new_res, Some(user_args.args)) } _ => { - let mut err = tcx.dcx().struct_span_err( + let mut err = self.dcx().struct_span_err( span, "the `Self` constructor can only be used with tuple or unit structs", ); @@ -1304,7 +1304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.ty_infer(Some(param), inf.span).into() } ( - &GenericParamDefKind::Const { has_default, is_host_effect }, + &GenericParamDefKind::Const { has_default, is_host_effect, .. }, GenericArg::Infer(inf), ) => { if has_default && is_host_effect { @@ -1346,7 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.var_for_def(self.span, param) } } - GenericParamDefKind::Const { has_default, is_host_effect } => { + GenericParamDefKind::Const { has_default, is_host_effect, .. } => { if has_default { // N.B. this is a bit of a hack. `infer_args` is passed depending on // whether the user has provided generic args. E.g. for `Vec::new` @@ -1375,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let args_raw = self_ctor_args.unwrap_or_else(|| { lower_generic_args( - tcx, + self, def_id, &[], has_self, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1138642c56d..ab0f356ce91 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -28,7 +28,6 @@ use rustc_hir::{ExprKind, HirId, Node, QPath}; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; -use rustc_hir_analysis::structured_errors::StructuredDiag; use rustc_index::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::TypeTrace; @@ -40,6 +39,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; @@ -238,7 +238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 let guar = struct_span_code_err!( - tcx.dcx(), + self.dcx(), call_span, E0059, "cannot use call notation; the first type parameter \ @@ -405,9 +405,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, cast_ty: &str, ) { - use rustc_hir_analysis::structured_errors::MissingCastForVariadicArg; + let (sugg_span, replace, help) = + if let Ok(snippet) = sess.source_map().span_to_snippet(span) { + (Some(span), format!("{snippet} as {cast_ty}"), None) + } else { + (None, "".to_string(), Some(())) + }; - MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); + sess.dcx().emit_err(errors::PassToVariadicFunction { + span, + ty, + cast_ty, + help, + replace, + sugg_span, + teach: sess.teach(E0617).then_some(()), + }); } // There are a few types which get autopromoted when passed via varargs @@ -453,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|vars| self.resolve_vars_if_possible(vars)), ); - self.set_tainted_by_errors(self.report_arg_errors( + self.report_arg_errors( compatibility_diagonal, formal_and_expected_inputs, provided_args, @@ -462,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_def_id, call_span, call_expr, - )); + ); } } @@ -788,7 +801,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); } else { - err = tcx.dcx().struct_span_err( + err = self.dcx().struct_span_err( full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", @@ -848,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span_bug!(error_span, "expected errors from argument matrix"); } else { let mut err = - tcx.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); + self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); suggest_confusable(&mut err); return err.emit(); } @@ -951,16 +964,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return err.emit(); } + // Special case, we found an extra argument is provided, which is very common in practice. + // but there is a obviously better removing suggestion compared to the current one, + // try to find the argument with Error type, if we removed it all the types will become good, + // then we will replace the current suggestion. + if let [Error::Extra(provided_idx)] = &errors[..] { + let remove_idx_is_perfect = |idx: usize| -> bool { + let removed_arg_tys = provided_arg_tys + .iter() + .enumerate() + .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) + .collect::<IndexVec<ProvidedIdx, _>>(); + std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( + |((expected_ty, _), (provided_ty, _))| { + !provided_ty.references_error() + && self.can_coerce(*provided_ty, *expected_ty) + }, + ) + }; + + if !remove_idx_is_perfect(provided_idx.as_usize()) { + if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) { + errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; + } + } + } + let mut err = if formal_and_expected_inputs.len() == provided_args.len() { struct_span_code_err!( - tcx.dcx(), + self.dcx(), full_call_span, E0308, "arguments to this {} are incorrect", call_name, ) } else { - tcx.dcx() + self.dcx() .struct_span_err( full_call_span, format!( @@ -1358,7 +1397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(format!("provide the argument{}", if plural { "s" } else { "" })) } SuggestionText::Remove(plural) => { - err.multipart_suggestion( + err.multipart_suggestion_verbose( format!("remove the extra argument{}", if plural { "s" } else { "" }), suggestions, Applicability::HasPlaceholders, @@ -1424,7 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, provided_ty: Ty<'tcx>, arg: &hir::Expr<'tcx>, - err: &mut Diag<'tcx>, + err: &mut Diag<'_>, ) { if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind() && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind() @@ -1652,7 +1691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - // Hide the outer diverging and `has_errors` flags. + // Hide the outer diverging flags. let old_diverges = self.diverges.replace(Diverges::Maybe); match stmt.kind { @@ -2042,7 +2081,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if block_num > 1 && found_semi { err.span_suggestion_verbose( - span.shrink_to_lo(), + // use the span of the *whole* expr + self.tcx.hir().span(binding_hir_id).shrink_to_lo(), "you might have meant to return this to infer its type parameters", "return ", Applicability::MaybeIncorrect, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index fab7eb7495c..90dd5f73586 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -104,8 +104,6 @@ struct NestedObligationsForSelfTy<'a, 'tcx> { } impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { - type Result = (); - fn span(&self) -> Span { self.root_cause.span } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index c0d60477967..b5796fbd48a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,7 +5,7 @@ mod checks; mod inspect_obligations; mod suggestions; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; +use rustc_errors::DiagCtxtHandle; use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; @@ -144,8 +144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { - self.infcx.dcx() + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'a> { + self.root_ctxt.infcx.dcx() } pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { @@ -217,6 +217,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { self.tcx } + fn dcx(&self) -> DiagCtxtHandle<'_> { + self.root_ctxt.dcx() + } + fn item_def_id(&self) -> LocalDefId { self.body_id } @@ -274,6 +278,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { } }), ), + effects_min_tys: ty::List::empty(), } } @@ -337,10 +342,6 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { Some(&self.infcx) } - fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { - self.infcx.set_tainted_by_errors(e) - } - fn lower_fn_sig( &self, decl: &rustc_hir::FnDecl<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 8d380caf916..5f897c74482 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -32,10 +32,10 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; +use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; -use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -2582,7 +2582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _)) - if self.can_sub(self.param_env, checked, expected) => + if self.can_eq(self.param_env, checked, expected) => { let make_sugg = |start: Span, end: BytePos| { // skip `(` for tuples such as `(c) = (&123)`. diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 5eafc60a04e..0389c06c312 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Note: this path is currently not reached in any test, so any // example that triggers this would be worth minimizing and // converting into a test. - tcx.dcx().span_bug(span, "argument to transmute has inference variables"); + self.dcx().span_bug(span, "argument to transmute has inference variables"); } // Transmutes that are only changing lifetimes are always ok. if from == to { @@ -76,7 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) { - struct_span_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type") + struct_span_code_err!(self.dcx(), span, E0591, "can't transmute zero-sized type") .with_note(format!("source type: {from}")) .with_note(format!("target type: {to}")) .with_help("cast with `as` to a pointer instead") @@ -116,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), span, E0512, "cannot transmute between types of different sizes, \ diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 120e3239d1f..e574fde14fb 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -354,7 +354,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let generics = self.tcx.generics_of(pick.item.def_id); let arg_count_correct = check_generic_arg_count_for_call( - self.tcx, + self.fcx, pick.item.def_id, generics, seg, @@ -425,7 +425,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } let args = lower_generic_args( - self.tcx, + self.fcx, pick.item.def_id, parent_args, false, @@ -510,9 +510,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .report_mismatched_types(&cause, method_self_ty, self_ty, terr) .emit(); } else { - error!("{self_ty} was a subtype of {method_self_ty} but now is not?"); - // This must already have errored elsewhere. - self.dcx().has_errors().unwrap(); + // This has/will have errored in wfcheck, which we cannot depend on from here, as typeck on functions + // may run before wfcheck if the function is used in const eval. + self.dcx().span_delayed_bug( + cause.span(), + format!("{self_ty} was a subtype of {method_self_ty} but now is not?"), + ); } } } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e1223307b53..dc1b888374c 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -356,6 +356,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { let (obligation, args) = self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types); + // FIXME(effects) find a better way to do this + // Operators don't have generic methods, but making them `#[const_trait]` gives them + // `const host: bool`. + let args = if self.tcx.is_const_trait(trait_def_id) { + self.tcx.mk_args_from_iter( + args.iter() + .chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]), + ) + } else { + args + }; self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args) } @@ -393,6 +404,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); let mut obligations = vec![]; + // FIXME(effects): revisit when binops get `#[const_trait]` + // Instantiate late-bound regions and instantiate the trait // parameters into the method type to get the actual method type. // diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9747a91ccbf..6a7af5510e0 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -33,6 +33,7 @@ use rustc_span::edit_distance::{ }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -857,7 +858,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let args = self.fresh_args_for_item(self.span, method.def_id); let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args); let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); - self.can_sub(self.param_env, fty.output(), expected) + self.can_eq(self.param_env, fty.output(), expected) }), _ => false, } @@ -1279,6 +1280,7 @@ impl<'tcx> Pick<'tcx> { trait_item_def_id: _, fn_has_self_parameter: _, opt_rpitit_info: _, + is_effects_desugaring: _, }, kind: _, import_ids: _, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a385bc70e35..425289ce3c5 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -35,9 +35,9 @@ use rustc_span::def_id::DefIdSet; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span}; use rustc_span::{Symbol, DUMMY_SP}; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ supertraits, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, @@ -499,7 +499,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // If the shadowed binding has an an itializer expression, + // If the shadowed binding has an itializer expression, // use the initializer expression'ty to try to find the method again. // For example like: `let mut x = Vec::new();`, // `Vec::new()` is the itializer expression. @@ -705,7 +705,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { - let mut err = tcx.dcx().create_err(NoAssociatedItem { + let mut err = self.dcx().create_err(NoAssociatedItem { span, item_kind, item_name, @@ -968,7 +968,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Make sure that, if any traits other than the found ones were involved, - // we don't don't report an unimplemented trait. + // we don't report an unimplemented trait. // We don't want to say that `iter::Cloned` is not an iterator, just // because of some non-Clone item being iterated over. for (predicate, _parent_pred, _cause) in unsatisfied_predicates { @@ -1194,7 +1194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: item_span, .. })) => { - tcx.dcx().span_delayed_bug( + self.dcx().span_delayed_bug( *item_span, "auto trait is invoked with no method error, but no error reported?", ); @@ -2129,7 +2129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { - DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey } + DeepRejectCtxt::new(self.tcx, TreatParams::ForLookup) .types_may_unify(*rcvr_ty, impl_ty) }) .map_or(impl_ty, |(ty, _)| ty) @@ -2361,7 +2361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); - return Err(tcx.dcx().emit_err(errors::MissingParenthesesInRange { + return Err(self.dcx().emit_err(errors::MissingParenthesesInRange { span, ty_str: ty_str.to_string(), method_name: item_name.as_str().to_string(), @@ -2420,7 +2420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let SelfSource::MethodCall(expr) = source { let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), span, E0689, "can't call {} `{}` on ambiguous numeric type `{}`", diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 5a11cb7096f..d59b8276d3a 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -18,8 +18,8 @@ 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::error_reporting::traits::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt}; use rustc_type_ir::TyKind::*; @@ -838,8 +838,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ) = ex.kind { - err.span_suggestion( - ex.span, + let span = if let hir::Node::Expr(parent) = + self.tcx.parent_hir_node(ex.hir_id) + && let hir::ExprKind::Cast(..) = parent.kind + { + // `-1 as usize` -> `usize::MAX` + parent.span + } else { + ex.span + }; + err.span_suggestion_verbose( + span, format!( "you may have meant the maximum value of `{actual}`", ), diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index aaf3d3ec34d..be526e1c26c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -19,6 +19,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use ty::VariantDef; @@ -89,7 +90,7 @@ struct PatInfo<'tcx, 'a> { current_depth: u32, } -impl<'tcx> FnCtxt<'_, 'tcx> { +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { let code = ObligationCauseCode::Pattern { span: ti.span, @@ -100,12 +101,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } fn demand_eqtype_pat_diag( - &self, + &'a self, cause_span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, ti: &TopInfo<'tcx>, - ) -> Result<(), Diag<'tcx>> { + ) -> Result<(), Diag<'a>> { self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) .map_err(|mut diag| { if let Some(expr) = ti.origin_expr { @@ -328,8 +329,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adjust_mode: AdjustMode, max_ref_mutbl: MutblCap, ) -> (Ty<'tcx>, ByRef, MutblCap) { - if let ByRef::Yes(Mutability::Mut) = def_br { - debug_assert!(max_ref_mutbl == MutblCap::Mut); + #[cfg(debug_assertions)] + if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut { + span_bug!(pat.span, "Pattern mutability cap violated!"); } match adjust_mode { AdjustMode::Pass => (expected, def_br, max_ref_mutbl), @@ -437,7 +439,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); } - if self.tcx.features().ref_pat_eat_one_layer_2024 { + let features = self.tcx.features(); + if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural { def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { max_ref_mutbl = MutblCap::Not; @@ -669,7 +672,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => { - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + if pat.span.at_least_rust_2024() + && (self.tcx.features().ref_pat_eat_one_layer_2024 + || self.tcx.features().ref_pat_eat_one_layer_2024_structural) + { if !self.tcx.features().mut_ref { feature_err( &self.tcx.sess, @@ -698,7 +704,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl { let mut err = struct_span_code_err!( - self.tcx.dcx(), + self.dcx(), ident.span, E0596, "cannot borrow as mutable inside an `&` pattern" @@ -1010,7 +1016,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (res, opt_ty, segments) = path_resolution; match res { Res::Err => { - let e = tcx.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); + let e = + self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); return Ty::new_error(tcx, e); } @@ -1191,7 +1198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); if res == Res::Err { - let e = tcx.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); + let e = self.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); on_error(e); return Ty::new_error(tcx, e); @@ -1207,7 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let variant = match res { Res::Err => { - tcx.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); + self.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { let e = report_unexpected_res(res); @@ -1549,10 +1556,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Report an error if an incorrect number of fields was specified. if adt.is_union() { if fields.len() != 1 { - tcx.dcx().emit_err(errors::UnionPatMultipleFields { span: pat.span }); + self.dcx().emit_err(errors::UnionPatMultipleFields { span: pat.span }); } if has_rest_pat { - tcx.dcx().emit_err(errors::UnionPatDotDot { span: pat.span }); + self.dcx().emit_err(errors::UnionPatDotDot { span: pat.span }); } } else if !unmentioned_fields.is_empty() { let accessible_unmentioned_fields: Vec<_> = unmentioned_fields @@ -1690,7 +1697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, variant: &ty::VariantDef, args: ty::GenericArgsRef<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let tcx = self.tcx; let (field_names, t, plural) = if let [field] = inexistent_fields { (format!("a field named `{}`", field.ident), "this", "") @@ -1710,7 +1717,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::<Vec<_>>(); let mut err = struct_span_code_err!( - tcx.dcx(), + self.dcx(), spans, E0026, "{} `{}` does not have {}", @@ -1881,7 +1888,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'_>, fields: &'tcx [hir::PatField<'tcx>], - ) -> Diag<'tcx> { + ) -> Diag<'a> { let mut err = self .dcx() .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); @@ -1973,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unmentioned_fields: &[(&ty::FieldDef, Ident)], have_inaccessible_fields: bool, fields: &'tcx [hir::PatField<'tcx>], - ) -> Diag<'tcx> { + ) -> Diag<'a> { let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" }; let field_names = if let [(_, field)] = unmentioned_fields { format!("field `{field}`{inaccessible}") @@ -2122,7 +2129,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut expected: Ty<'tcx>, mut pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024; + let tcx = self.tcx; + let features = tcx.features(); + let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024; + let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural; + + let no_ref_mut_behind_and = + ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural; let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and; let pat_prefix_span = @@ -2137,32 +2150,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info.max_ref_mutbl = MutblCap::Mut; } + expected = self.try_structurally_resolve_type(pat.span, expected); if new_match_ergonomics { if let ByRef::Yes(inh_mut) = pat_info.binding_mode { - // ref pattern consumes inherited reference - - if pat_mutbl > inh_mut { - // Tried to match inherited `ref` with `&mut`, which is an error - let err_msg = "cannot match inherited `&` with `&mut` pattern"; - let err = if let Some(span) = pat_prefix_span { - let mut err = self.dcx().struct_span_err(span, err_msg); - err.span_suggestion_verbose( - span, - "replace this `&mut` pattern with `&`", - "&", - Applicability::MachineApplicable, - ); - err + if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() { + // Don't attempt to consume inherited reference + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl); + } else { + // ref pattern attempts to consume inherited reference + if pat_mutbl > inh_mut { + // Tried to match inherited `ref` with `&mut` + if !ref_pat_eat_one_layer_2024_structural { + let err_msg = "mismatched types"; + let err = if let Some(span) = pat_prefix_span { + let mut err = self.dcx().struct_span_err(span, err_msg); + err.code(E0308); + err.note("cannot match inherited `&` with `&mut` pattern"); + err.span_suggestion_verbose( + span, + "replace this `&mut` pattern with `&`", + "&", + Applicability::MachineApplicable, + ); + err + } else { + self.dcx().struct_span_err(pat.span, err_msg) + }; + err.emit(); + + pat_info.binding_mode = ByRef::No; + self.typeck_results + .borrow_mut() + .skipped_ref_pats_mut() + .insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; + } } else { - self.dcx().struct_span_err(pat.span, err_msg) - }; - err.emit(); + pat_info.binding_mode = ByRef::No; + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; + } } - - pat_info.binding_mode = ByRef::No; - self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - return expected; } } else { // Reset binding mode on old editions @@ -2177,8 +2207,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let tcx = self.tcx; - expected = self.try_structurally_resolve_type(pat.span, expected); let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns @@ -2190,7 +2218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("check_pat_ref: expected={:?}", expected); match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) - if (new_match_ergonomics && r_mutbl >= pat_mutbl) + if (no_ref_mut_behind_and && r_mutbl >= pat_mutbl) || r_mutbl == pat_mutbl => { if no_ref_mut_behind_and && r_mutbl == Mutability::Not { @@ -2498,7 +2526,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) && let Some(span) = ti.span && let Some(_) = ti.origin_expr - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let resolved_ty = self.resolve_vars_if_possible(ti.expected); let (is_slice_or_array_or_vector, resolved_ty) = @@ -2509,10 +2536,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => { // Slicing won't work here, but `.as_deref()` might (issue #91328). - err.span_suggestion( - span, + err.span_suggestion_verbose( + span.shrink_to_hi(), "consider using `as_deref` here", - format!("{snippet}.as_deref()"), + ".as_deref()", Applicability::MaybeIncorrect, ); } @@ -2521,10 +2548,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_top_level = current_depth <= 1; if is_slice_or_array_or_vector && is_top_level { - err.span_suggestion( - span, + err.span_suggestion_verbose( + span.shrink_to_hi(), "consider slicing here", - format!("{snippet}[..]"), + "[..]", Applicability::MachineApplicable, ); } diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index b6e9000ef95..c99e8a7fe8e 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -18,7 +18,7 @@ use rustc_trait_selection::traits::{ use std::cell::RefCell; use std::ops::Deref; -// Data shared between a "typeck root" and its nested bodies, +/// Data shared between a "typeck root" and its nested bodies, /// e.g. closures defined within the function. For example: /// ```ignore (illustrative) /// fn foo() { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index aea0114167e..e800c1a97d9 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::TypeSuperFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::solve; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use std::mem; @@ -219,28 +219,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = self.typeck_results.expr_ty_adjusted_opt(base); - if base_ty.is_none() { - // When encountering `return [0][0]` outside of a `fn` body we can encounter a base - // that isn't in the type table. We assume more relevant errors have already been - // emitted. (#64638) - assert!(self.tcx().dcx().has_errors().is_some(), "bad base: `{base:?}`"); - } - if let Some(base_ty) = base_ty - && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() - { - let index_ty = - self.typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { - // When encountering `return [0][0]` outside of a `fn` body we would attempt - // to access an nonexistent index. We assume that more relevant errors will - // already have been emitted, so we only gate on this with an ICE if no - // error has been emitted. (#64638) - Ty::new_error_with_message( - self.fcx.tcx, - e.span, - format!("bad index {index:?} for base: `{base:?}`"), - ) - }); + let base_ty = self.typeck_results.expr_ty_adjusted(base); + if let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() { + let index_ty = self.typeck_results.expr_ty_adjusted(index); if self.is_builtin_index(e, base_ty_inner, index_ty) { // Remove the method call record self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index db6b250467e..b775ae1f5e9 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,10 +1,7 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] -#![cfg_attr( - feature = "nightly", - feature(extend_one, min_specialization, new_uninit, step_trait, test) -)] #![cfg_attr(feature = "nightly", allow(internal_features))] +#![cfg_attr(feature = "nightly", feature(extend_one, new_uninit, step_trait, test))] // tidy-alphabetical-end pub mod bit_set; diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 88298150a79..b866c8b8433 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -208,6 +208,11 @@ impl<I: Idx, T> IndexVec<I, Option<T>> { pub fn remove(&mut self, index: I) -> Option<T> { self.get_mut(index)?.take() } + + #[inline] + pub fn contains(&self, index: I) -> bool { + self.get(index).and_then(Option::as_ref).is_some() + } } impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> { diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs index 3e55dd82a6e..2b444932f85 100644 --- a/compiler/rustc_index_macros/src/lib.rs +++ b/compiler/rustc_index_macros/src/lib.rs @@ -34,10 +34,7 @@ mod newtype; /// optimizations. The default max value is 0xFFFF_FF00. /// - `#[gate_rustc_only]`: makes parts of the generated code nightly-only. #[proc_macro] -#[cfg_attr( - feature = "nightly", - allow_internal_unstable(step_trait, rustc_attrs, trusted_step, min_specialization) -)] +#[cfg_attr(feature = "nightly", allow_internal_unstable(step_trait, rustc_attrs, trusted_step))] pub fn newtype_index(input: TokenStream) -> TokenStream { newtype::newtype(input) } diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 41863f7b15f..1ac2c44e9dc 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -139,10 +139,6 @@ impl Parse for Newtype { Self::index(start).checked_sub(u).map(Self::from_usize) } } - - // Safety: The implementation of `Step` upholds all invariants. - #gate_rustc_only - unsafe impl ::std::iter::TrustedStep for #name {} } } else { quote! {} diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index c1565a7d40f..1f616710200 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -16,8 +16,10 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } +rustc_next_trait_solver = { path = "../rustc_next_trait_solver" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } +rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs new file mode 100644 index 00000000000..f35a8162d96 --- /dev/null +++ b/compiler/rustc_infer/src/infer/context.rs @@ -0,0 +1,176 @@ +///! Definition of `InferCtxtLike` from the librarified type layer. +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::traits::solve::{Goal, NoSolution, SolverMode}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::DUMMY_SP; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::InferCtxtLike; + +use super::{BoundRegionConversionTime, InferCtxt, SubregionOrigin}; + +impl<'tcx> InferCtxtLike for InferCtxt<'tcx> { + type Interner = TyCtxt<'tcx>; + + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn solver_mode(&self) -> ty::solve::SolverMode { + match self.intercrate { + true => SolverMode::Coherence, + false => SolverMode::Normal, + } + } + + fn universe(&self) -> ty::UniverseIndex { + self.universe() + } + + fn create_next_universe(&self) -> ty::UniverseIndex { + self.create_next_universe() + } + + fn universe_of_ty(&self, vid: ty::TyVid) -> Option<ty::UniverseIndex> { + match self.probe_ty_var(vid) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> { + match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex> { + match self.probe_const_var(ct) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { + self.root_var(var) + } + + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { + self.root_const_var(var) + } + + fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { + match self.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.tcx, self.root_var(vid)), + } + } + + fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { + self.opportunistic_resolve_int_var(vid) + } + + fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { + self.opportunistic_resolve_float_var(vid) + } + + fn opportunistic_resolve_ct_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> { + match self.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => ty::Const::new_var(self.tcx, self.root_const_var(vid)), + } + } + + fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> { + match self.probe_effect_var(vid) { + Some(ct) => ct, + None => { + ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(self.root_effect_var(vid))) + } + } + } + + fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { + self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid) + } + + fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { + self.defining_opaque_types() + } + + fn next_ty_infer(&self) -> Ty<'tcx> { + self.next_ty_var(DUMMY_SP) + } + + fn next_const_infer(&self) -> ty::Const<'tcx> { + self.next_const_var(DUMMY_SP) + } + + fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { + self.fresh_args_for_item(DUMMY_SP, def_id) + } + + fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>( + &self, + value: ty::Binder<'tcx, T>, + ) -> T { + self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + value, + ) + } + + fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>( + &self, + value: ty::Binder<'tcx, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, f) + } + + fn relate<T: Relate<TyCtxt<'tcx>>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs) + } + + fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + self.at(&ObligationCause::dummy(), param_env) + .eq_structurally_relating_aliases_no_trace(lhs, rhs) + } + + fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.shallow_resolve(ty) + } + + fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + self.resolve_vars_if_possible(value) + } + + fn probe<T>(&self, probe: impl FnOnce() -> T) -> T { + self.probe(|_| probe()) + } + + fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) { + self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup) + } + + fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) { + self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy()); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 227691d0994..bb1285ee813 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -45,13 +45,10 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use super::lexical_region_resolve::RegionResolutionError; -use super::region_constraints::GenericKind; -use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; +use super::{InferCtxt, TypeTrace, ValuePairs}; -use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; +use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; -use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -61,38 +58,36 @@ use crate::traits::{ use crate::infer::relate::{self, RelateResult, TypeRelation}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, - DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart, + pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart, }; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ParamName}; +use rustc_hir::{self as hir}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; -use rustc_middle::ty::Upcast; use rustc_middle::ty::{ - self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, }; -use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; +use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::borrow::Cow; use std::ops::{ControlFlow, Deref}; use std::path::PathBuf; use std::{cmp, fmt, iter}; -mod note; mod note_and_explain; mod suggest; pub(crate) mod need_type_info; pub mod sub_relations; pub use need_type_info::TypeAnnotationNeeded; +pub mod region; pub mod nice_region_error; @@ -139,7 +134,7 @@ pub struct TypeErrCtxt<'a, 'tcx> { } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { + pub fn dcx(&self) -> DiagCtxtHandle<'a> { self.infcx.dcx() } @@ -159,244 +154,6 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { } } -pub(super) fn note_and_explain_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, - alt_span: Option<Span>, -) { - let (description, span) = match *region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { - msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) - } - - ty::ReError(_) => return, - - // FIXME(#125431): `ReVar` shouldn't reach here. - ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), - - ty::ReBound(..) | ty::ReErased => { - bug!("unexpected region for note_and_explain_region: {:?}", region); - } - }; - - emit_msg_span(err, prefix, description, span, suffix); -} - -fn explain_free_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, -) { - let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); - - label_msg_span(err, prefix, description, span, suffix); -} - -fn msg_span_from_named_region<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: ty::Region<'tcx>, - alt_span: Option<Span>, -) -> (String, Option<Span>) { - match *region { - ty::ReEarlyParam(br) => { - let scope = tcx - .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) - .expect_local(); - let span = if let Some(param) = - tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) - { - param.span - } else { - tcx.def_span(scope) - }; - let text = if br.has_name() { - format!("the lifetime `{}` as defined here", br.name) - } else { - "the anonymous lifetime as defined here".to_string() - }; - (text, Some(span)) - } - ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) - { - ("the anonymous lifetime defined here".to_string(), Some(ty.span)) - } else { - match fr.bound_region { - ty::BoundRegionKind::BrNamed(_, name) => { - let span = if let Some(param) = tcx - .hir() - .get_generics(generic_param_scope) - .and_then(|generics| generics.get_named(name)) - { - param.span - } else { - tcx.def_span(generic_param_scope) - }; - let text = if name == kw::UnderscoreLifetime { - "the anonymous lifetime as defined here".to_string() - } else { - format!("the lifetime `{name}` as defined here") - }; - (text, Some(span)) - } - ty::BrAnon => ( - "the anonymous lifetime as defined here".to_string(), - Some(tcx.def_span(generic_param_scope)), - ), - _ => ( - format!("the lifetime `{region}` as defined here"), - Some(tcx.def_span(generic_param_scope)), - ), - } - } - } - ty::ReStatic => ("the static lifetime".to_owned(), alt_span), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. }, - .. - }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, - .. - }) => ("an anonymous lifetime".to_owned(), None), - _ => bug!("{:?}", region), - } -} - -fn emit_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option<Span>, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_note(span, message); - } else { - err.note(message); - } -} - -fn label_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option<Span>, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_label(span, message); - } else { - err.note(message); - } -} - -#[instrument(level = "trace", skip(tcx))] -pub fn unexpected_hidden_region_diagnostic<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - span: Span, - hidden_ty: Ty<'tcx>, - hidden_region: ty::Region<'tcx>, - opaque_ty_key: ty::OpaqueTypeKey<'tcx>, -) -> Diag<'tcx> { - let mut err = tcx.dcx().create_err(errors::OpaqueCapturesLifetime { - span, - opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), - opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), - }); - - // Explain the region we are capturing. - match *hidden_region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - // Assuming regionck succeeded (*), we ought to always be - // capturing *some* region from the fn header, and hence it - // ought to be free. So under normal circumstances, we will go - // down this path which gives a decent human readable - // explanation. - // - // (*) if not, the `tainted_by_errors` field would be set to - // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - ); - if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { - let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); - nice_region_error::suggest_new_region_bound( - tcx, - &mut err, - fn_returns, - hidden_region.to_string(), - None, - format!("captures `{hidden_region}`"), - None, - Some(reg_info.def_id), - ) - } - } - ty::RePlaceholder(_) => { - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } - ty::ReError(_) => { - err.downgrade_to_delayed_bug(); - } - _ => { - // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happen - // in a case like - // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: - // - // ``` - // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { - // if condition() { a } else { b } - // } - // ``` - // - // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. - - // We can at least report a really cryptic error for now. - note_and_explain_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - None, - ); - } - } - - err -} - impl<'tcx> InferCtxt<'tcx> { pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { let (def_id, args) = match *ty.kind() { @@ -436,194 +193,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - pub fn report_region_errors( - &self, - generic_param_scope: LocalDefId, - errors: &[RegionResolutionError<'tcx>], - ) -> ErrorGuaranteed { - assert!(!errors.is_empty()); - - if let Some(guaranteed) = self.infcx.tainted_by_errors() { - return guaranteed; - } - - debug!("report_region_errors(): {} errors to start", errors.len()); - - // try to pre-process the errors, which will group some of them - // together into a `ProcessedErrors` group: - let errors = self.process_errors(errors); - - debug!("report_region_errors: {} errors after preprocessing", errors.len()); - - let mut guar = None; - for error in errors { - debug!("report_region_errors: error = {:?}", error); - - let e = if let Some(guar) = - self.try_report_nice_region_error(generic_param_scope, &error) - { - guar - } else { - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - RegionResolutionError::ConcreteFailure(origin, sub, sup) => { - if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(generic_param_scope, origin, sub, sup) - .emit() - } else { - self.report_concrete_failure(generic_param_scope, origin, sub, sup) - .emit() - } - } - - RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self - .report_generic_bound_failure( - generic_param_scope, - origin.span(), - Some(origin), - param_ty, - sub, - ), - - RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - _, - ) => { - if sub_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sub_origin, - sub_r, - sup_r, - ) - .emit() - } else if sup_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } else { - self.report_sub_sup_conflict( - generic_param_scope, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - ) - } - } - - RegionResolutionError::UpperBoundUniverseConflict( - _, - _, - _, - sup_origin, - sup_r, - ) => { - assert!(sup_r.is_placeholder()); - - // Make a dummy value for the "sub region" -- - // this is the initial value of the - // placeholder. In practice, we expect more - // tailored errors that don't really use this - // value. - let sub_r = self.tcx.lifetimes.re_erased; - - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } - - RegionResolutionError::CannotNormalize(clause, origin) => { - let clause: ty::Clause<'tcx> = - clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx); - self.tcx - .dcx() - .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) - .emit() - } - } - }; - - guar = Some(e) - } - - guar.unwrap() - } - - // This method goes through all the errors and try to group certain types - // of error together, for the purpose of suggesting explicit lifetime - // parameters to the user. This is done so that we can have a more - // complete view of what lifetimes should be the same. - // If the return value is an empty vector, it means that processing - // failed (so the return value of this method should not be used). - // - // The method also attempts to weed out messages that seem like - // duplicates that will be unhelpful to the end-user. But - // obviously it never weeds out ALL errors. - fn process_errors( - &self, - errors: &[RegionResolutionError<'tcx>], - ) -> Vec<RegionResolutionError<'tcx>> { - debug!("process_errors()"); - - // We want to avoid reporting generic-bound failures if we can - // avoid it: these have a very high rate of being unhelpful in - // practice. This is because they are basically secondary - // checks that test the state of the region graph after the - // rest of inference is done, and the other kinds of errors - // indicate that the region constraint graph is internally - // inconsistent, so these test results are likely to be - // meaningless. - // - // Therefore, we filter them out of the list unless they are - // the only thing in the list. - - let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - RegionResolutionError::GenericBoundFailure(..) => true, - RegionResolutionError::ConcreteFailure(..) - | RegionResolutionError::SubSupConflict(..) - | RegionResolutionError::UpperBoundUniverseConflict(..) - | RegionResolutionError::CannotNormalize(..) => false, - }; - - let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { - errors.to_owned() - } else { - errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() - }; - - // sort the errors by span, for better error message stability. - errors.sort_by_key(|u| match *u { - RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), - RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), - RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), - }); - errors - } - +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Adds a note if the types come from similarly named crates fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { use hir::def_id::CrateNum; @@ -1167,14 +737,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let output1 = sig1.output(); let output2 = sig2.output(); let (x1, x2) = self.cmp(output1, output2); - if !output1.is_unit() { + let output_diff = x1 != x2; + if !output1.is_unit() || output_diff { values.0.push_normal(" -> "); (values.0).0.extend(x1.0); } - if !output2.is_unit() { + if !output2.is_unit() || output_diff { values.1.push_normal(" -> "); (values.1).0.extend(x2.0); } + values } @@ -2206,7 +1778,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, trace: TypeTrace<'tcx>, terr: TypeError<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); let span = trace.cause.span(); @@ -2215,7 +1787,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, self.type_error_additional_suggestions(&trace, terr), ); - let mut diag = self.tcx.dcx().create_err(failure_code); + let mut diag = self.dcx().create_err(failure_code); self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); diag } @@ -2338,359 +1910,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )) } - pub fn report_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option<SubregionOrigin<'tcx>>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> ErrorGuaranteed { - self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) - .emit() - } - - pub fn construct_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option<SubregionOrigin<'tcx>>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> Diag<'tcx> { - if let Some(SubregionOrigin::CompareImplItemObligation { - span, - impl_item_def_id, - trait_item_def_id, - }) = origin - { - return self.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{bound_kind}: {sub}`"), - ); - } - - let labeled_user_string = match bound_kind { - GenericKind::Param(ref p) => format!("the parameter type `{p}`"), - GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), - GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::Projection | ty::Inherent => { - format!("the associated type `{p}`") - } - ty::Weak => format!("the type alias `{p}`"), - ty::Opaque => format!("the opaque type `{p}`"), - }, - }; - - let mut err = self - .tcx - .dcx() - .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); - err.code(match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309, - ty::ReStatic => E0310, - _ => E0311, - }); - - '_explain: { - let (description, span) = match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) - } - _ => (format!("lifetime `{sub}`"), Some(span)), - }; - let prefix = format!("{labeled_user_string} must be valid for "); - label_msg_span(&mut err, &prefix, description, span, "..."); - if let Some(origin) = origin { - self.note_region_origin(&mut err, &origin); - } - } - - 'suggestion: { - let msg = "consider adding an explicit lifetime bound"; - - if (bound_kind, sub).has_infer_regions() - || (bound_kind, sub).has_placeholders() - || !bound_kind.is_suggestable(self.tcx, false) - { - let lt_name = sub.get_name_or_anon().to_string(); - err.help(format!("{msg} `{bound_kind}: {lt_name}`...")); - break 'suggestion; - } - - let mut generic_param_scope = generic_param_scope; - while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy { - generic_param_scope = self.tcx.local_parent(generic_param_scope); - } - - // type_param_sugg_span is (span, has_bounds, needs_parentheses) - let (type_scope, type_param_sugg_span) = match bound_kind { - GenericKind::Param(param) => { - let generics = self.tcx.generics_of(generic_param_scope); - let type_param = generics.type_param(param, self.tcx); - let def_id = type_param.def_id.expect_local(); - let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; - // Get the `hir::Param` to verify whether it already has any bounds. - // We do this to avoid suggesting code that ends up as `T: 'a'b`, - // instead we suggest `T: 'a + 'b` in that case. - let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); - let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { - Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), - // If `param` corresponds to `Self`, no usable suggestion span. - None if generics.has_self && param.index == 0 => None, - None => { - let span = if let Some(param) = - hir_generics.params.iter().find(|param| param.def_id == def_id) - && let ParamName::Plain(ident) = param.name - { - ident.span.shrink_to_hi() - } else { - let span = self.tcx.def_span(def_id); - span.shrink_to_hi() - }; - Some((span, false, None)) - } - }; - (scope, sugg_span) - } - _ => (generic_param_scope, None), - }; - let suggestion_scope = { - let lifetime_scope = match sub.kind() { - ty::ReStatic => hir::def_id::CRATE_DEF_ID, - _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { - Some(info) => info.def_id, - None => generic_param_scope, - }, - }; - match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { - true => type_scope, - false => lifetime_scope, - } - }; - - let mut suggs = vec![]; - let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); - - if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span - && suggestion_scope == type_scope - { - let suggestion = - if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; - - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((sp, format!("){suggestion}"))); - } else { - suggs.push((sp, suggestion)) - } - } else if let GenericKind::Alias(ref p) = bound_kind - && let ty::Projection = p.kind(self.tcx) - && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) - && let Some(ty::ImplTraitInTraitData::Trait { .. }) = - self.tcx.opt_rpitit_info(p.def_id) - { - // The lifetime found in the `impl` is longer than the one on the RPITIT. - // Do not suggest `<Type as Trait>::{opaque}: 'static`. - } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) { - let pred = format!("{bound_kind}: {lt_name}"); - let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); - suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) - } else { - let consider = format!("{msg} `{bound_kind}: {sub}`..."); - err.help(consider); - } - - if !suggs.is_empty() { - err.multipart_suggestion_verbose( - msg, - suggs, - Applicability::MaybeIncorrect, // Issue #41966 - ); - } - } - - err - } - - pub fn suggest_name_region( - &self, - generic_param_scope: LocalDefId, - lifetime: Region<'tcx>, - add_lt_suggs: &mut Vec<(Span, String)>, - ) -> String { - struct LifetimeReplaceVisitor<'tcx, 'a> { - tcx: TyCtxt<'tcx>, - needle: hir::LifetimeName, - new_lt: &'a str, - add_lt_suggs: &'a mut Vec<(Span, String)>, - } - - impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> { - fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { - if lt.res == self.needle { - let (pos, span) = lt.suggestion_position(); - let new_lt = &self.new_lt; - let sugg = match pos { - hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"), - hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "), - hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"), - hir::LifetimeSuggestionPosition::ElidedPathArgument => { - format!("{new_lt}, ") - } - hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"), - }; - self.add_lt_suggs.push((span, sugg)); - } - } - - fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { - return hir::intravisit::walk_ty(self, ty); - }; - let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); - if let Some(&(_, b)) = - opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) - { - let prev_needle = - std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b)); - for bound in opaque_ty.bounds { - self.visit_param_bound(bound); - } - self.needle = prev_needle; - } - } - } - - let (lifetime_def_id, lifetime_scope) = - match self.tcx.is_suitable_region(generic_param_scope, lifetime) { - Some(info) if !lifetime.has_name() => { - (info.bound_region.get_id().unwrap().expect_local(), info.def_id) - } - _ => return lifetime.get_name_or_anon().to_string(), - }; - - let new_lt = { - let generics = self.tcx.generics_of(lifetime_scope); - let mut used_names = - iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) - .flat_map(|g| &g.own_params) - .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) - .map(|p| p.name) - .collect::<Vec<_>>(); - let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope); - // consider late-bound lifetimes ... - used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map( - |p| match p { - ty::BoundVariableKind::Region(lt) => lt.get_name(), - _ => None, - }, - )); - (b'a'..=b'z') - .map(|c| format!("'{}", c as char)) - .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate)) - .unwrap_or("'lt".to_string()) - }; - - let mut visitor = LifetimeReplaceVisitor { - tcx: self.tcx, - needle: hir::LifetimeName::Param(lifetime_def_id), - add_lt_suggs, - new_lt: &new_lt, - }; - match self.tcx.expect_hir_owner_node(lifetime_scope) { - hir::OwnerNode::Item(i) => visitor.visit_item(i), - hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i), - hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), - hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), - hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), - hir::OwnerNode::Synthetic => unreachable!(), - } - - let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); - let sugg = ast_generics - .span_for_lifetime_suggestion() - .map(|span| (span, format!("{new_lt}, "))) - .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); - add_lt_suggs.push(sugg); - - new_lt - } - - fn report_sub_sup_conflict( - &self, - generic_param_scope: LocalDefId, - var_origin: RegionVariableOrigin, - sub_origin: SubregionOrigin<'tcx>, - sub_region: Region<'tcx>, - sup_origin: SubregionOrigin<'tcx>, - sup_region: Region<'tcx>, - ) -> ErrorGuaranteed { - let mut err = self.report_inference_failure(var_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "first, the lifetime cannot outlive ", - sup_region, - "...", - None, - ); - - debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); - debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); - debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); - debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); - debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); - - if let infer::Subtype(ref sup_trace) = sup_origin - && let infer::Subtype(ref sub_trace) = sub_origin - && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) - && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) - && sub_expected == sup_expected - && sub_found == sup_found - { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...but the lifetime must also be valid for ", - sub_region, - "...", - None, - ); - err.span_note( - sup_trace.cause.span, - format!("...so that the {}", sup_trace.cause.as_requirement_str()), - ); - - err.note_expected_found(&"", sup_expected, &"", sup_found); - return if sub_region.is_error() | sup_region.is_error() { - err.delay_as_bug() - } else { - err.emit() - }; - } - - self.note_region_origin(&mut err, &sup_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "but, the lifetime must be valid for ", - sub_region, - "...", - None, - ); - - self.note_region_origin(&mut err, &sub_origin); - if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } - } - /// Determine whether an error associated with the given span and definition /// should be treated as being caused by the implicit `From` conversion /// within `?` desugaring. @@ -2789,55 +2008,6 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> { } } -impl<'tcx> InferCtxt<'tcx> { - fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'tcx> { - let br_string = |br: ty::BoundRegionKind| { - let mut s = match br { - ty::BrNamed(_, name) => name.to_string(), - _ => String::new(), - }; - if !s.is_empty() { - s.push(' '); - } - s - }; - let var_description = match var_origin { - infer::MiscVariable(_) => String::new(), - infer::PatternRegion(_) => " for pattern".to_string(), - infer::AddrOfRegion(_) => " for borrow expression".to_string(), - infer::Autoref(_) => " for autoref".to_string(), - infer::Coercion(_) => " for automatic coercion".to_string(), - infer::BoundRegion(_, br, infer::FnCall) => { - format!(" for lifetime parameter {}in function call", br_string(br)) - } - infer::BoundRegion(_, br, infer::HigherRankedType) => { - format!(" for lifetime parameter {}in generic type", br_string(br)) - } - infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( - " for lifetime parameter {}in trait containing associated type `{}`", - br_string(br), - self.tcx.associated_item(def_id).name - ), - infer::RegionParameterDefinition(_, name) => { - format!(" for lifetime parameter `{name}`") - } - infer::UpvarRegion(ref upvar_id, _) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - format!(" for capture of `{var_name}` by closure") - } - infer::Nll(..) => bug!("NLL variable found in lexical phase"), - }; - - struct_span_code_err!( - self.tcx.dcx(), - var_origin.span(), - E0495, - "cannot infer an appropriate lifetime{} due to conflicting requirements", - var_description - ) - } -} - pub enum FailureCode { Error0317, Error0580, diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index d7349abc44c..084aebc296f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -391,7 +391,7 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, arg_data: InferenceDiagnosticsData, error_code: TypeAnnotationNeeded, - ) -> Diag<'tcx> { + ) -> Diag<'_> { let source_kind = "other"; let source_name = ""; let failure_span = None; @@ -436,7 +436,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'tcx> TypeErrCtxt<'_, 'tcx> { +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, error_code))] pub fn emit_inference_failure_err( &self, @@ -445,7 +445,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { arg: GenericArg<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let arg = self.resolve_vars_if_possible(arg); let arg_data = self.extract_inference_diagnostics_data(arg, None); @@ -453,7 +453,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If we don't have any typeck results we're outside // of a body, so we won't be able to get better info // here. - return self.bad_inference_failure_err(failure_span, arg_data, error_code); + return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); }; let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); @@ -465,7 +465,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let Some(InferSource { span, kind }) = local_visitor.infer_source else { - return self.bad_inference_failure_err(failure_span, arg_data, error_code); + return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); }; let (source_kind, name, path) = kind.ty_localized_msg(self); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 71a86683c21..9973646aecd 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -534,7 +534,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let tcx = self.tcx(); // Find the method being called. - let Ok(Some(instance)) = ty::Instance::resolve( + let Ok(Some(instance)) = ty::Instance::try_resolve( tcx, ctxt.param_env, ctxt.assoc_item.def_id, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs deleted file mode 100644 index 8fd19563c30..00000000000 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ /dev/null @@ -1,421 +0,0 @@ -use crate::errors::{ - note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, - RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, -}; -use crate::fluent_generated as fluent; -use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; -use crate::infer::{self, SubregionOrigin}; -use rustc_errors::{Diag, Subdiagnostic}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::symbol::kw; - -use super::ObligationCauseAsDiagArg; - -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(err); - } - infer::CheckAssociatedTypeBounds { ref parent, .. } => { - self.note_region_origin(err, parent); - } - infer::AscribeUserTypeProvePredicate(span) => { - RegionOriginNote::Plain { - span, - msg: fluent::infer_ascribe_user_type_prove_predicate, - } - .add_to_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'tcx> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::<Result<Vec<_>, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'tcx> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } -} diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index bc59b5e033b..d5e7de897d0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -52,10 +52,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) = tcx.sess.source_map().span_to_snippet(sp) => { if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { - diag.span_suggestion( - sp, + diag.span_suggestion_verbose( + sp.shrink_to_hi(), "use a float literal", - format!("{snippet}.0"), + ".0", MachineApplicable, ); } @@ -820,7 +820,7 @@ fn foo(&self) -> Self::T { String::new() } tcx.defaultness(item.id.owner_id) { let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); - if self.infcx.can_eq(param_env, assoc_ty, found) { + if self.infcx.can_eq_shallow(param_env, assoc_ty, found) { diag.span_label( item.span, "associated type defaults can't be assumed inside the \ @@ -843,7 +843,7 @@ fn foo(&self) -> Self::T { String::new() } let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); if let hir::Defaultness::Default { has_value: true } = tcx.defaultness(item.id.owner_id) - && self.infcx.can_eq(param_env, assoc_ty, found) + && self.infcx.can_eq_shallow(param_env, assoc_ty, found) { diag.span_label( item.span, diff --git a/compiler/rustc_infer/src/infer/error_reporting/region.rs b/compiler/rustc_infer/src/infer/error_reporting/region.rs new file mode 100644 index 00000000000..5a465f46e47 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/region.rs @@ -0,0 +1,1259 @@ +use std::iter; + +use rustc_errors::{ + struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495, +}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, ParamName}; +use rustc_middle::bug; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _}; +use rustc_span::symbol::kw; +use rustc_span::{ErrorGuaranteed, Span}; +use rustc_type_ir::Upcast as _; + +use super::nice_region_error::find_anon_type; +use super::{nice_region_error, ObligationCauseAsDiagArg}; +use crate::errors::{ + self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, + OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, +}; +use crate::fluent_generated as fluent; +use crate::infer::error_reporting::{ObligationCauseExt as _, TypeErrCtxt}; +use crate::infer::region_constraints::GenericKind; +use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin}; + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn report_region_errors( + &self, + generic_param_scope: LocalDefId, + errors: &[RegionResolutionError<'tcx>], + ) -> ErrorGuaranteed { + assert!(!errors.is_empty()); + + if let Some(guaranteed) = self.infcx.tainted_by_errors() { + return guaranteed; + } + + debug!("report_region_errors(): {} errors to start", errors.len()); + + // try to pre-process the errors, which will group some of them + // together into a `ProcessedErrors` group: + let errors = self.process_errors(errors); + + debug!("report_region_errors: {} errors after preprocessing", errors.len()); + + let mut guar = None; + for error in errors { + debug!("report_region_errors: error = {:?}", error); + + let e = if let Some(guar) = + self.try_report_nice_region_error(generic_param_scope, &error) + { + guar + } else { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + if sub.is_placeholder() || sup.is_placeholder() { + self.report_placeholder_failure(generic_param_scope, origin, sub, sup) + .emit() + } else { + self.report_concrete_failure(generic_param_scope, origin, sub, sup) + .emit() + } + } + + RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self + .report_generic_bound_failure( + generic_param_scope, + origin.span(), + Some(origin), + param_ty, + sub, + ), + + RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + _, + ) => { + if sub_r.is_placeholder() { + self.report_placeholder_failure( + generic_param_scope, + sub_origin, + sub_r, + sup_r, + ) + .emit() + } else if sup_r.is_placeholder() { + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() + } else { + self.report_sub_sup_conflict( + generic_param_scope, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + ) + } + } + + RegionResolutionError::UpperBoundUniverseConflict( + _, + _, + _, + sup_origin, + sup_r, + ) => { + assert!(sup_r.is_placeholder()); + + // Make a dummy value for the "sub region" -- + // this is the initial value of the + // placeholder. In practice, we expect more + // tailored errors that don't really use this + // value. + let sub_r = self.tcx.lifetimes.re_erased; + + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() + } + + RegionResolutionError::CannotNormalize(clause, origin) => { + let clause: ty::Clause<'tcx> = + clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx); + self.tcx + .dcx() + .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) + .emit() + } + } + }; + + guar = Some(e) + } + + guar.unwrap() + } + + // This method goes through all the errors and try to group certain types + // of error together, for the purpose of suggesting explicit lifetime + // parameters to the user. This is done so that we can have a more + // complete view of what lifetimes should be the same. + // If the return value is an empty vector, it means that processing + // failed (so the return value of this method should not be used). + // + // The method also attempts to weed out messages that seem like + // duplicates that will be unhelpful to the end-user. But + // obviously it never weeds out ALL errors. + fn process_errors( + &self, + errors: &[RegionResolutionError<'tcx>], + ) -> Vec<RegionResolutionError<'tcx>> { + debug!("process_errors()"); + + // We want to avoid reporting generic-bound failures if we can + // avoid it: these have a very high rate of being unhelpful in + // practice. This is because they are basically secondary + // checks that test the state of the region graph after the + // rest of inference is done, and the other kinds of errors + // indicate that the region constraint graph is internally + // inconsistent, so these test results are likely to be + // meaningless. + // + // Therefore, we filter them out of the list unless they are + // the only thing in the list. + + let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) + | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::UpperBoundUniverseConflict(..) + | RegionResolutionError::CannotNormalize(..) => false, + }; + + let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { + errors.to_owned() + } else { + errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() + }; + + // sort the errors by span, for better error message stability. + errors.sort_by_key(|u| match *u { + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), + RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), + RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), + }); + errors + } + + pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { + match *origin { + infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + } + .add_to_diag(err), + infer::Reborrow(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) + } + infer::RelateObjectBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } + .add_to_diag(err); + } + infer::ReferenceOutlivesReferent(ty, span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diag(err); + } + infer::RelateParamBound(span, ty, opt_span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + } + .add_to_diag(err); + if let Some(span) = opt_span { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } + .add_to_diag(err); + } + } + infer::RelateRegionParamBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } + .add_to_diag(err); + } + infer::CompareImplItemObligation { span, .. } => { + RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } + .add_to_diag(err); + } + infer::CheckAssociatedTypeBounds { ref parent, .. } => { + self.note_region_origin(err, parent); + } + infer::AscribeUserTypeProvePredicate(span) => { + RegionOriginNote::Plain { + span, + msg: fluent::infer_ascribe_user_type_prove_predicate, + } + .add_to_diag(err); + } + } + } + + pub(super) fn report_concrete_failure( + &self, + generic_param_scope: LocalDefId, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + let mut err = match origin { + infer::Subtype(box trace) => { + let terr = TypeError::RegionsDoesNotOutlive(sup, sub); + let mut err = self.report_and_explain_type_error(trace, terr); + match (*sub, *sup) { + (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} + (ty::RePlaceholder(_), _) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + " doesn't meet the lifetime requirements", + None, + ); + } + (_, ty::RePlaceholder(_)) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "the required lifetime does not necessarily outlive ", + sub, + "", + None, + ); + } + _ => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "...does not necessarily outlive ", + sub, + "", + None, + ); + } + } + err + } + infer::Reborrow(span) => { + let reference_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::RefValidFor, + note_and_explain::SuffixKind::Continues, + ); + let content_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::ContentValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesContent { + span, + notes: reference_valid.into_iter().chain(content_valid).collect(), + }) + } + infer::RelateObjectBound(span) => { + let object_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::TypeObjValidFor, + note_and_explain::SuffixKind::Empty, + ); + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::SourcePointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesBound { + span, + notes: object_valid.into_iter().chain(pointer_valid).collect(), + }) + } + infer::RelateParamBound(span, ty, opt_span) => { + let prefix = match *sub { + ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, + _ => note_and_explain::PrefixKind::TypeOutlive, + }; + let suffix = if opt_span.is_some() { + note_and_explain::SuffixKind::ReqByBinding + } else { + note_and_explain::SuffixKind::Empty + }; + let note = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + opt_span, + prefix, + suffix, + ); + self.dcx().create_err(FulfillReqLifetime { + span, + ty: self.resolve_vars_if_possible(ty), + note, + }) + } + infer::RelateRegionParamBound(span) => { + let param_instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfParamInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let param_must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfParamMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), + }) + } + infer::ReferenceOutlivesReferent(ty, span) => { + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::PointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + let data_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::DataValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(RefLongerThanData { + span, + ty: self.resolve_vars_if_possible(ty), + notes: pointer_valid.into_iter().chain(data_valid).collect(), + }) + } + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { + let mut err = self.infcx.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{sup}: {sub}`"), + ); + // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause + if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) + && generics.where_clause_span.contains(span) + { + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + } + err + } + infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { + let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); + + // Don't mention the item name if it's an RPITIT, since that'll just confuse + // folks. + if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label( + trait_item_span, + format!("definition of `{item_name}` from trait"), + ); + } + + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + err + } + infer::AscribeUserTypeProvePredicate(span) => { + let instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: instantiated.into_iter().chain(must_outlive).collect(), + }) + } + }; + if sub.is_error() || sup.is_error() { + err.downgrade_to_delayed_bug(); + } + err + } + + pub fn suggest_copy_trait_method_bounds( + &self, + trait_item_def_id: DefId, + impl_item_def_id: LocalDefId, + err: &mut Diag<'_>, + ) { + // FIXME(compiler-errors): Right now this is only being used for region + // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, + // but right now it's not really very smart when it comes to implicit `Sized` + // predicates and bounds on the trait itself. + + let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) + else { + return; + }; + let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + return; + }; + let trait_args = trait_ref + .instantiate_identity() + // Replace the explicit self type with `Self` for better suggestion rendering + .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) + .args; + let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) + .rebase_onto(self.tcx, impl_def_id, trait_args); + + let Ok(trait_predicates) = + self.tcx + .explicit_predicates_of(trait_item_def_id) + .instantiate_own(self.tcx, trait_item_args) + .map(|(pred, _)| { + if pred.is_suggestable(self.tcx, false) { + Ok(pred.to_string()) + } else { + Err(()) + } + }) + .collect::<Result<Vec<_>, ()>>() + else { + return; + }; + + let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { + return; + }; + + let suggestion = if trait_predicates.is_empty() { + WhereClauseSuggestions::Remove { span: generics.where_clause_span } + } else { + let space = if generics.where_clause_span.is_empty() { " " } else { "" }; + WhereClauseSuggestions::CopyPredicates { + span: generics.where_clause_span, + space, + trait_predicates: trait_predicates.join(", "), + } + }; + err.subdiagnostic(suggestion); + } + + pub(super) fn report_placeholder_failure( + &self, + generic_param_scope: LocalDefId, + placeholder_origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + // I can't think how to do better than this right now. -nikomatsakis + debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); + match placeholder_origin { + infer::Subtype(box ref trace) + if matches!( + &trace.cause.code().peel_derives(), + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + ) => + { + // Hack to get around the borrow checker because trace.cause has an `Rc`. + if let ObligationCauseCode::WhereClause(_, span) + | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = + &trace.cause.code().peel_derives() + && !span.is_dummy() + { + let span = *span; + self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) + .with_span_note(span, "the lifetime requirement is introduced here") + } else { + unreachable!( + "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." + ) + } + } + infer::Subtype(box trace) => { + let terr = TypeError::RegionsPlaceholderMismatch; + return self.report_and_explain_type_error(trace, terr); + } + _ => { + return self.report_concrete_failure( + generic_param_scope, + placeholder_origin, + sub, + sup, + ); + } + } + } + + pub fn report_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> ErrorGuaranteed { + self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) + .emit() + } + + pub fn construct_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> Diag<'a> { + if let Some(SubregionOrigin::CompareImplItemObligation { + span, + impl_item_def_id, + trait_item_def_id, + }) = origin + { + return self.infcx.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{bound_kind}: {sub}`"), + ); + } + + let labeled_user_string = match bound_kind { + GenericKind::Param(ref p) => format!("the parameter type `{p}`"), + GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), + GenericKind::Alias(ref p) => match p.kind(self.tcx) { + ty::Projection | ty::Inherent => { + format!("the associated type `{p}`") + } + ty::Weak => format!("the type alias `{p}`"), + ty::Opaque => format!("the opaque type `{p}`"), + }, + }; + + let mut err = self + .tcx + .dcx() + .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); + err.code(match sub.kind() { + ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309, + ty::ReStatic => E0310, + _ => E0311, + }); + + '_explain: { + let (description, span) = match sub.kind() { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { + msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) + } + _ => (format!("lifetime `{sub}`"), Some(span)), + }; + let prefix = format!("{labeled_user_string} must be valid for "); + label_msg_span(&mut err, &prefix, description, span, "..."); + if let Some(origin) = origin { + self.note_region_origin(&mut err, &origin); + } + } + + 'suggestion: { + let msg = "consider adding an explicit lifetime bound"; + + if (bound_kind, sub).has_infer_regions() + || (bound_kind, sub).has_placeholders() + || !bound_kind.is_suggestable(self.tcx, false) + { + let lt_name = sub.get_name_or_anon().to_string(); + err.help(format!("{msg} `{bound_kind}: {lt_name}`...")); + break 'suggestion; + } + + let mut generic_param_scope = generic_param_scope; + while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy { + generic_param_scope = self.tcx.local_parent(generic_param_scope); + } + + // type_param_sugg_span is (span, has_bounds, needs_parentheses) + let (type_scope, type_param_sugg_span) = match bound_kind { + GenericKind::Param(param) => { + let generics = self.tcx.generics_of(generic_param_scope); + let type_param = generics.type_param(param, self.tcx); + let def_id = type_param.def_id.expect_local(); + let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; + // Get the `hir::Param` to verify whether it already has any bounds. + // We do this to avoid suggesting code that ends up as `T: 'a'b`, + // instead we suggest `T: 'a + 'b` in that case. + let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); + let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { + Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), + // If `param` corresponds to `Self`, no usable suggestion span. + None if generics.has_self && param.index == 0 => None, + None => { + let span = if let Some(param) = + hir_generics.params.iter().find(|param| param.def_id == def_id) + && let ParamName::Plain(ident) = param.name + { + ident.span.shrink_to_hi() + } else { + let span = self.tcx.def_span(def_id); + span.shrink_to_hi() + }; + Some((span, false, None)) + } + }; + (scope, sugg_span) + } + _ => (generic_param_scope, None), + }; + let suggestion_scope = { + let lifetime_scope = match sub.kind() { + ty::ReStatic => hir::def_id::CRATE_DEF_ID, + _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { + Some(info) => info.def_id, + None => generic_param_scope, + }, + }; + match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { + true => type_scope, + false => lifetime_scope, + } + }; + + let mut suggs = vec![]; + let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); + + if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span + && suggestion_scope == type_scope + { + let suggestion = + if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((sp, format!("){suggestion}"))); + } else { + suggs.push((sp, suggestion)) + } + } else if let GenericKind::Alias(ref p) = bound_kind + && let ty::Projection = p.kind(self.tcx) + && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) + && let Some(ty::ImplTraitInTraitData::Trait { .. }) = + self.tcx.opt_rpitit_info(p.def_id) + { + // The lifetime found in the `impl` is longer than the one on the RPITIT. + // Do not suggest `<Type as Trait>::{opaque}: 'static`. + } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) { + let pred = format!("{bound_kind}: {lt_name}"); + let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); + suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) + } else { + let consider = format!("{msg} `{bound_kind}: {sub}`..."); + err.help(consider); + } + + if !suggs.is_empty() { + err.multipart_suggestion_verbose( + msg, + suggs, + Applicability::MaybeIncorrect, // Issue #41966 + ); + } + } + + err + } + + pub fn suggest_name_region( + &self, + generic_param_scope: LocalDefId, + lifetime: Region<'tcx>, + add_lt_suggs: &mut Vec<(Span, String)>, + ) -> String { + struct LifetimeReplaceVisitor<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + needle: hir::LifetimeName, + new_lt: &'a str, + add_lt_suggs: &'a mut Vec<(Span, String)>, + } + + impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> { + fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { + if lt.res == self.needle { + let (pos, span) = lt.suggestion_position(); + let new_lt = &self.new_lt; + let sugg = match pos { + hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"), + hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "), + hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"), + hir::LifetimeSuggestionPosition::ElidedPathArgument => { + format!("{new_lt}, ") + } + hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"), + }; + self.add_lt_suggs.push((span, sugg)); + } + } + + fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { + let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { + return hir::intravisit::walk_ty(self, ty); + }; + let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); + if let Some(&(_, b)) = + opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) + { + let prev_needle = + std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b)); + for bound in opaque_ty.bounds { + self.visit_param_bound(bound); + } + self.needle = prev_needle; + } + } + } + + let (lifetime_def_id, lifetime_scope) = + match self.tcx.is_suitable_region(generic_param_scope, lifetime) { + Some(info) if !lifetime.has_name() => { + (info.bound_region.get_id().unwrap().expect_local(), info.def_id) + } + _ => return lifetime.get_name_or_anon().to_string(), + }; + + let new_lt = { + let generics = self.tcx.generics_of(lifetime_scope); + let mut used_names = + iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) + .flat_map(|g| &g.own_params) + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name) + .collect::<Vec<_>>(); + let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope); + // consider late-bound lifetimes ... + used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map( + |p| match p { + ty::BoundVariableKind::Region(lt) => lt.get_name(), + _ => None, + }, + )); + (b'a'..=b'z') + .map(|c| format!("'{}", c as char)) + .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate)) + .unwrap_or("'lt".to_string()) + }; + + let mut visitor = LifetimeReplaceVisitor { + tcx: self.tcx, + needle: hir::LifetimeName::Param(lifetime_def_id), + add_lt_suggs, + new_lt: &new_lt, + }; + match self.tcx.expect_hir_owner_node(lifetime_scope) { + hir::OwnerNode::Item(i) => visitor.visit_item(i), + hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i), + hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), + hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), + hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), + hir::OwnerNode::Synthetic => unreachable!(), + } + + let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); + let sugg = ast_generics + .span_for_lifetime_suggestion() + .map(|span| (span, format!("{new_lt}, "))) + .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); + add_lt_suggs.push(sugg); + + new_lt + } + + fn report_sub_sup_conflict( + &self, + generic_param_scope: LocalDefId, + var_origin: RegionVariableOrigin, + sub_origin: SubregionOrigin<'tcx>, + sub_region: Region<'tcx>, + sup_origin: SubregionOrigin<'tcx>, + sup_region: Region<'tcx>, + ) -> ErrorGuaranteed { + let mut err = self.report_inference_failure(var_origin); + + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "first, the lifetime cannot outlive ", + sup_region, + "...", + None, + ); + + debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); + debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); + debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); + debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); + debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); + + if let infer::Subtype(ref sup_trace) = sup_origin + && let infer::Subtype(ref sub_trace) = sub_origin + && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) + && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) + && sub_expected == sup_expected + && sub_found == sup_found + { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "...but the lifetime must also be valid for ", + sub_region, + "...", + None, + ); + err.span_note( + sup_trace.cause.span, + format!("...so that the {}", sup_trace.cause.as_requirement_str()), + ); + + err.note_expected_found(&"", sup_expected, &"", sup_found); + return if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug() + } else { + err.emit() + }; + } + + self.note_region_origin(&mut err, &sup_origin); + + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "but, the lifetime must be valid for ", + sub_region, + "...", + None, + ); + + self.note_region_origin(&mut err, &sub_origin); + if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } + } + + fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> { + let br_string = |br: ty::BoundRegionKind| { + let mut s = match br { + ty::BrNamed(_, name) => name.to_string(), + _ => String::new(), + }; + if !s.is_empty() { + s.push(' '); + } + s + }; + let var_description = match var_origin { + infer::MiscVariable(_) => String::new(), + infer::PatternRegion(_) => " for pattern".to_string(), + infer::AddrOfRegion(_) => " for borrow expression".to_string(), + infer::Autoref(_) => " for autoref".to_string(), + infer::Coercion(_) => " for automatic coercion".to_string(), + infer::BoundRegion(_, br, infer::FnCall) => { + format!(" for lifetime parameter {}in function call", br_string(br)) + } + infer::BoundRegion(_, br, infer::HigherRankedType) => { + format!(" for lifetime parameter {}in generic type", br_string(br)) + } + infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( + " for lifetime parameter {}in trait containing associated type `{}`", + br_string(br), + self.tcx.associated_item(def_id).name + ), + infer::RegionParameterDefinition(_, name) => { + format!(" for lifetime parameter `{name}`") + } + infer::UpvarRegion(ref upvar_id, _) => { + let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + format!(" for capture of `{var_name}` by closure") + } + infer::Nll(..) => bug!("NLL variable found in lexical phase"), + }; + + struct_span_code_err!( + self.dcx(), + var_origin.span(), + E0495, + "cannot infer an appropriate lifetime{} due to conflicting requirements", + var_description + ) + } +} + +pub(super) fn note_and_explain_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_>, + generic_param_scope: LocalDefId, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, + alt_span: Option<Span>, +) { + let (description, span) = match *region { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { + msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) + } + + ty::ReError(_) => return, + + // FIXME(#125431): `ReVar` shouldn't reach here. + ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), + + ty::ReBound(..) | ty::ReErased => { + bug!("unexpected region for note_and_explain_region: {:?}", region); + } + }; + + emit_msg_span(err, prefix, description, span, suffix); +} + +fn explain_free_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_>, + generic_param_scope: LocalDefId, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, +) { + let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); + + label_msg_span(err, prefix, description, span, suffix); +} + +fn msg_span_from_named_region<'tcx>( + tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, + region: ty::Region<'tcx>, + alt_span: Option<Span>, +) -> (String, Option<Span>) { + match *region { + ty::ReEarlyParam(br) => { + let scope = tcx + .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) + .expect_local(); + let span = if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + param.span + } else { + tcx.def_span(scope) + }; + let text = if br.has_name() { + format!("the lifetime `{}` as defined here", br.name) + } else { + "the anonymous lifetime as defined here".to_string() + }; + (text, Some(span)) + } + ty::ReLateParam(ref fr) => { + if !fr.bound_region.is_named() + && let Some((ty, _)) = + find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) + { + ("the anonymous lifetime defined here".to_string(), Some(ty.span)) + } else { + match fr.bound_region { + ty::BoundRegionKind::BrNamed(_, name) => { + let span = if let Some(param) = tcx + .hir() + .get_generics(generic_param_scope) + .and_then(|generics| generics.get_named(name)) + { + param.span + } else { + tcx.def_span(generic_param_scope) + }; + let text = if name == kw::UnderscoreLifetime { + "the anonymous lifetime as defined here".to_string() + } else { + format!("the lifetime `{name}` as defined here") + }; + (text, Some(span)) + } + ty::BrAnon => ( + "the anonymous lifetime as defined here".to_string(), + Some(tcx.def_span(generic_param_scope)), + ), + _ => ( + format!("the lifetime `{region}` as defined here"), + Some(tcx.def_span(generic_param_scope)), + ), + } + } + } + ty::ReStatic => ("the static lifetime".to_owned(), alt_span), + ty::RePlaceholder(ty::PlaceholderRegion { + bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. }, + .. + }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), + ty::RePlaceholder(ty::PlaceholderRegion { + bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, + .. + }) => ("an anonymous lifetime".to_owned(), None), + _ => bug!("{:?}", region), + } +} + +fn emit_msg_span( + err: &mut Diag<'_>, + prefix: &str, + description: String, + span: Option<Span>, + suffix: &str, +) { + let message = format!("{prefix}{description}{suffix}"); + + if let Some(span) = span { + err.span_note(span, message); + } else { + err.note(message); + } +} + +fn label_msg_span( + err: &mut Diag<'_>, + prefix: &str, + description: String, + span: Option<Span>, + suffix: &str, +) { + let message = format!("{prefix}{description}{suffix}"); + + if let Some(span) = span { + err.span_label(span, message); + } else { + err.note(message); + } +} + +#[instrument(level = "trace", skip(infcx))] +pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( + infcx: &'a InferCtxt<'tcx>, + generic_param_scope: LocalDefId, + span: Span, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, + opaque_ty_key: ty::OpaqueTypeKey<'tcx>, +) -> Diag<'a> { + let tcx = infcx.tcx; + let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime { + span, + opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), + opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), + }); + + // Explain the region we are capturing. + match *hidden_region { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` field would be set to + // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. + explain_free_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{hidden_ty}` captures "), + hidden_region, + "", + ); + if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { + let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); + nice_region_error::suggest_new_region_bound( + tcx, + &mut err, + fn_returns, + hidden_region.to_string(), + None, + format!("captures `{hidden_region}`"), + None, + Some(reg_info.def_id), + ) + } + } + ty::RePlaceholder(_) => { + explain_free_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } + ty::ReError(_) => { + err.downgrade_to_delayed_bug(); + } + _ => { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happen + // in a case like + // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. + + // We can at least report a really cryptic error for now. + note_and_explain_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{hidden_ty}` captures "), + hidden_region, + "", + None, + ); + } + } + + err +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a3cf588da1c..9f55939c165 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -53,6 +53,7 @@ use type_variable::TypeVariableOrigin; pub mod at; pub mod canonical; +mod context; pub mod error_reporting; pub mod free_regions; mod freshen; @@ -684,8 +685,8 @@ impl<'tcx> InferOk<'tcx, ()> { } impl<'tcx> InferCtxt<'tcx> { - pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { - self.tcx.dcx() + pub fn dcx(&self) -> DiagCtxtHandle<'_> { + self.tcx.dcx().taintable_handle(&self.tainted_by_errors) } pub fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { @@ -767,19 +768,9 @@ impl<'tcx> InferCtxt<'tcx> { .collect() } - pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool - where - T: at::ToTrace<'tcx>, - { - let origin = &ObligationCause::dummy(); - self.probe(|_| { - // We're only answering whether there could be a subtyping relation, and with - // opaque types, "there could be one", via registering a hidden type. - self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok() - }) - } - - pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool + // FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that, + // or we need to process the obligations. + pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool where T: at::ToTrace<'tcx>, { @@ -1089,19 +1080,7 @@ impl<'tcx> InferCtxt<'tcx> { /// inference variables, regionck errors). #[must_use = "this method does not have any side effects"] pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> { - if let Some(guar) = self.tainted_by_errors.get() { - Some(guar) - } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation { - // Errors reported since this infcx was made. Lint errors are - // excluded to avoid some being swallowed in the presence of - // non-lint errors. (It's arguable whether or not this exclusion is - // important.) - let guar = self.dcx().has_errors().unwrap(); - self.set_tainted_by_errors(guar); - Some(guar) - } else { - None - } + self.tainted_by_errors.get() } /// Set the "tainted by errors" flag to true. We call this when we @@ -1328,8 +1307,7 @@ impl<'tcx> InferCtxt<'tcx> { bug!("`{value:?}` is not fully resolved"); } if value.has_infer_regions() { - let guar = - self.tcx.dcx().delayed_bug(format!("`{value:?}` is not fully resolved")); + let guar = self.dcx().delayed_bug(format!("`{value:?}` is not fully resolved")); Ok(self.tcx.fold_regions(value, |re, _| { if re.is_var() { ty::Region::new_error(self.tcx, guar) } else { re } })) @@ -1607,7 +1585,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'tcx> TypeErrCtxt<'_, 'tcx> { +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // [Note-Type-error-reporting] // An invariant is that anytime the expected or actual type is Error (the special // error type, meaning that an error occurred when typechecking this expression), @@ -1623,9 +1601,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sp: Span, mk_diag: M, actual_ty: Ty<'tcx>, - ) -> Diag<'tcx> + ) -> Diag<'a> where - M: FnOnce(String) -> Diag<'tcx>, + M: FnOnce(String) -> Diag<'a>, { let actual_ty = self.resolve_vars_if_possible(actual_ty); debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); @@ -1646,7 +1624,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, err: TypeError<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) } @@ -1656,7 +1634,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expected: ty::Const<'tcx>, actual: ty::Const<'tcx>, err: TypeError<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index b8dd501a721..7c764cccc47 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -156,7 +156,7 @@ impl<'tcx> InferCtxt<'tcx> { if self.can_define_opaque_ty(b_def_id) && self.tcx.is_type_alias_impl_trait(b_def_id) { - self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag { + self.dcx().emit_err(OpaqueHiddenTypeDiag { span, hidden_type: self.tcx.def_span(b_def_id), opaque_type: self.tcx.def_span(def_id), diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs deleted file mode 100644 index 6bab3ad6ba3..00000000000 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ /dev/null @@ -1,266 +0,0 @@ -// The outlines relation `T: 'a` or `'a: 'b`. This code frequently -// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that -// RFC for reference. - -use rustc_data_structures::sso::SsoHashSet; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{GenericArg, GenericArgKind}; -use smallvec::{smallvec, SmallVec}; - -#[derive(Debug)] -pub enum Component<'tcx> { - Region(ty::Region<'tcx>), - Param(ty::ParamTy), - Placeholder(ty::PlaceholderType), - UnresolvedInferenceVariable(ty::InferTy), - - // Projections like `T::Foo` are tricky because a constraint like - // `T::Foo: 'a` can be satisfied in so many ways. There may be a - // where-clause that says `T::Foo: 'a`, or the defining trait may - // include a bound like `type Foo: 'static`, or -- in the most - // conservative way -- we can prove that `T: 'a` (more generally, - // that all components in the projection outlive `'a`). This code - // is not in a position to judge which is the best technique, so - // we just product the projection as a component and leave it to - // the consumer to decide (but see `EscapingProjection` below). - Alias(ty::AliasTy<'tcx>), - - // In the case where a projection has escaping regions -- meaning - // regions bound within the type itself -- we always use - // the most conservative rule, which requires that all components - // outlive the bound. So for example if we had a type like this: - // - // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo > - // ~~~~~~~~~~~~~~~~~~~~~~~~~ - // - // then the inner projection (underlined) has an escaping region - // `'a`. We consider that outer trait `'c` to meet a bound if `'b` - // outlives `'b: 'c`, and we don't consider whether the trait - // declares that `Foo: 'static` etc. Therefore, we just return the - // free components of such a projection (in this case, `'b`). - // - // However, in the future, we may want to get smarter, and - // actually return a "higher-ranked projection" here. Therefore, - // we mark that these components are part of an escaping - // projection, so that implied bounds code can avoid relying on - // them. This gives us room to improve the regionck reasoning in - // the future without breaking backwards compat. - EscapingAlias(Vec<Component<'tcx>>), -} - -/// Push onto `out` all the things that must outlive `'a` for the condition -/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. -pub fn push_outlives_components<'tcx>( - tcx: TyCtxt<'tcx>, - ty0: Ty<'tcx>, - out: &mut SmallVec<[Component<'tcx>; 4]>, -) { - let mut visited = SsoHashSet::new(); - compute_components(tcx, ty0, out, &mut visited); - debug!("components({:?}) = {:?}", ty0, out); -} - -fn compute_components<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, -) { - // Descend through the types, looking for the various "base" - // components and collecting them into `out`. This is not written - // with `collect()` because of the need to sometimes skip subtrees - // in the `subtys` iterator (e.g., when encountering a - // projection). - match *ty.kind() { - ty::FnDef(_, args) => { - // HACK(eddyb) ignore lifetimes found shallowly in `args`. - // This is inconsistent with `ty::Adt` (including all args) - // and with `ty::Closure` (ignoring all args other than - // upvars, of which a `ty::FnDef` doesn't have any), but - // consistent with previous (accidental) behavior. - // See https://github.com/rust-lang/rust/issues/70917 - // for further background and discussion. - for child in args { - match child.unpack() { - GenericArgKind::Type(ty) => { - compute_components(tcx, ty, out, visited); - } - GenericArgKind::Lifetime(_) => {} - GenericArgKind::Const(_) => { - compute_components_recursive(tcx, child, out, visited); - } - } - } - } - - ty::Pat(element, _) | - ty::Array(element, _) => { - // Don't look into the len const as it doesn't affect regions - compute_components(tcx, element, out, visited); - } - - ty::Closure(_, args) => { - let tupled_ty = args.as_closure().tupled_upvars_ty(); - compute_components(tcx, tupled_ty, out, visited); - } - - ty::CoroutineClosure(_, args) => { - let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty(); - compute_components(tcx, tupled_ty, out, visited); - } - - ty::Coroutine(_, args) => { - // Same as the closure case - let tupled_ty = args.as_coroutine().tupled_upvars_ty(); - compute_components(tcx, tupled_ty, out, visited); - - // We ignore regions in the coroutine interior as we don't - // want these to affect region inference - } - - // All regions are bound inside a witness - ty::CoroutineWitness(..) => (), - - // OutlivesTypeParameterEnv -- the actual checking that `X:'a` - // is implied by the environment is done in regionck. - ty::Param(p) => { - out.push(Component::Param(p)); - } - - ty::Placeholder(p) => { - out.push(Component::Placeholder(p)); - } - - // For projections, we prefer to generate an obligation like - // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the - // regionck more ways to prove that it holds. However, - // regionck is not (at least currently) prepared to deal with - // higher-ranked regions that may appear in the - // trait-ref. Therefore, if we see any higher-ranked regions, - // we simply fallback to the most restrictive rule, which - // requires that `Pi: 'a` for all `i`. - ty::Alias(_, alias_ty) => { - if !alias_ty.has_escaping_bound_vars() { - // best case: no escaping regions, so push the - // projection and skip the subtree (thus generating no - // constraints for Pi). This defers the choice between - // the rules OutlivesProjectionEnv, - // OutlivesProjectionTraitDef, and - // OutlivesProjectionComponents to regionck. - out.push(Component::Alias(alias_ty)); - } else { - // fallback case: hard code - // OutlivesProjectionComponents. Continue walking - // through and constrain Pi. - let mut subcomponents = smallvec![]; - let mut subvisited = SsoHashSet::new(); - compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited); - out.push(Component::EscapingAlias(subcomponents.into_iter().collect())); - } - } - - // We assume that inference variables are fully resolved. - // So, if we encounter an inference variable, just record - // the unresolved variable as a component. - ty::Infer(infer_ty) => { - out.push(Component::UnresolvedInferenceVariable(infer_ty)); - } - - // Most types do not introduce any region binders, nor - // involve any other subtle cases, and so the WF relation - // simply constraints any regions referenced directly by - // the type and then visits the types that are lexically - // contained within. (The comments refer to relevant rules - // from RFC1214.) - ty::Bool | // OutlivesScalar - ty::Char | // OutlivesScalar - ty::Int(..) | // OutlivesScalar - ty::Uint(..) | // OutlivesScalar - ty::Float(..) | // OutlivesScalar - ty::Never | // ... - ty::Adt(..) | // OutlivesNominalType - ty::Foreign(..) | // OutlivesNominalType - ty::Str | // OutlivesScalar (ish) - ty::Slice(..) | // ... - ty::RawPtr(..) | // ... - ty::Ref(..) | // OutlivesReference - ty::Tuple(..) | // ... - ty::FnPtr(_) | // OutlivesFunction (*) - ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) - ty::Bound(..) | - ty::Error(_) => { - // (*) Function pointers and trait objects are both binders. - // In the RFC, this means we would add the bound regions to - // the "bound regions list". In our representation, no such - // list is maintained explicitly, because bound regions - // themselves can be readily identified. - compute_components_recursive(tcx, ty.into(), out, visited); - } - } -} - -/// Collect [Component]s for *all* the args of `parent`. -/// -/// This should not be used to get the components of `parent` itself. -/// Use [push_outlives_components] instead. -pub(super) fn compute_alias_components_recursive<'tcx>( - tcx: TyCtxt<'tcx>, - alias_ty: Ty<'tcx>, - out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, -) { - let ty::Alias(kind, alias_ty) = alias_ty.kind() else { - unreachable!("can only call `compute_alias_components_recursive` on an alias type") - }; - let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] }; - for (index, child) in alias_ty.args.iter().enumerate() { - if opt_variances.get(index) == Some(&ty::Bivariant) { - continue; - } - if !visited.insert(child) { - continue; - } - match child.unpack() { - GenericArgKind::Type(ty) => { - compute_components(tcx, ty, out, visited); - } - GenericArgKind::Lifetime(lt) => { - // Ignore higher ranked regions. - if !lt.is_bound() { - out.push(Component::Region(lt)); - } - } - GenericArgKind::Const(_) => { - compute_components_recursive(tcx, child, out, visited); - } - } - } -} - -/// Collect [Component]s for *all* the args of `parent`. -/// -/// This should not be used to get the components of `parent` itself. -/// Use [push_outlives_components] instead. -fn compute_components_recursive<'tcx>( - tcx: TyCtxt<'tcx>, - parent: GenericArg<'tcx>, - out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, -) { - for child in parent.walk_shallow(visited) { - match child.unpack() { - GenericArgKind::Type(ty) => { - compute_components(tcx, ty, out, visited); - } - GenericArgKind::Lifetime(lt) => { - // Ignore higher ranked regions. - if !lt.is_bound() { - out.push(Component::Region(lt)); - } - } - GenericArgKind::Const(_) => { - compute_components_recursive(tcx, child, out, visited); - } - } - } -} diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 48d006e7fbc..89ff4604560 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -8,7 +8,6 @@ use crate::infer::lexical_region_resolve; use rustc_middle::traits::query::{NoSolution, OutlivesBound}; use rustc_middle::ty; -pub mod components; pub mod env; pub mod for_liveness; pub mod obligations; diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 32c790523b6..d82ae7b4fb8 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -59,7 +59,6 @@ //! might later infer `?U` to something like `&'b u32`, which would //! imply that `'b: 'a`. -use crate::infer::outlives::components::{push_outlives_components, Component}; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; use crate::infer::resolve::OpportunisticRegionResolver; @@ -75,6 +74,7 @@ use rustc_middle::ty::{ }; use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate}; use rustc_span::DUMMY_SP; +use rustc_type_ir::outlives::{push_outlives_components, Component}; use smallvec::smallvec; use super::env::OutlivesEnvironment; @@ -291,7 +291,7 @@ where fn components_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, - components: &[Component<'tcx>], + components: &[Component<TyCtxt<'tcx>>], region: ty::Region<'tcx>, category: ConstraintCategory<'tcx>, ) { @@ -471,7 +471,7 @@ where // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let verify_bound = self.verify_bound.alias_bound(alias_ty, &mut Default::default()); + let verify_bound = self.verify_bound.alias_bound(alias_ty); debug!("alias_must_outlive: pushing {:?}", verify_bound); self.delegate.push_verify(origin, GenericKind::Alias(alias_ty), region, verify_bound); } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 7e977b9b954..2392a82025a 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,10 +1,8 @@ -use crate::infer::outlives::components::{compute_alias_components_recursive, Component}; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; use crate::infer::{GenericKind, VerifyBound}; -use rustc_data_structures::sso::SsoHashSet; -use rustc_middle::ty::GenericArg; use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt}; +use rustc_type_ir::outlives::{compute_alias_components_recursive, Component}; use smallvec::smallvec; @@ -99,12 +97,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } - #[instrument(level = "debug", skip(self, visited))] - pub fn alias_bound( - &self, - alias_ty: ty::AliasTy<'tcx>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, - ) -> VerifyBound<'tcx> { + #[instrument(level = "debug", skip(self))] + pub fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { let alias_ty_as_ty = alias_ty.to_ty(self.tcx); // Search the env for where clauses like `P: 'a`. @@ -130,21 +124,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // see the extensive comment in projection_must_outlive let recursive_bound = { let mut components = smallvec![]; - compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components, visited); - self.bound_from_components(&components, visited) + compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components); + self.bound_from_components(&components) }; VerifyBound::AnyBound(env_bounds.chain(definition_bounds).collect()).or(recursive_bound) } - fn bound_from_components( - &self, - components: &[Component<'tcx>], - visited: &mut SsoHashSet<GenericArg<'tcx>>, - ) -> VerifyBound<'tcx> { + fn bound_from_components(&self, components: &[Component<TyCtxt<'tcx>>]) -> VerifyBound<'tcx> { let mut bounds = components .iter() - .map(|component| self.bound_from_single_component(component, visited)) + .map(|component| self.bound_from_single_component(component)) // Remove bounds that must hold, since they are not interesting. .filter(|bound| !bound.must_hold()); @@ -158,8 +148,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn bound_from_single_component( &self, - component: &Component<'tcx>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, + component: &Component<TyCtxt<'tcx>>, ) -> VerifyBound<'tcx> { match *component { Component::Region(lt) => VerifyBound::OutlivedBy(lt), @@ -167,10 +156,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { Component::Placeholder(placeholder_ty) => { self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty)) } - Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited), - Component::EscapingAlias(ref components) => { - self.bound_from_components(components, visited) - } + Component::Alias(alias_ty) => self.alias_bound(alias_ty), + Component::EscapingAlias(ref components) => self.bound_from_components(components), Component::UnresolvedInferenceVariable(v) => { // Ignore this, we presume it will yield an error later, since // if a type variable is not resolved by this point it never diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 0a2e85cc891..c93b89756f9 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -18,11 +18,13 @@ //! On success, the LUB/GLB operations return the appropriate bound. The //! return value of `Equate` or `Sub` shouldn't really be used. +pub use rustc_next_trait_solver::relate::combine::*; + use super::glb::Glb; use super::lub::Lub; use super::type_relating::TypeRelating; +use super::RelateResult; use super::StructurallyRelateAliases; -use super::{RelateResult, TypeRelation}; use crate::infer::relate; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; use crate::traits::{Obligation, PredicateObligation}; @@ -32,7 +34,6 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast}; use rustc_middle::ty::{IntType, UintType}; -use rustc_span::Span; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -76,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> { b: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> where - R: PredicateEmittingRelation<'tcx>, + R: PredicateEmittingRelation<InferCtxt<'tcx>>, { debug_assert!(!a.has_escaping_bound_vars()); debug_assert!(!b.has_escaping_bound_vars()); @@ -171,7 +172,7 @@ impl<'tcx> InferCtxt<'tcx> { b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> where - R: PredicateEmittingRelation<'tcx>, + R: PredicateEmittingRelation<InferCtxt<'tcx>>, { debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); debug_assert!(!a.has_escaping_bound_vars()); @@ -323,30 +324,3 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { ) } } - -pub trait PredicateEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> { - fn span(&self) -> Span; - - fn param_env(&self) -> ty::ParamEnv<'tcx>; - - /// Whether aliases should be related structurally. This is pretty much - /// always `No` unless you're equating in some specific locations of the - /// new solver. See the comments in these use-cases for more details. - fn structurally_relate_aliases(&self) -> StructurallyRelateAliases; - - /// Register obligations that must hold in order for this relation to hold - fn register_goals( - &mut self, - obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, - ); - - /// Register predicates that must hold in order for this relation to hold. - /// This uses the default `param_env` of the obligation. - fn register_predicates( - &mut self, - obligations: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>, - ); - - /// Register `AliasRelate` obligation(s) that both types must be related to each other. - fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>); -} diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index d6e57d85387..fe3b8d60fb9 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -30,7 +30,7 @@ impl<'tcx> InferCtxt<'tcx> { /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all /// other usecases (i.e. setting the value of a type var). #[instrument(level = "debug", skip(self, relation))] - pub fn instantiate_ty_var<R: PredicateEmittingRelation<'tcx>>( + pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>( &self, relation: &mut R, target_is_expected: bool, @@ -178,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> { /// /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. #[instrument(level = "debug", skip(self, relation))] - pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'tcx>>( + pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>( &self, relation: &mut R, target_is_expected: bool, diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index 067004ecaeb..5bb8a113e17 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } } -impl<'tcx> PredicateEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Glb<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 6cc8d6d910a..46e7466141a 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty}; /// /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). -pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<'tcx> { +pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<InferCtxt<'tcx>> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 2184416b4cc..94c1464817f 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, } } -impl<'tcx> PredicateEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Lub<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 41cc945492d..dd97dc061fe 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -2,11 +2,13 @@ //! (except for some relations used for diagnostics and heuristics in the compiler). //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). -pub use rustc_middle::ty::relate::*; +pub use rustc_middle::ty::relate::RelateResult; +pub use rustc_next_trait_solver::relate::*; pub use self::combine::CombineFields; pub use self::combine::PredicateEmittingRelation; +#[allow(hidden_glob_reexports)] pub(super) mod combine; mod generalize; mod glb; diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index f2bec9392d5..e206f530519 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,7 +1,7 @@ use super::combine::CombineFields; use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases}; use crate::infer::BoundRegionConversionTime::HigherRankedType; -use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; +use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{ relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation, @@ -296,7 +296,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> { } } -impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 890e25368bc..7730fe29e09 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -12,15 +12,15 @@ use std::fmt; use std::iter; impl<'tcx> InferCtxt<'tcx> { - pub fn report_extra_impl_obligation( - &self, + pub fn report_extra_impl_obligation<'a>( + &'a self, error_span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId, requirement: &dyn fmt::Display, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let mut err = struct_span_code_err!( - self.tcx.dcx(), + self.dcx(), error_span, E0276, "impl has stricter requirements than trait" diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index ab4148faaab..f54d0418595 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,11 +1,10 @@ -use smallvec::smallvec; - -use crate::infer::outlives::components::{push_outlives_components, Component}; use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; -use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::ToPolyTraitRef; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::Ident; use rustc_span::Span; +pub use rustc_type_ir::elaborate::*; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -63,51 +62,9 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> { } } -/////////////////////////////////////////////////////////////////////////// -// `Elaboration` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Elaboration" is the process of identifying all the predicates that -/// are implied by a source predicate. Currently, this basically means -/// walking the "supertraits" and other similar assumptions. For example, -/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` -/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that -/// `T: Foo`, then we know that `T: 'static`. -pub struct Elaborator<'tcx, O> { - stack: Vec<O>, - visited: PredicateSet<'tcx>, - mode: Filter, -} - -enum Filter { - All, - OnlySelf, - OnlySelfThatDefines(Ident), -} - -/// Describes how to elaborate an obligation into a sub-obligation. -/// /// For [`Obligation`], a sub-obligation is combined with the current obligation's -/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since -/// there is no param-env or cause code to copy over. -pub trait Elaboratable<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx>; - - // Makes a new `Self` but with a different clause that comes from elaboration. - fn child(&self, clause: ty::Clause<'tcx>) -> Self; - - // Makes a new `Self` but with a different clause and a different cause - // code (if `Self` has one, such as [`PredicateObligation`]). - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - span: Span, - parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - index: usize, - ) -> Self; -} - -impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> { +/// param-env and cause code. +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for PredicateObligation<'tcx> { fn predicate(&self) -> ty::Predicate<'tcx> { self.predicate } @@ -145,324 +102,37 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> { } } -impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx> { - *self - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - clause.as_predicate() - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - clause.as_predicate() - } -} - -impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.0 - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - (clause.as_predicate(), self.1) - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - (clause.as_predicate(), self.1) - } -} - -impl<'tcx> Elaboratable<'tcx> for (ty::Clause<'tcx>, Span) { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.0.as_predicate() - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - (clause, self.1) - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - (clause, self.1) - } -} - -impl<'tcx> Elaboratable<'tcx> for ty::Clause<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.as_predicate() - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - clause - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - clause - } -} - -pub fn elaborate<'tcx, O: Elaboratable<'tcx>>( - tcx: TyCtxt<'tcx>, - obligations: impl IntoIterator<Item = O>, -) -> Elaborator<'tcx, O> { - let mut elaborator = - Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx), mode: Filter::All }; - elaborator.extend_deduped(obligations); - elaborator -} - -impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { - fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) { - // Only keep those bounds that we haven't already seen. - // This is necessary to prevent infinite recursion in some - // cases. One common case is when people define - // `trait Sized: Sized { }` rather than `trait Sized { }`. - // let visited = &mut self.visited; - self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate()))); - } - - /// Filter to only the supertraits of trait predicates, i.e. only the predicates - /// that have `Self` as their self type, instead of all implied predicates. - pub fn filter_only_self(mut self) -> Self { - self.mode = Filter::OnlySelf; - self - } - - /// Filter to only the supertraits of trait predicates that define the assoc_ty. - pub fn filter_only_self_that_defines(mut self, assoc_ty: Ident) -> Self { - self.mode = Filter::OnlySelfThatDefines(assoc_ty); - self - } - - fn elaborate(&mut self, elaboratable: &O) { - let tcx = self.visited.tcx; - - // We only elaborate clauses. - let Some(clause) = elaboratable.predicate().as_clause() else { - return; - }; - - let bound_clause = clause.kind(); - match bound_clause.skip_binder() { - ty::ClauseKind::Trait(data) => { - // Negative trait bounds do not imply any supertrait bounds - if data.polarity != ty::PredicatePolarity::Positive { - return; - } - // Get predicates implied by the trait, or only super predicates if we only care about self predicates. - let predicates = match self.mode { - Filter::All => tcx.implied_predicates_of(data.def_id()), - Filter::OnlySelf => tcx.super_predicates_of(data.def_id()), - Filter::OnlySelfThatDefines(ident) => { - tcx.super_predicates_that_define_assoc_item((data.def_id(), ident)) - } - }; - - let obligations = - predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| { - elaboratable.child_with_derived_cause( - clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)), - span, - bound_clause.rebind(data), - index, - ) - }); - debug!(?data, ?obligations, "super_predicates"); - self.extend_deduped(obligations); - } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { - // We know that `T: 'a` for some type `T`. We can - // often elaborate this. For example, if we know that - // `[U]: 'a`, that implies that `U: 'a`. Similarly, if - // we know `&'a U: 'b`, then we know that `'a: 'b` and - // `U: 'b`. - // - // We can basically ignore bound regions here. So for - // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to - // `'a: 'b`. - - // Ignore `for<'a> T: 'a` -- we might in the future - // consider this as evidence that `T: 'static`, but - // I'm a bit wary of such constructions and so for now - // I want to be conservative. --nmatsakis - if r_min.is_bound() { - return; - } - - let mut components = smallvec![]; - push_outlives_components(tcx, ty_max, &mut components); - self.extend_deduped( - components - .into_iter() - .filter_map(|component| match component { - Component::Region(r) => { - if r.is_bound() { - None - } else { - Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - r, r_min, - ))) - } - } - - Component::Param(p) => { - let ty = Ty::new_param(tcx, p.index, p.name); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) - } - - Component::Placeholder(p) => { - let ty = Ty::new_placeholder(tcx, p); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) - } - - Component::UnresolvedInferenceVariable(_) => None, - - Component::Alias(alias_ty) => { - // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. - // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - alias_ty.to_ty(tcx), - r_min, - ))) - } - - Component::EscapingAlias(_) => { - // We might be able to do more here, but we don't - // want to deal with escaping vars right now. - None - } - }) - .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(tcx))), - ); - } - ty::ClauseKind::RegionOutlives(..) => { - // Nothing to elaborate from `'a: 'b`. - } - ty::ClauseKind::WellFormed(..) => { - // Currently, we do not elaborate WF predicates, - // although we easily could. - } - ty::ClauseKind::Projection(..) => { - // Nothing to elaborate in a projection predicate. - } - ty::ClauseKind::ConstEvaluatable(..) => { - // Currently, we do not elaborate const-evaluatable - // predicates. - } - ty::ClauseKind::ConstArgHasType(..) => { - // Nothing to elaborate - } - } - } -} - -impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> { - type Item = O; - - fn size_hint(&self) -> (usize, Option<usize>) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option<Self::Item> { - // Extract next item from top-most stack frame, if any. - if let Some(obligation) = self.stack.pop() { - self.elaborate(&obligation); - Some(obligation) - } else { - None - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Supertrait iterator -/////////////////////////////////////////////////////////////////////////// - -pub fn supertraits<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> { - elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits() -} - -pub fn transitive_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, -) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> { - elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx))) - .filter_only_self() - .filter_to_traits() -} - /// A specialized variant of `elaborate` that only elaborates trait references that may /// define the given associated item with the name `assoc_name`. It uses the -/// `super_predicates_that_define_assoc_item` query to avoid enumerating super-predicates that +/// `explicit_supertraits_containing_assoc_item` query to avoid enumerating super-predicates that /// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or /// `T::Item` and helps to avoid cycle errors (see e.g. #35237). pub fn transitive_bounds_that_define_assoc_item<'tcx>( tcx: TyCtxt<'tcx>, trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, assoc_name: Ident, -) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> { - elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx))) - .filter_only_self_that_defines(assoc_name) - .filter_to_traits() -} - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -impl<'tcx> Elaborator<'tcx, ty::Predicate<'tcx>> { - fn filter_to_traits(self) -> FilterToTraits<Self> { - FilterToTraits { base_iterator: self } - } -} - -/// A filter around an iterator of predicates that makes it yield up -/// just trait references. -pub struct FilterToTraits<I> { - base_iterator: I, -} +) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> { + let mut seen = FxHashSet::default(); + let mut stack: Vec<_> = trait_refs.collect(); + + std::iter::from_fn(move || { + while let Some(trait_ref) = stack.pop() { + if !seen.insert(tcx.anonymize_bound_vars(trait_ref)) { + continue; + } -impl<'tcx, I: Iterator<Item = ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> { - type Item = ty::PolyTraitRef<'tcx>; + stack.extend( + tcx.explicit_supertraits_containing_assoc_item((trait_ref.def_id(), assoc_name)) + .instantiate_own_identity() + .map(|(clause, _)| clause.instantiate_supertrait(tcx, trait_ref)) + .filter_map(|clause| clause.as_trait_clause()) + // FIXME: Negative supertraits are elaborated here lol + .map(|trait_pred| trait_pred.to_poly_trait_ref()), + ); - fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> { - while let Some(pred) = self.base_iterator.next() { - if let Some(data) = pred.as_trait_clause() { - return Some(data.map_bound(|t| t.trait_ref)); - } + return Some(trait_ref); } - None - } - fn size_hint(&self) -> (usize, Option<usize>) { - let (_, upper) = self.base_iterator.size_hint(); - (0, upper) - } + None + }) } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 38f64ebb04e..e37b30749ab 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -16,7 +16,7 @@ pub mod util; pub use callbacks::setup_callbacks; pub use interface::{run_compiler, Config}; pub use passes::DEFAULT_QUERY_PROVIDERS; -pub use queries::Queries; +pub use queries::{Linker, Queries}; #[cfg(test)] mod tests; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index cfd4304e893..821e8ee7ba5 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -65,12 +65,6 @@ impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> { } } -impl<T> Default for Query<T> { - fn default() -> Self { - Query { result: RefCell::new(None) } - } -} - pub struct Queries<'tcx> { compiler: &'tcx Compiler, gcx_cell: OnceLock<GlobalCtxt<'tcx>>, @@ -90,8 +84,8 @@ impl<'tcx> Queries<'tcx> { gcx_cell: OnceLock::new(), arena: WorkerLocal::new(|_| Arena::default()), hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()), - parse: Default::default(), - gcx: Default::default(), + parse: Query { result: RefCell::new(None) }, + gcx: Query { result: RefCell::new(None) }, } } @@ -116,23 +110,6 @@ impl<'tcx> Queries<'tcx> { ) }) } - - pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> { - self.global_ctxt()?.enter(|tcx| { - let ongoing_codegen = passes::start_codegen(&*self.compiler.codegen_backend, tcx)?; - - Ok(Linker { - dep_graph: tcx.dep_graph.clone(), - output_filenames: tcx.output_filenames(()).clone(), - crate_hash: if tcx.needs_crate_hash() { - Some(tcx.crate_hash(LOCAL_CRATE)) - } else { - None - }, - ongoing_codegen, - }) - }) - } } pub struct Linker { @@ -144,6 +121,36 @@ pub struct Linker { } impl Linker { + pub fn codegen_and_build_linker( + tcx: TyCtxt<'_>, + codegen_backend: &dyn CodegenBackend, + ) -> Result<Linker> { + let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?; + + // This must run after monomorphization so that all generic types + // have been instantiated. + if tcx.sess.opts.unstable_opts.print_type_sizes { + tcx.sess.code_stats.print_type_sizes(); + } + + if tcx.sess.opts.unstable_opts.print_vtable_sizes { + let crate_name = tcx.crate_name(LOCAL_CRATE); + + tcx.sess.code_stats.print_vtable_sizes(crate_name); + } + + Ok(Linker { + dep_graph: tcx.dep_graph.clone(), + output_filenames: tcx.output_filenames(()).clone(), + crate_hash: if tcx.needs_crate_hash() { + Some(tcx.crate_hash(LOCAL_CRATE)) + } else { + None + }, + ongoing_codegen, + }) + } + pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> { let (codegen_results, work_products) = codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames); @@ -197,7 +204,7 @@ impl Compiler { F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T, { // Must declare `_timer` first so that it is dropped after `queries`. - let mut _timer = None; + let _timer; let queries = Queries::new(self); let ret = f(&queries); @@ -220,7 +227,7 @@ impl Compiler { // The timer's lifetime spans the dropping of `queries`, which contains // the global context. - _timer = Some(self.sess.timer("free_global_ctxt")); + _timer = self.sess.timer("free_global_ctxt"); if let Err((path, error)) = queries.finish() { self.sess.dcx().emit_fatal(errors::FailedWritingFile { path: &path, error }); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6ffc518097e..e2ba75dfd19 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -2,14 +2,22 @@ use crate::interface::{initialize_checked_jobserver, parse_cfg}; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_session::config::{build_configuration, build_session_options, rustc_optgroups}; use rustc_session::config::{ - build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg, - CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, - ErrorOutputType, ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, - Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, - LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, - PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, + BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, + DebugInfo, DumpMonoStatsFormat, ErrorOutputType, +}; +use rustc_session::config::{ + ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, Input, + InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, +}; +use rustc_session::config::{ + LocationDetail, LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, + OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, +}; +use rustc_session::config::{ + Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -749,7 +757,6 @@ fn test_unstable_options_tracking_hash() { // tidy-alphabetical-start tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); - tracked!(asm_comments, true); tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); @@ -813,6 +820,11 @@ fn test_unstable_options_tracking_hash() { tracked!(packed_bundled_libs, true); tracked!(panic_abort_tests, true); tracked!(panic_in_drop, PanicStrategy::Abort); + tracked!( + patchable_function_entry, + PatchableFunctionEntry::from_total_and_prefix_nops(10, 5) + .expect("total must be greater than or equal to prefix") + ); tracked!(plt, Some(true)); tracked!(polonius, Polonius::Legacy); tracked!(precise_enum_drop_elaboration, false); @@ -849,6 +861,7 @@ fn test_unstable_options_tracking_hash() { tracked!(uninit_const_chunk_threshold, 123); tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); + tracked!(verbose_asm, true); tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 46cf87d1e3c..f048f6fe8ad 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -56,8 +56,6 @@ lint_builtin_asm_labels = avoid using named labels in inline assembly .help = only local labels of the form `<number>:` should be used in inline asm .note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information -lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty} - lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature .previous_decl_label = `{$orig}` previously declared here .mismatch_label = this signature doesn't match the previous declaration @@ -441,6 +439,9 @@ lint_lintpass_by_hand = implementing `LintPass` by hand lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths .note = the macro is defined here +lint_macro_expr_fragment_specifier_2024_migration = + the `expr` fragment specifier will accept more expressions in the 2024 edition + .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier lint_macro_is_private = macro `{$ident}` is private lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index 40778542c75..6daee95dda6 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -3,7 +3,7 @@ use crate::LateContext; use crate::LateLintPass; use rustc_hir as hir; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait; +use rustc_trait_selection::error_reporting::traits::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait; declare_lint! { /// The `async_fn_in_trait` lint detects use of `async fn` in the diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 98318cd14d9..79c8046f9b7 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -24,9 +24,9 @@ use crate::fluent_generated as fluent; use crate::{ errors::BuiltinEllipsisInclusiveRangePatterns, lints::{ - BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle, - BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, - BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, + BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, + BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, + BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, @@ -56,7 +56,6 @@ use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::Upcast; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; @@ -135,80 +134,6 @@ impl EarlyLintPass for WhileTrue { } declare_lint! { - /// The `box_pointers` lints use of the Box type. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(box_pointers)] - /// struct Foo { - /// x: Box<i32>, - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is mostly historical, and not particularly useful. `Box<T>` - /// used to be built into the language, and the only way to do heap - /// allocation. Today's Rust can call into other allocators, etc. - BOX_POINTERS, - Allow, - "use of owned (Box type) heap memory" -} - -declare_lint_pass!(BoxPointers => [BOX_POINTERS]); - -impl BoxPointers { - fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { - for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() - && leaf_ty.is_box() - { - cx.emit_span_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty }); - } - } - } -} - -impl<'tcx> LateLintPass<'tcx> for BoxPointers { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) => self.check_heap_type( - cx, - it.span, - cx.tcx.type_of(it.owner_id).instantiate_identity(), - ), - _ => (), - } - - // If it's a struct, we also have to check the fields' types - match it.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - for field in struct_def.fields() { - self.check_heap_type( - cx, - field.span, - cx.tcx.type_of(field.def_id).instantiate_identity(), - ); - } - } - _ => (), - } - } - - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { - let ty = cx.typeck_results().node_type(e.hir_id); - self.check_heap_type(cx, e.span, ty); - } -} - -declare_lint! { /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }` /// instead of `Struct { x }` in a pattern. /// @@ -1640,7 +1565,6 @@ declare_lint_pass!( /// which are used by other parts of the compiler. SoftLints => [ WHILE_TRUE, - BOX_POINTERS, NON_SHORTHAND_FIELD_PATTERNS, UNSAFE_CODE, MISSING_DOCS, diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs index c69e680cb64..da36f68fca9 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -261,10 +261,16 @@ pub(super) fn unexpected_cfg_value( lints::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } }; - // We don't want to suggest adding values to well known names - // since those are defined by rustc it-self. Users can still - // do it if they want, but should not encourage them. - let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); + // We don't want to encourage people to add values to a well-known names, as these are + // defined by rustc/Rust itself. Users can still do this if they wish, but should not be + // encouraged to do so. + let can_suggest_adding_value = !sess.psess.check_config.well_known_names.contains(&name) + // Except when working on rustc or the standard library itself, in which case we want to + // suggest adding these cfgs to the "normal" place because of bootstraping reasons. As a + // basic heuristic, we use the "cheat" unstable feature enable method and the + // non-ui-testing enabled option. + || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat) + && !sess.opts.unstable_opts.ui_testing); let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); @@ -275,14 +281,14 @@ pub(super) fn unexpected_cfg_value( } else { Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures) } - } else if !is_cfg_a_well_know_name { + } else if can_suggest_adding_value { Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) } else { None }; lints::unexpected_cfg_value::InvocationHelp::Cargo(help) } else { - let help = if !is_cfg_a_well_know_name { + let help = if can_suggest_adding_value { Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) } else { None diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 9110cccdc46..772cc2ff8b9 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -88,7 +88,7 @@ declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]); impl LateLintPass<'_> for QueryStability { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; - if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, args) { + if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, args) { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { cx.emit_span_lint( @@ -393,7 +393,7 @@ impl LateLintPass<'_> for Diagnostics { }; // Is the callee marked with `#[rustc_lint_diagnostics]`? - let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, fn_gen_args) + let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args) .ok() .flatten() .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 7dae2de7bfb..868a44a980a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -60,6 +60,7 @@ mod late; mod let_underscore; mod levels; mod lints; +mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod methods; mod multiple_supertrait_upcastable; @@ -97,6 +98,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures; use internal::*; use invalid_from_utf8::*; use let_underscore::*; +use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use methods::*; use multiple_supertrait_upcastable::*; @@ -170,6 +172,7 @@ early_lint_methods!( IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, + Expr2024: Expr2024, ] ] ); @@ -187,7 +190,6 @@ late_lint_methods!( ImproperCTypesDefinitions: ImproperCTypesDefinitions, InvalidFromUtf8: InvalidFromUtf8, VariantSizeDifferences: VariantSizeDifferences, - BoxPointers: BoxPointers, PathStatements: PathStatements, LetUnderscore: LetUnderscore, InvalidReferenceCasting: InvalidReferenceCasting, @@ -551,6 +553,10 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see RFC #3535 \ <https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information", ); + store.register_removed( + "box_pointers", + "it does not detect other kinds of allocations, and existed only for historical reasons", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 14084405d0e..54c73710eca 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -67,12 +67,6 @@ pub struct BuiltinWhileTrue { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_box_pointers)] -pub struct BuiltinBoxPointers<'a> { - pub ty: Ty<'a>, -} - -#[derive(LintDiagnostic)] #[diag(lint_builtin_non_shorthand_field_patterns)] pub struct BuiltinNonShorthandFieldPatterns { pub ident: Ident, @@ -323,6 +317,13 @@ pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, } +#[derive(LintDiagnostic)] +#[diag(lint_macro_expr_fragment_specifier_2024_migration)] +pub struct MacroExprFragment2024 { + #[suggestion(code = "expr_2021", applicability = "machine-applicable")] + pub suggestion: Span, +} + pub struct BuiltinTypeAliasGenericBoundsSuggestion { pub suggestions: Vec<(Span, String)>, } diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs new file mode 100644 index 00000000000..867e132b106 --- /dev/null +++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs @@ -0,0 +1,155 @@ +//! Migration code for the `expr_fragment_specifier_2024` +//! rule. +use tracing::debug; + +use rustc_ast::token::Token; +use rustc_ast::token::TokenKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::tokenstream::TokenTree; +use rustc_session::declare_lint; +use rustc_session::declare_lint_pass; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_span::edition::Edition; +use rustc_span::sym; + +use crate::lints::MacroExprFragment2024; +use crate::EarlyLintPass; + +declare_lint! { + /// The `edition_2024_expr_fragment_specifier` lint detects the use of + /// `expr` fragments in macros during migration to the 2024 edition. + /// + /// The `expr` fragment specifier will accept more expressions in the 2024 + /// edition. To maintain the behavior from the 2021 edition and earlier, use + /// the `expr_2021` fragment specifier. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// #![deny(edition_2024_expr_fragment_specifier)] + /// macro_rules! m { + /// ($e:expr) => { + /// $e + /// } + /// } + /// + /// fn main() { + /// m!(1); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking backwards + /// compatibility. This lint catches code that uses [macro matcher fragment + /// specifiers] that have changed meaning in the 2024 edition. If you switch + /// to the new edition without updating the code, your macros may behave + /// differently. + /// + /// In the 2024 edition, the `expr` fragment specifier `expr` will also + /// match `const { ... }` blocks. This means if a macro had a pattern that + /// matched `$e:expr` and another that matches `const { $e: expr }`, for + /// example, that under the 2024 edition the first pattern would match while + /// in the 2021 and earlier editions the second pattern would match. To keep + /// the old behavior, use the `expr_2021` fragment specifier. + /// + /// This lint detects macros whose behavior might change due to the changing + /// meaning of the `expr` fragment specifier. It is "allow" by default + /// because the code is perfectly valid in older editions. The [`cargo fix`] + /// tool with the `--edition` flag will switch this lint to "warn" and + /// automatically apply the suggested fix from the compiler. This provides a + /// completely automated way to update old code for a new edition. + /// + /// Using `cargo fix --edition` with this lint will ensure that your code + /// retains the same behavior. This may not be the desired, as macro authors + /// often will want their macros to use the latest grammar for matching + /// expressions. Be sure to carefully review changes introduced by this lint + /// to ensure the macros implement the desired behavior. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html + pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER, + Allow, + "The `expr` fragment specifier will accept more expressions in the 2024 edition. \ + To keep the existing behavior, use the `expr_2021` fragment specifier.", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>", + }; +} + +declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]); + +impl Expr2024 { + fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) { + let mut prev_colon = false; + let mut prev_identifier = false; + let mut prev_dollar = false; + for tt in tokens.trees() { + debug!( + "check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}", + tt + ); + match tt { + TokenTree::Token(token, _) => match token.kind { + TokenKind::Dollar => { + prev_dollar = true; + continue; + } + TokenKind::Ident(..) | TokenKind::NtIdent(..) => { + if prev_colon && prev_identifier && prev_dollar { + self.check_ident_token(cx, token); + } else if prev_dollar { + prev_identifier = true; + continue; + } + } + TokenKind::Colon => { + if prev_dollar && prev_identifier { + prev_colon = true; + continue; + } + } + _ => {} + }, + TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts), + } + prev_colon = false; + prev_identifier = false; + prev_dollar = false; + } + } + + fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) { + debug!("check_ident_token: {:?}", token); + let (sym, edition) = match token.kind { + TokenKind::Ident(sym, _) => (sym, Edition::Edition2024), + _ => return, + }; + + debug!("token.span.edition(): {:?}", token.span.edition()); + if token.span.edition() >= edition { + return; + } + + if sym != sym::expr { + return; + } + + debug!("emitting lint"); + cx.builder.emit_span_lint( + &EDITION_2024_EXPR_FRAGMENT_SPECIFIER, + token.span.into(), + MacroExprFragment2024 { suggestion: token.span }, + ); + } +} + +impl EarlyLintPass for Expr2024 { + fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) { + self.check_tokens(cx, &mc.body.tokens); + } +} diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index aa1d94228ea..445dcd41e5d 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { { let direct_super_traits_iter = cx .tcx - .super_predicates_of(def_id) + .explicit_super_predicates_of(def_id) .predicates .into_iter() .filter_map(|(pred, _)| pred.as_trait_clause()); diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 300dac442d5..2f8eea6cd18 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -11,10 +11,10 @@ use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::Span; use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind, Symbol}; -use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_trait_selection::traits::error_reporting::ambiguity::{ +use rustc_trait_selection::error_reporting::traits::ambiguity::{ compute_applicable_impls_for_diagnostics, CandidateSource, }; +use rustc_trait_selection::infer::TyCtxtInferExt; use crate::fluent_generated as fluent; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; @@ -50,7 +50,7 @@ declare_lint! { /// All nested bodies (functions, enum discriminant, array length, consts) (expect for /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked. pub NON_LOCAL_DEFINITIONS, - Warn, + Allow, "checks for non-local definitions", report_in_external_macro } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 91441248e70..307e4bebe9a 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -96,7 +96,9 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { .tcx .normalize_erasing_regions(cx.param_env, cx.typeck_results().node_args(expr.hir_id)); // Resolve the trait method instance. - let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, args) else { return }; + let Ok(Some(i)) = ty::Instance::try_resolve(cx.tcx, cx.param_env, did, args) else { + return; + }; // (Re)check that it implements the noop diagnostic. let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; if !matches!( diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 8b669bcc13f..5ee73dbfdc6 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -76,9 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // For every projection predicate in the opaque type's explicit bounds, // check that the type that we're assigning actually satisfies the bounds // of the associated type. - for (pred, pred_span) in - cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() - { + for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).iter_identity_copied() { infcx.enter_forall(pred.kind(), |predicate| { let ty::ClauseKind::Projection(proj) = predicate else { return; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 195a0f72475..65d42ed8054 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -298,9 +298,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate( cx.tcx, - cx.tcx - .explicit_item_super_predicates(def) - .instantiate_identity_iter_copied(), + cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), ) // We only care about self bounds for the impl-trait .filter_only_self() diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 472e93d202d..48e20a586c6 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -74,11 +74,13 @@ declare_lint_pass! { NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, ORDER_DEPENDENT_TRAIT_OBJECTS, + OUT_OF_SCOPE_MACRO_CALLS, OVERLAPPING_RANGE_ENDPOINTS, PATTERNS_IN_FNS_WITHOUT_BODY, PRIVATE_BOUNDS, PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + PTR_CAST_ADD_AUTO_TO_OBJECT, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, @@ -4188,6 +4190,7 @@ declare_lint! { reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>", }; + @edition Edition2024 => Deny; report_in_external_macro } @@ -4617,7 +4620,7 @@ declare_lint! { /// [against]: https://github.com/rust-lang/rust/issues/38831 /// [future-incompatible]: ../index.md#future-incompatible-lints pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, - Warn, + Deny, "elided lifetimes cannot be used in associated constants in impls", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, @@ -4937,6 +4940,58 @@ declare_lint! { } declare_lint! { + /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait + /// objects, which add auto traits. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// let ptr: *const dyn core::any::Any = &(); + /// _ = ptr as *const dyn core::any::Any + Send; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Adding an auto trait can make the vtable invalid, potentially causing + /// UB in safe code afterwards. For example: + /// + /// ```ignore (causes a warning) + /// #![feature(arbitrary_self_types)] + /// + /// trait Trait { + /// fn f(self: *const Self) + /// where + /// Self: Send; + /// } + /// + /// impl Trait for *const () { + /// fn f(self: *const Self) { + /// unreachable!() + /// } + /// } + /// + /// fn main() { + /// let unsend: *const () = &(); + /// let unsend: *const dyn Trait = &unsend; + /// let send_bad: *const (dyn Trait + Send) = unsend as _; + /// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f` + /// } + /// ``` + /// + /// Generally you must ensure that vtable is right for the pointer's type, + /// before passing the pointer to safe code. + pub PTR_CAST_ADD_AUTO_TO_OBJECT, + Warn, + "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + reference: "issue #127323 <https://github.com/rust-lang/rust/issues/127323>", + }; +} + +declare_lint! { /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope, /// above their definition, which may happen in key-value attributes. /// diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index c4cfc0b6dc6..283c4fbbb7c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -407,7 +407,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, bool FunctionSections, bool DataSections, bool UniqueSectionNames, - bool TrapUnreachable, bool Singlethread, bool AsmComments, + bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, const char *DebugInfoCompression, bool UseEmulatedTls, @@ -435,8 +435,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.DataSections = DataSections; Options.FunctionSections = FunctionSections; Options.UniqueSectionNames = UniqueSectionNames; - Options.MCOptions.AsmVerbose = AsmComments; - Options.MCOptions.PreserveAsmComments = AsmComments; + Options.MCOptions.AsmVerbose = VerboseAsm; + // Always preserve comments that were written by the user + Options.MCOptions.PreserveAsmComments = true; Options.MCOptions.ABIName = ABIStr; if (SplitDwarfFile) { Options.MCOptions.SplitDwarfFile = SplitDwarfFile; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index ea7037740f1..83b41e0540e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1348,7 +1348,9 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem { - let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some() { + let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some() + || self.root.tables.is_effects_desugaring.get(self, id) + { kw::Empty } else { self.item_name(id) @@ -1371,6 +1373,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { container, fn_has_self_parameter: has_self, opt_rpitit_info, + is_effects_desugaring: self.root.tables.is_effects_desugaring.get(self, id), } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c9450142cd3..6b240f0f0b3 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -211,8 +211,8 @@ provide! { tcx, def_id, other, cdata, explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } - super_predicates_of => { table } - implied_predicates_of => { table } + explicit_super_predicates_of => { table } + explicit_implied_predicates_of => { table } type_of => { table } type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) } variances_of => { table } @@ -279,6 +279,9 @@ provide! { tcx, def_id, other, cdata, .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } + associated_type_for_effects => { + table + } associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array } visibility => { cdata.get_visibility(def_id.index) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4bd2ec09a6e..209316ca20f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1431,8 +1431,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::Trait = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id)); - record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id)); + record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id)); + record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id)); let module_children = self.tcx.module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- @@ -1440,8 +1440,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::TraitAlias = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); - record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id)); - record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id)); + record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id)); + record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id)); } if let DefKind::Trait | DefKind::Impl { .. } = def_kind { let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id); @@ -1454,6 +1454,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for &def_id in associated_item_def_ids { self.encode_info_for_assoc_item(def_id); } + if let Some(assoc_def_id) = self.tcx.associated_type_for_effects(def_id) { + record!(self.tables.associated_type_for_effects[def_id] <- assoc_def_id); + } } if def_kind == DefKind::Closure && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) @@ -1634,6 +1637,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ); } } + if item.is_effects_desugaring { + self.tables.is_effects_desugaring.set(def_id.index, true); + } } fn encode_mir(&mut self) { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 87900c23d8d..2a44b3423ae 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -391,7 +391,9 @@ define_tables! { inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>, + associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>, opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>, + is_effects_desugaring: Table<DefIndex, bool>, unused_generic_params: Table<DefIndex, UnusedGenericParams>, // Reexported names are not associated with individual `DefId`s, // e.g. a glob import can introduce a lot of names, all with the same `DefId`. @@ -416,10 +418,10 @@ define_tables! { lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>, explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, generics_of: Table<DefIndex, LazyValue<ty::Generics>>, - super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, + explicit_super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, // As an optimization, we only store this for trait aliases, - // since it's identical to super_predicates_of for traits. - implied_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, + // since it's identical to explicit_super_predicates_of for traits. + explicit_implied_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>, type_of: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, Ty<'static>>>>, variances_of: Table<DefIndex, LazyArray<ty::Variance>>, fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>, diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index f4d619329eb..2b9d9a07a98 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -41,6 +41,9 @@ middle_cannot_be_normalized = middle_conflict_types = this expression supplies two conflicting concrete types for the same opaque type +middle_consider_type_length_limit = + consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate + middle_const_eval_non_int = constant evaluation of enum discriminant resulted in non-integer @@ -94,8 +97,11 @@ middle_strict_coherence_needs_negative_coherence = to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled .label = due to this attribute +middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` + middle_unknown_layout = the type `{$ty}` has an unknown layout middle_values_too_big = values of the type `{$ty}` are too big for the current architecture +middle_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 6e622b0405f..711db4e0a6b 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::path::PathBuf; use rustc_errors::{codes::*, DiagArgName, DiagArgValue, DiagMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -149,3 +150,16 @@ pub struct ErroneousConstant { /// Used by `rustc_const_eval` pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error; + +#[derive(Diagnostic)] +#[diag(middle_type_length_limit)] +#[help(middle_consider_type_length_limit)] +pub struct TypeLengthLimit { + #[primary_span] + pub span: Span, + pub shrunk: String, + #[note(middle_written_to_path)] + pub was_written: Option<()>, + pub path: PathBuf, + pub type_length: usize, +} diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 305ba1ef3bb..2f3a6ee601b 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -31,9 +31,18 @@ pub struct Map<'hir> { /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir().parent_iter(hir_id)`. -pub struct ParentHirIterator<'hir> { +struct ParentHirIterator<'hir> { current_id: HirId, map: Map<'hir>, + // Cache the current value of `hir_owner_nodes` to avoid repeatedly calling the same query for + // the same owner, which will uselessly record many times the same query dependency. + current_owner_nodes: Option<&'hir OwnerNodes<'hir>>, +} + +impl<'hir> ParentHirIterator<'hir> { + fn new(map: Map<'hir>, current_id: HirId) -> ParentHirIterator<'hir> { + ParentHirIterator { current_id, map, current_owner_nodes: None } + } } impl<'hir> Iterator for ParentHirIterator<'hir> { @@ -44,13 +53,22 @@ impl<'hir> Iterator for ParentHirIterator<'hir> { return None; } - // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.tcx.parent_hir_id(self.current_id); + let HirId { owner, local_id } = self.current_id; - if parent_id == self.current_id { - self.current_id = CRATE_HIR_ID; - return None; - } + let parent_id = if local_id == ItemLocalId::ZERO { + // We go from an owner to its parent, so clear the cache. + self.current_owner_nodes = None; + self.map.tcx.hir_owner_parent(owner) + } else { + let owner_nodes = + self.current_owner_nodes.get_or_insert_with(|| self.map.tcx.hir_owner_nodes(owner)); + let parent_local_id = owner_nodes.nodes[local_id].parent; + // HIR indexing should have checked that. + debug_assert_ne!(parent_local_id, local_id); + HirId { owner, local_id: parent_local_id } + }; + + debug_assert_ne!(parent_id, self.current_id); self.current_id = parent_id; return Some(parent_id); @@ -479,7 +497,7 @@ impl<'hir> Map<'hir> { /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] pub fn parent_id_iter(self, current_id: HirId) -> impl Iterator<Item = HirId> + 'hir { - ParentHirIterator { current_id, map: self } + ParentHirIterator::new(self, current_id) } /// Returns an iterator for the nodes in the ancestor tree of the `current_id` diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index c8f0d0795a3..3ddf889b63a 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -45,6 +45,32 @@ pub struct CodegenFnAttrs { /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be /// aligned to. pub alignment: Option<Align>, + /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around + /// the function entry. + pub patchable_function_entry: Option<PatchableFunctionEntry>, +} + +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct PatchableFunctionEntry { + /// Nops to prepend to the function + prefix: u8, + /// Nops after entry, but before body + entry: u8, +} + +impl PatchableFunctionEntry { + pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self { + Self { prefix: config.prefix(), entry: config.entry() } + } + pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self { + Self { prefix, entry } + } + pub fn prefix(&self) -> u8 { + self.prefix + } + pub fn entry(&self) -> u8 { + self.entry + } } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] @@ -121,6 +147,7 @@ impl CodegenFnAttrs { no_sanitize: SanitizerSet::empty(), instruction_set: None, alignment: None, + patchable_function_entry: None, } } diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs index e7d0cffc85c..a3aff9a1101 100644 --- a/compiler/rustc_middle/src/middle/dependency_format.rs +++ b/compiler/rustc_middle/src/middle/dependency_format.rs @@ -4,15 +4,15 @@ //! For all the gory details, see the provider of the `dependency_formats` //! query. +// FIXME: move this file to rustc_metadata::dependency_format, but +// this will introduce circular dependency between rustc_metadata and rustc_middle + use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::config::CrateType; /// A list of dependencies for a certain crate type. /// /// The length of this vector is the same as the number of external crates used. -/// The value is None if the crate does not need to be linked (it was found -/// statically in another dylib), or Some(kind) if it needs to be linked as -/// `kind` (either static or dynamic). pub type DependencyList = Vec<Linkage>; /// A mapping of all required dependencies for a particular flavor of output. diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index e76d7af6e4a..a0c9af436e2 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -27,6 +27,10 @@ impl<'tcx> TyCtxt<'tcx> { self.lang_items().get(lang_item) == Some(def_id) } + pub fn as_lang_item(self, def_id: DefId) -> Option<LangItem> { + self.lang_items().from_def_id(def_id) + } + /// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits, /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 4d698012749..d0b4f36a426 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -30,7 +30,7 @@ pub fn provide(providers: &mut Providers) { tcx.hir().krate_attrs(), tcx.sess, sym::type_length_limit, - 1048576, + 2usize.pow(24), ), } } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index da25fbb0a82..beaaadd497d 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -103,7 +103,7 @@ pub enum CoverageKind { SpanMarker, /// Marks its enclosing basic block with an ID that can be referred to by - /// side data in [`BranchInfo`]. + /// side data in [`CoverageInfoHi`]. /// /// Should be erased before codegen (at some point after `InstrumentCoverage`). BlockMarker { id: BlockMarkerId }, @@ -274,10 +274,15 @@ pub struct FunctionCoverageInfo { pub mcdc_num_condition_bitmaps: usize, } -/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR. +/// Coverage information for a function, recorded during MIR building and +/// attached to the corresponding `mir::Body`. Used by the `InstrumentCoverage` +/// MIR pass. +/// +/// ("Hi" indicates that this is "high-level" information collected at the +/// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.) #[derive(Clone, Debug)] #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub struct BranchInfo { +pub struct CoverageInfoHi { /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was /// injected into the MIR body. This makes it possible to allocate per-ID /// data structures without having to scan the entire body first. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 16093cfca6a..4e95e600b5a 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -18,6 +18,7 @@ use smallvec::{smallvec, SmallVec}; use tracing::{debug, trace}; use rustc_ast::LitKind; +use rustc_attr::InlineAttr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_errors::ErrorGuaranteed; @@ -134,10 +135,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>( AllocDiscriminant::Alloc.encode(encoder); alloc.encode(encoder); } - GlobalAlloc::Function(fn_instance) => { - trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + GlobalAlloc::Function { instance, unique } => { + trace!("encoding {:?} with {:#?}", alloc_id, instance); AllocDiscriminant::Fn.encode(encoder); - fn_instance.encode(encoder); + instance.encode(encoder); + unique.encode(encoder); } GlobalAlloc::VTable(ty, poly_trait_ref) => { trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id); @@ -285,7 +287,12 @@ impl<'s> AllocDecodingSession<'s> { trace!("creating fn alloc ID"); let instance = ty::Instance::decode(decoder); trace!("decoded fn alloc instance: {:?}", instance); - let alloc_id = decoder.interner().reserve_and_set_fn_alloc(instance); + let unique = bool::decode(decoder); + // Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which + // is not possible in this context. That's why the allocation stores + // whether it is unique or not. + let alloc_id = + decoder.interner().reserve_and_set_fn_alloc_internal(instance, unique); alloc_id } AllocDiscriminant::VTable => { @@ -323,7 +330,12 @@ impl<'s> AllocDecodingSession<'s> { #[derive(Debug, Clone, Eq, PartialEq, Hash, TyDecodable, TyEncodable, HashStable)] pub enum GlobalAlloc<'tcx> { /// The alloc ID is used as a function pointer. - Function(Instance<'tcx>), + Function { + instance: Instance<'tcx>, + /// Stores whether this instance is unique, i.e. all pointers to this function use the same + /// alloc ID. + unique: bool, + }, /// This alloc ID points to a symbolic (not-reified) vtable. VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), /// The alloc ID points to a "lazy" static variable that did not get computed (yet). @@ -349,7 +361,7 @@ impl<'tcx> GlobalAlloc<'tcx> { #[inline] pub fn unwrap_fn(&self) -> Instance<'tcx> { match *self { - GlobalAlloc::Function(instance) => instance, + GlobalAlloc::Function { instance, .. } => instance, _ => bug!("expected function, got {:?}", self), } } @@ -368,7 +380,7 @@ impl<'tcx> GlobalAlloc<'tcx> { #[inline] pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { match self { - GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space, + GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { AddressSpace::DATA } @@ -426,7 +438,7 @@ impl<'tcx> TyCtxt<'tcx> { fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { let mut alloc_map = self.alloc_map.lock(); match alloc { - GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {} + GlobalAlloc::Function { .. } | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {} GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), } if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { @@ -445,30 +457,45 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) } + /// Generates an `AllocId` for a function. The caller must already have decided whether this + /// function obtains a unique AllocId or gets de-duplicated via the cache. + fn reserve_and_set_fn_alloc_internal(self, instance: Instance<'tcx>, unique: bool) -> AllocId { + let alloc = GlobalAlloc::Function { instance, unique }; + if unique { + // Deduplicate. + self.reserve_and_set_dedup(alloc) + } else { + // Get a fresh ID. + let mut alloc_map = self.alloc_map.lock(); + let id = alloc_map.reserve(); + alloc_map.alloc_map.insert(id, alloc); + id + } + } + /// Generates an `AllocId` for a function. Depending on the function type, /// this might get deduplicated or assigned a new ID each time. pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId { // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be - // duplicated across crates. - // We thus generate a new `AllocId` for every mention of a function. This means that - // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. - // However, formatting code relies on function identity (see #58320), so we only do - // this for generic functions. Lifetime parameters are ignored. + // duplicated across crates. We thus generate a new `AllocId` for every mention of a + // function. This means that `main as fn() == main as fn()` is false, while `let x = main as + // fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify + // certain functions uniquely, e.g. for backtraces. So we identify whether codegen will + // actually emit duplicate functions. It does that when they have non-lifetime generics, or + // when they can be inlined. All other functions are given a unique address. + // This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied + // upon for anything. But if we don't do this, backtraces look terrible. let is_generic = instance .args .into_iter() .any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_))); - if is_generic { - // Get a fresh ID. - let mut alloc_map = self.alloc_map.lock(); - let id = alloc_map.reserve(); - alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance)); - id - } else { - // Deduplicate. - self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) - } + let can_be_inlined = match self.codegen_fn_attrs(instance.def_id()).inline { + InlineAttr::Never => false, + _ => true, + }; + let unique = !is_generic && !can_be_inlined; + self.reserve_and_set_fn_alloc_internal(instance, unique) } /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated. diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 95857e8579d..96613592bbc 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -73,7 +73,7 @@ impl<'tcx> TyCtxt<'tcx> { bug!("did not expect inference variables here"); } - match ty::Instance::resolve( + match ty::Instance::try_resolve( self, param_env, // FIXME: maybe have a separate version for resolving mir::UnevaluatedConst? ct.def, ct.args, @@ -106,7 +106,7 @@ impl<'tcx> TyCtxt<'tcx> { bug!("did not expect inference variables here"); } - match ty::Instance::resolve(self, param_env, ct.def, ct.args) { + match ty::Instance::try_resolve(self, param_env, ct.def, ct.args) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: None }; self.const_eval_global_id_for_typeck(param_env, cid, span).inspect(|_| { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ef88b253864..83e3898cebf 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -430,11 +430,12 @@ pub struct Body<'tcx> { pub tainted_by_errors: Option<ErrorGuaranteed>, - /// Branch coverage information collected during MIR building, to be used by - /// the `InstrumentCoverage` pass. + /// Coverage information collected from THIR/MIR during MIR building, + /// to be used by the `InstrumentCoverage` pass. /// - /// Only present if branch coverage is enabled and this function is eligible. - pub coverage_branch_info: Option<Box<coverage::BranchInfo>>, + /// Only present if coverage is enabled and this function is eligible. + /// Boxed to limit space overhead in non-coverage builds. + pub coverage_info_hi: Option<Box<coverage::CoverageInfoHi>>, /// Per-function coverage information added by the `InstrumentCoverage` /// pass, to be used in conjunction with the coverage statements injected @@ -484,7 +485,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors, - coverage_branch_info: None, + coverage_info_hi: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); @@ -515,7 +516,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors: None, - coverage_branch_info: None, + coverage_info_hi: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 4657f4dcf81..82625ae3d47 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -473,8 +473,8 @@ pub fn write_mir_intro<'tcx>( // Add an empty line before the first block is printed. writeln!(w)?; - if let Some(branch_info) = &body.coverage_branch_info { - write_coverage_branch_info(branch_info, w)?; + if let Some(coverage_info_hi) = &body.coverage_info_hi { + write_coverage_info_hi(coverage_info_hi, w)?; } if let Some(function_coverage_info) = &body.function_coverage_info { write_function_coverage_info(function_coverage_info, w)?; @@ -483,18 +483,26 @@ pub fn write_mir_intro<'tcx>( Ok(()) } -fn write_coverage_branch_info( - branch_info: &coverage::BranchInfo, +fn write_coverage_info_hi( + coverage_info_hi: &coverage::CoverageInfoHi, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } = - branch_info; + let coverage::CoverageInfoHi { + num_block_markers: _, + branch_spans, + mcdc_branch_spans, + mcdc_decision_spans, + } = coverage_info_hi; + + // Only add an extra trailing newline if we printed at least one thing. + let mut did_print = false; for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { writeln!( w, "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", )?; + did_print = true; } for coverage::MCDCBranchSpan { @@ -510,6 +518,7 @@ fn write_coverage_branch_info( "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}", condition_info.map(|info| info.condition_id) )?; + did_print = true; } for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in @@ -519,10 +528,10 @@ fn write_coverage_branch_info( w, "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}" )?; + did_print = true; } - if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty() - { + if did_print { writeln!(w)?; } @@ -845,6 +854,16 @@ impl<'tcx> TerminatorKind<'tcx> { } write!(fmt, ")") } + TailCall { func, args, .. } => { + write!(fmt, "tailcall {func:?}(")?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", arg)?; + } + write!(fmt, ")") + } Assert { cond, expected, msg, .. } => { write!(fmt, "assert(")?; if !expected { @@ -912,7 +931,12 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> { use self::TerminatorKind::*; match *self { - Return | UnwindResume | UnwindTerminate(_) | Unreachable | CoroutineDrop => vec![], + Return + | TailCall { .. } + | UnwindResume + | UnwindTerminate(_) + | Unreachable + | CoroutineDrop => vec![], Goto { .. } => vec!["".into()], SwitchInt { ref targets, .. } => targets .values @@ -1449,7 +1473,7 @@ pub fn write_allocations<'tcx>( // This can't really happen unless there are bugs, but it doesn't cost us anything to // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, - Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?, + Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?, Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { write!(w, " (vtable: impl {trait_ref} for {ty})")? } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 46b38e4a6a6..cd8e28522ec 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -274,6 +274,9 @@ pub enum ConstraintCategory<'tcx> { /// A constraint that doesn't correspond to anything the user sees. Internal, + + /// An internal constraint derived from an illegal universe relation. + IllegalUniverse, } #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2c2884f1897..620fa962d79 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -744,6 +744,36 @@ pub enum TerminatorKind<'tcx> { fn_span: Span, }, + /// Tail call. + /// + /// Roughly speaking this is a chimera of [`Call`] and [`Return`], with some caveats. + /// Semantically tail calls consists of two actions: + /// - pop of the current stack frame + /// - a call to the `func`, with the return address of the **current** caller + /// - so that a `return` inside `func` returns to the caller of the caller + /// of the function that is currently being executed + /// + /// Note that in difference with [`Call`] this is missing + /// - `destination` (because it's always the return place) + /// - `target` (because it's always taken from the current stack frame) + /// - `unwind` (because it's always taken from the current stack frame) + /// + /// [`Call`]: TerminatorKind::Call + /// [`Return`]: TerminatorKind::Return + TailCall { + /// The function that’s being called. + func: Operand<'tcx>, + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. + args: Box<[Spanned<Operand<'tcx>>]>, + // FIXME(explicit_tail_calls): should we have the span for `become`? is this span accurate? do we need it? + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, + }, + /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`, /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some /// unspecified constant as the function to call, all the operands stored in the `AssertMessage` @@ -870,6 +900,7 @@ impl TerminatorKind<'_> { TerminatorKind::Unreachable => "Unreachable", TerminatorKind::Drop { .. } => "Drop", TerminatorKind::Call { .. } => "Call", + TerminatorKind::TailCall { .. } => "TailCall", TerminatorKind::Assert { .. } => "Assert", TerminatorKind::Yield { .. } => "Yield", TerminatorKind::CoroutineDrop => "CoroutineDrop", diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index ed592612358..5b035d9579d 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -439,6 +439,7 @@ mod helper { | CoroutineDrop | Return | Unreachable + | TailCall { .. } | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { targets.iter().copied().chain(Some(u)) @@ -479,6 +480,7 @@ mod helper { | CoroutineDrop | Return | Unreachable + | TailCall { .. } | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { targets.iter_mut().chain(Some(u)) @@ -501,6 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } @@ -521,6 +524,7 @@ impl<'tcx> TerminatorKind<'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } @@ -606,9 +610,12 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> { use TerminatorKind::*; match *self { - Return | UnwindResume | UnwindTerminate(_) | CoroutineDrop | Unreachable => { - TerminatorEdges::None - } + Return + | TailCall { .. } + | UnwindResume + | UnwindTerminate(_) + | CoroutineDrop + | Unreachable => TerminatorEdges::None, Goto { target } => TerminatorEdges::Single(target), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 7628a1ed2fe..0d3c419748b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -540,6 +540,17 @@ macro_rules! make_mir_visitor { ); } + TerminatorKind::TailCall { + func, + args, + fn_span: _, + } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(&$($mutability)? arg.node, location); + } + }, + TerminatorKind::Assert { cond, expected: _, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 230a44bcf24..33c27d41d86 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -368,6 +368,7 @@ rustc_queries! { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that @@ -378,6 +379,7 @@ rustc_queries! { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Elaborated version of the predicates from `explicit_item_bounds`. @@ -646,6 +648,9 @@ rustc_queries! { } /// Returns the predicates written explicitly by the user. + /// + /// You should probably use `predicates_of` unless you're looking for + /// predicates with explicit spans for diagnostics purposes. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -662,29 +667,32 @@ rustc_queries! { feedable } - /// Maps from the `DefId` of a trait to the list of - /// super-predicates. This is a subset of the full list of - /// predicates. We store these in a separate map because we must - /// evaluate them even during type conversion, often before the - /// full predicates are available (note that supertraits have - /// additional acyclicity requirements). - query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + /// Maps from the `DefId` of a trait to the list of super-predicates of the trait, + /// *before* elaboration (so it doesn't contain transitive super-predicates). This + /// is a subset of the full list of predicates. We store these in a separate map + /// because we must evaluate them even during type conversion, often before the full + /// predicates are available (note that super-predicates must not be cyclic). + query explicit_super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } - query implied_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + /// The predicates of the trait that are implied during elaboration. This is a + /// superset of the super-predicates of the trait, but a subset of the predicates + /// of the trait. For regular traits, this includes all super-predicates and their + /// associated type bounds. For trait aliases, currently, this includes all of the + /// predicates of the trait alias. + query explicit_implied_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } - /// The `Option<Ident>` is the name of an associated type. If it is `None`, then this query - /// returns the full set of predicates. If `Some<Ident>`, then the query returns only the - /// subset of super-predicates that reference traits that define the given associated type. - /// This is used to avoid cycles in resolving types like `T::Item`. - query super_predicates_that_define_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> { + /// The Ident is the name of an associated type.The query returns only the subset + /// of supertraits that define the given associated type. This is used to avoid + /// cycles in resolving type-dependent associated item paths like `T::Item`. + query explicit_supertraits_containing_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", tcx.def_path_str(key.0), key.1 @@ -847,6 +855,12 @@ rustc_queries! { separate_provide_extern } + query associated_type_for_effects(def_id: DefId) -> Option<DefId> { + desc { |tcx| "creating associated items for effects in `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding /// associated item. query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId { @@ -2183,8 +2197,8 @@ rustc_queries! { /// * `Err(ErrorGuaranteed)` when the `Instance` resolution process /// couldn't complete due to errors elsewhere - this is distinct /// from `Ok(None)` to avoid misleading diagnostics when an error - /// has already been/will be emitted, for the original cause - query resolve_instance( + /// has already been/will be emitted, for the original cause. + query resolve_instance_raw( key: ty::ParamEnvAnd<'tcx, (DefId, GenericArgsRef<'tcx>)> ) -> Result<Option<ty::Instance<'tcx>>, ErrorGuaranteed> { desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b4e3fae1b43..b74775142e4 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -7,7 +7,6 @@ pub mod select; pub mod solve; pub mod specialization_graph; mod structural_impls; -pub mod util; use crate::mir::ConstraintCategory; use crate::ty::abstract_const::NotConstEvaluatable; diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs deleted file mode 100644 index adbb6cf2ddc..00000000000 --- a/compiler/rustc_middle/src/traits/util.rs +++ /dev/null @@ -1,62 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; - -use crate::ty::{Clause, PolyTraitRef, ToPolyTraitRef, TyCtxt, Upcast}; - -/// Given a [`PolyTraitRef`], get the [`Clause`]s implied by the trait's definition. -/// -/// This only exists in `rustc_middle` because the more powerful elaborator depends on -/// `rustc_infer` for elaborating outlives bounds -- this should only be used for pretty -/// printing. -pub fn super_predicates_for_pretty_printing<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: PolyTraitRef<'tcx>, -) -> impl Iterator<Item = Clause<'tcx>> { - let clause = trait_ref.upcast(tcx); - Elaborator { tcx, visited: FxHashSet::from_iter([clause]), stack: vec![clause] } -} - -/// Like [`super_predicates_for_pretty_printing`], except it only returns traits and filters out -/// all other [`Clause`]s. -pub fn supertraits_for_pretty_printing<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: PolyTraitRef<'tcx>, -) -> impl Iterator<Item = PolyTraitRef<'tcx>> { - super_predicates_for_pretty_printing(tcx, trait_ref).filter_map(|clause| { - clause.as_trait_clause().map(|trait_clause| trait_clause.to_poly_trait_ref()) - }) -} - -struct Elaborator<'tcx> { - tcx: TyCtxt<'tcx>, - visited: FxHashSet<Clause<'tcx>>, - stack: Vec<Clause<'tcx>>, -} - -impl<'tcx> Elaborator<'tcx> { - fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) { - let super_predicates = - self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map( - |&(pred, _)| { - let clause = pred.instantiate_supertrait(self.tcx, trait_ref); - self.visited.insert(clause).then_some(clause) - }, - ); - - self.stack.extend(super_predicates); - } -} - -impl<'tcx> Iterator for Elaborator<'tcx> { - type Item = Clause<'tcx>; - - fn next(&mut self) -> Option<Clause<'tcx>> { - if let Some(clause) = self.stack.pop() { - if let Some(trait_clause) = clause.as_trait_clause() { - self.elaborate(trait_clause.to_poly_trait_ref()); - } - Some(clause) - } else { - None - } - } -} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 8e221cdc603..88ee32eae95 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -229,6 +229,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> { fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { self.sized_constraint(tcx) } + + fn is_fundamental(self) -> bool { + self.is_fundamental() + } } #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 1cdde3f057c..820f5e950a9 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -34,6 +34,8 @@ pub struct AssocItem { /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData` /// provides additional information about its source. pub opt_rpitit_info: Option<ty::ImplTraitInTraitData>, + + pub is_effects_desugaring: bool, } impl AssocItem { diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index bade0d56415..bdd9a6bab2b 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -237,7 +237,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of /// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`. /// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`. -/// 2. Since we only look at the projections here function will return `bar.x` as an a valid +/// 2. Since we only look at the projections here function will return `bar.x` as a valid /// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections /// list are being applied to the same root variable. pub fn is_ancestor_or_same_capture( diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index bf834ef7607..98f35b6b8ab 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -54,6 +54,13 @@ pub struct Expr<'tcx> { pub kind: ExprKind, args: ty::GenericArgsRef<'tcx>, } + +impl<'tcx> rustc_type_ir::inherent::ExprConst<TyCtxt<'tcx>> for Expr<'tcx> { + fn args(self) -> ty::GenericArgsRef<'tcx> { + self.args + } +} + impl<'tcx> Expr<'tcx> { pub fn new_binop( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 4bac9396e59..aee42bfe3aa 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -37,7 +37,7 @@ use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; @@ -92,6 +92,8 @@ use std::ops::{Bound, Deref}; impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; type LocalDefId = LocalDefId; + type Span = Span; + type GenericArgs = ty::GenericArgsRef<'tcx>; type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>]; @@ -342,12 +344,19 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) } - fn super_predicates_of( + fn explicit_super_predicates_of( self, def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> { + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> { + ty::EarlyBinder::bind(self.explicit_super_predicates_of(def_id).instantiate_identity(self)) + } + + fn explicit_implied_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> { ty::EarlyBinder::bind( - self.super_predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), + self.explicit_implied_predicates_of(def_id).instantiate_identity(self), ) } @@ -363,6 +372,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item)) } + fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> { + lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?) + } + fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> { self.associated_items(def_id) .in_definition_order() @@ -370,17 +383,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { .map(|assoc_item| assoc_item.def_id) } - fn args_may_unify_deep( - self, - obligation_args: ty::GenericArgsRef<'tcx>, - impl_args: ty::GenericArgsRef<'tcx>, - ) -> bool { - ty::fast_reject::DeepRejectCtxt { - treat_obligation_params: ty::fast_reject::TreatParams::ForLookup, - } - .args_may_unify(obligation_args, impl_args) - } - // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, // since we want to skip over blanket impls for non-rigid aliases, and also we // only want to consider types that *actually* unify with float/int vars. @@ -526,20 +528,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_object_safe(trait_def_id) } - fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).implement_via_object - } - - fn fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> { - self.fn_trait_kind_from_def_id(trait_def_id) - } - - fn async_fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> { - self.async_fn_trait_kind_from_def_id(trait_def_id) + fn trait_is_fundamental(self, def_id: DefId) -> bool { + self.trait_def(def_id).is_fundamental } - fn supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator<Item = DefId> { - self.supertrait_def_ids(trait_def_id) + fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).implement_via_object } fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { @@ -579,44 +573,83 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Ty<'tcx> { placeholder.find_const_ty_from_env(param_env) } + + fn anonymize_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + binder: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.anonymize_bound_vars(binder) + } } -fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem { - match lang_item { - TraitSolverLangItem::AsyncDestruct => LangItem::AsyncDestruct, - TraitSolverLangItem::AsyncFnKindHelper => LangItem::AsyncFnKindHelper, - TraitSolverLangItem::AsyncFnKindUpvars => LangItem::AsyncFnKindUpvars, - TraitSolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, - TraitSolverLangItem::AsyncIterator => LangItem::AsyncIterator, - TraitSolverLangItem::CallOnceFuture => LangItem::CallOnceFuture, - TraitSolverLangItem::CallRefFuture => LangItem::CallRefFuture, - TraitSolverLangItem::Clone => LangItem::Clone, - TraitSolverLangItem::Copy => LangItem::Copy, - TraitSolverLangItem::Coroutine => LangItem::Coroutine, - TraitSolverLangItem::CoroutineReturn => LangItem::CoroutineReturn, - TraitSolverLangItem::CoroutineYield => LangItem::CoroutineYield, - TraitSolverLangItem::Destruct => LangItem::Destruct, - TraitSolverLangItem::DiscriminantKind => LangItem::DiscriminantKind, - TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, - TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, - TraitSolverLangItem::FusedIterator => LangItem::FusedIterator, - TraitSolverLangItem::Future => LangItem::Future, - TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, - TraitSolverLangItem::Iterator => LangItem::Iterator, - TraitSolverLangItem::Metadata => LangItem::Metadata, - TraitSolverLangItem::Option => LangItem::Option, - TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, - TraitSolverLangItem::PointerLike => LangItem::PointerLike, - TraitSolverLangItem::Poll => LangItem::Poll, - TraitSolverLangItem::Sized => LangItem::Sized, - TraitSolverLangItem::TransmuteTrait => LangItem::TransmuteTrait, - TraitSolverLangItem::Tuple => LangItem::Tuple, - TraitSolverLangItem::Unpin => LangItem::Unpin, - TraitSolverLangItem::Unsize => LangItem::Unsize, +macro_rules! bidirectional_lang_item_map { + ($($name:ident),+ $(,)?) => { + fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem { + match lang_item { + $(TraitSolverLangItem::$name => LangItem::$name,)+ + } + } + + fn lang_item_to_trait_lang_item(lang_item: LangItem) -> Option<TraitSolverLangItem> { + Some(match lang_item { + $(LangItem::$name => TraitSolverLangItem::$name,)+ + _ => return None, + }) + } } } +bidirectional_lang_item_map! { +// tidy-alphabetical-start + AsyncDestruct, + AsyncFn, + AsyncFnKindHelper, + AsyncFnKindUpvars, + AsyncFnMut, + AsyncFnOnce, + AsyncFnOnceOutput, + AsyncIterator, + CallOnceFuture, + CallRefFuture, + Clone, + Copy, + Coroutine, + CoroutineReturn, + CoroutineYield, + Destruct, + DiscriminantKind, + DynMetadata, + EffectsIntersection, + EffectsIntersectionOutput, + EffectsMaybe, + EffectsNoRuntime, + EffectsRuntime, + Fn, + FnMut, + FnOnce, + FnPtrTrait, + FusedIterator, + Future, + FutureOutput, + Iterator, + Metadata, + Option, + PointeeTrait, + PointerLike, + Poll, + Sized, + TransmuteTrait, + Tuple, + Unpin, + Unsize, +// tidy-alphabetical-end +} + impl<'tcx> rustc_type_ir::inherent::DefId<TyCtxt<'tcx>> for DefId { + fn is_local(self) -> bool { + self.is_local() + } + fn as_local(self) -> Option<LocalDefId> { self.as_local() } @@ -2440,7 +2473,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`. pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool { - self.super_traits_of(trait_def_id).any(|trait_did| { + self.supertrait_def_ids(trait_def_id).any(|trait_did| { self.associated_items(trait_did) .filter_by_name_unhygienic(assoc_name.name) .any(|item| self.hygienic_eq(assoc_name, item.ident(self), trait_did)) @@ -2463,28 +2496,10 @@ impl<'tcx> TyCtxt<'tcx> { /// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally) /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used - /// to identify which traits may define a given associated type to help avoid cycle errors. - /// Returns a `DefId` iterator. - fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { - let mut set = FxHashSet::default(); - let mut stack = vec![trait_def_id]; - - set.insert(trait_def_id); - - iter::from_fn(move || -> Option<DefId> { - let trait_did = stack.pop()?; - let generic_predicates = self.super_predicates_of(trait_did); - - for (predicate, _) in generic_predicates.predicates { - if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() { - if set.insert(data.def_id()) { - stack.push(data.def_id()); - } - } - } - - Some(trait_did) - }) + /// to identify which traits may define a given associated type to help avoid cycle errors, + /// and to make size estimates for vtable layout computation. + pub fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { + rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) } /// Given a closure signature, returns an equivalent fn signature. Detuples @@ -3095,9 +3110,9 @@ impl<'tcx> TyCtxt<'tcx> { matches!( node, hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { generics, .. }), + kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), .. - }) if generics.params.iter().any(|p| matches!(p.kind, hir::GenericParamKind::Const { is_host_effect: true, .. })) + }) if matches!(constness, hir::Constness::Const) ) } diff --git a/compiler/rustc_middle/src/ty/elaborate_impl.rs b/compiler/rustc_middle/src/ty/elaborate_impl.rs new file mode 100644 index 00000000000..8c89a2d884b --- /dev/null +++ b/compiler/rustc_middle/src/ty/elaborate_impl.rs @@ -0,0 +1,84 @@ +use rustc_span::Span; +use rustc_type_ir::elaborate::Elaboratable; + +use crate::ty::{self, TyCtxt}; + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Clause<'tcx> { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.as_predicate() + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + clause + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + clause + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Predicate<'tcx> { + fn predicate(&self) -> ty::Predicate<'tcx> { + *self + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + clause.as_predicate() + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + clause.as_predicate() + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Predicate<'tcx>, Span) { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.0 + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + (clause.as_predicate(), self.1) + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + (clause.as_predicate(), self.1) + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Clause<'tcx>, Span) { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.0.as_predicate() + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + (clause, self.1) + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + (clause, self.1) + } +} diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 923667e609b..0413cfa5a63 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -1,369 +1,9 @@ -use crate::mir::Mutability; -use crate::ty::GenericArgKind; -use crate::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_hir::def_id::DefId; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use std::fmt::Debug; -use std::hash::Hash; -use std::iter; -/// See `simplify_type`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] -pub enum SimplifiedType { - Bool, - Char, - Int(ty::IntTy), - Uint(ty::UintTy), - Float(ty::FloatTy), - Adt(DefId), - Foreign(DefId), - Str, - Array, - Slice, - Ref(Mutability), - Ptr(Mutability), - Never, - Tuple(usize), - /// A trait object, all of whose components are markers - /// (e.g., `dyn Send + Sync`). - MarkerTraitObject, - Trait(DefId), - Closure(DefId), - Coroutine(DefId), - CoroutineWitness(DefId), - Function(usize), - Placeholder, - Error, -} +use super::TyCtxt; -/// Generic parameters are pretty much just bound variables, e.g. -/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as -/// `for<'a, T> fn(&'a T) -> u32`. -/// -/// Typecheck of `foo` has to succeed for all possible generic arguments, so -/// during typeck, we have to treat its generic parameters as if they -/// were placeholders. -/// -/// But when calling `foo` we only have to provide a specific generic argument. -/// In that case the generic parameters are instantiated with inference variables. -/// As we use `simplify_type` before that instantiation happens, we just treat -/// generic parameters as if they were inference variables in that case. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum TreatParams { - /// Treat parameters as infer vars. This is the correct mode for caching - /// an impl's type for lookup. - AsCandidateKey, - /// Treat parameters as placeholders in the given environment. This is the - /// correct mode for *lookup*, as during candidate selection. - /// - /// This also treats projections with inference variables as infer vars - /// since they could be further normalized. - ForLookup, -} +pub use rustc_type_ir::fast_reject::*; -/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. -/// -/// **This function should only be used if you need to store or retrieve the type from some -/// hashmap. If you want to quickly decide whether two types may unify, use the [DeepRejectCtxt] -/// instead.** -/// -/// The idea is to get something simple that we can use to quickly decide if two types could unify, -/// for example during method lookup. If this function returns `Some(x)` it can only unify with -/// types for which this method returns either `Some(x)` as well or `None`. -/// -/// A special case here are parameters and projections, which are only injective -/// if they are treated as placeholders. -/// -/// For example when storing impls based on their simplified self type, we treat -/// generic parameters as if they were inference variables. We must not simplify them here, -/// as they can unify with any other type. -/// -/// With projections we have to be even more careful, as treating them as placeholders -/// is only correct if they are fully normalized. -/// -/// ¹ meaning that if the outermost layers are different, then the whole types are also different. -pub fn simplify_type<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - treat_params: TreatParams, -) -> Option<SimplifiedType> { - match *ty.kind() { - ty::Bool => Some(SimplifiedType::Bool), - ty::Char => Some(SimplifiedType::Char), - ty::Int(int_type) => Some(SimplifiedType::Int(int_type)), - ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)), - ty::Float(float_type) => Some(SimplifiedType::Float(float_type)), - ty::Adt(def, _) => Some(SimplifiedType::Adt(def.did())), - ty::Str => Some(SimplifiedType::Str), - ty::Array(..) => Some(SimplifiedType::Array), - ty::Slice(..) => Some(SimplifiedType::Slice), - ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params), - ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), - ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { - Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { - Some(SimplifiedType::Trait(principal_def_id)) - } - _ => Some(SimplifiedType::MarkerTraitObject), - }, - ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)), - ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => { - Some(SimplifiedType::Closure(def_id)) - } - ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)), - ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)), - ty::Never => Some(SimplifiedType::Never), - ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), - ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), - ty::Placeholder(..) => Some(SimplifiedType::Placeholder), - ty::Param(_) => match treat_params { - TreatParams::ForLookup => Some(SimplifiedType::Placeholder), - TreatParams::AsCandidateKey => None, - }, - ty::Alias(..) => match treat_params { - // When treating `ty::Param` as a placeholder, projections also - // don't unify with anything else as long as they are fully normalized. - // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder` - // when the new solver is enabled by default. - TreatParams::ForLookup if !ty.has_non_region_infer() => { - Some(SimplifiedType::Placeholder) - } - TreatParams::ForLookup | TreatParams::AsCandidateKey => None, - }, - ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), - ty::Error(_) => Some(SimplifiedType::Error), - ty::Bound(..) | ty::Infer(_) => None, - } -} +pub type DeepRejectCtxt<'tcx> = rustc_type_ir::fast_reject::DeepRejectCtxt<TyCtxt<'tcx>>; -impl SimplifiedType { - pub fn def(self) -> Option<DefId> { - match self { - SimplifiedType::Adt(d) - | SimplifiedType::Foreign(d) - | SimplifiedType::Trait(d) - | SimplifiedType::Closure(d) - | SimplifiedType::Coroutine(d) - | SimplifiedType::CoroutineWitness(d) => Some(d), - _ => None, - } - } -} - -/// Given generic arguments from an obligation and an impl, -/// could these two be unified after replacing parameters in the -/// the impl with inference variables. -/// -/// For obligations, parameters won't be replaced by inference -/// variables and only unify with themselves. We treat them -/// the same way we treat placeholders. -/// -/// We also use this function during coherence. For coherence the -/// impls only have to overlap for some value, so we treat parameters -/// on both sides like inference variables. This behavior is toggled -/// using the `treat_obligation_params` field. -#[derive(Debug, Clone, Copy)] -pub struct DeepRejectCtxt { - pub treat_obligation_params: TreatParams, -} - -impl DeepRejectCtxt { - pub fn args_may_unify<'tcx>( - self, - obligation_args: GenericArgsRef<'tcx>, - impl_args: GenericArgsRef<'tcx>, - ) -> bool { - iter::zip(obligation_args, impl_args).all(|(obl, imp)| { - match (obl.unpack(), imp.unpack()) { - // We don't fast reject based on regions. - (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => true, - (GenericArgKind::Type(obl), GenericArgKind::Type(imp)) => { - self.types_may_unify(obl, imp) - } - (GenericArgKind::Const(obl), GenericArgKind::Const(imp)) => { - self.consts_may_unify(obl, imp) - } - _ => bug!("kind mismatch: {obl} {imp}"), - } - }) - } - - pub fn types_may_unify<'tcx>(self, obligation_ty: Ty<'tcx>, impl_ty: Ty<'tcx>) -> bool { - match impl_ty.kind() { - // Start by checking whether the type in the impl may unify with - // pretty much everything. Just return `true` in that case. - ty::Param(_) | ty::Error(_) | ty::Alias(..) => return true, - // These types only unify with inference variables or their own - // variant. - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(..) - | ty::Str - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Dynamic(..) - | ty::Pat(..) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) - | ty::FnPtr(..) - | ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()), - ty::FnDef(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(_) => bug!("unexpected impl_ty: {impl_ty}"), - } - - let k = impl_ty.kind(); - match *obligation_ty.kind() { - // Purely rigid types, use structural equivalence. - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Never - | ty::Foreign(_) => obligation_ty == impl_ty, - ty::Ref(_, obl_ty, obl_mutbl) => match k { - &ty::Ref(_, impl_ty, impl_mutbl) => { - obl_mutbl == impl_mutbl && self.types_may_unify(obl_ty, impl_ty) - } - _ => false, - }, - ty::Adt(obl_def, obl_args) => match k { - &ty::Adt(impl_def, impl_args) => { - obl_def == impl_def && self.args_may_unify(obl_args, impl_args) - } - _ => false, - }, - ty::Pat(obl_ty, _) => { - // FIXME(pattern_types): take pattern into account - matches!(k, &ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty)) - } - ty::Slice(obl_ty) => { - matches!(k, &ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty)) - } - ty::Array(obl_ty, obl_len) => match k { - &ty::Array(impl_ty, impl_len) => { - self.types_may_unify(obl_ty, impl_ty) - && self.consts_may_unify(obl_len, impl_len) - } - _ => false, - }, - ty::Tuple(obl) => match k { - &ty::Tuple(imp) => { - obl.len() == imp.len() - && iter::zip(obl, imp).all(|(obl, imp)| self.types_may_unify(obl, imp)) - } - _ => false, - }, - ty::RawPtr(obl_ty, obl_mutbl) => match *k { - ty::RawPtr(imp_ty, imp_mutbl) => { - obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty) - } - _ => false, - }, - ty::Dynamic(obl_preds, ..) => { - // Ideally we would walk the existential predicates here or at least - // compare their length. But considering that the relevant `Relate` impl - // actually sorts and deduplicates these, that doesn't work. - matches!(k, ty::Dynamic(impl_preds, ..) if - obl_preds.principal_def_id() == impl_preds.principal_def_id() - ) - } - ty::FnPtr(obl_sig) => match k { - ty::FnPtr(impl_sig) => { - let ty::FnSig { inputs_and_output, c_variadic, safety, abi } = - obl_sig.skip_binder(); - let impl_sig = impl_sig.skip_binder(); - - abi == impl_sig.abi - && c_variadic == impl_sig.c_variadic - && safety == impl_sig.safety - && inputs_and_output.len() == impl_sig.inputs_and_output.len() - && iter::zip(inputs_and_output, impl_sig.inputs_and_output) - .all(|(obl, imp)| self.types_may_unify(obl, imp)) - } - _ => false, - }, - - // Impls cannot contain these types as these cannot be named directly. - ty::FnDef(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => false, - - // Placeholder types don't unify with anything on their own - ty::Placeholder(..) | ty::Bound(..) => false, - - // Depending on the value of `treat_obligation_params`, we either - // treat generic parameters like placeholders or like inference variables. - ty::Param(_) => match self.treat_obligation_params { - TreatParams::ForLookup => false, - TreatParams::AsCandidateKey => true, - }, - - ty::Infer(ty::IntVar(_)) => impl_ty.is_integral(), - - ty::Infer(ty::FloatVar(_)) => impl_ty.is_floating_point(), - - ty::Infer(_) => true, - - // As we're walking the whole type, it may encounter projections - // inside of binders and what not, so we're just going to assume that - // projections can unify with other stuff. - // - // Looking forward to lazy normalization this is the safer strategy anyways. - ty::Alias(..) => true, - - ty::Error(_) => true, - - ty::CoroutineWitness(..) => { - bug!("unexpected obligation type: {:?}", obligation_ty) - } - } - } - - pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool { - let impl_val = match impl_ct.kind() { - ty::ConstKind::Expr(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Error(_) => { - return true; - } - ty::ConstKind::Value(_, impl_val) => impl_val, - ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { - bug!("unexpected impl arg: {:?}", impl_ct) - } - }; - - match obligation_ct.kind() { - ty::ConstKind::Param(_) => match self.treat_obligation_params { - TreatParams::ForLookup => false, - TreatParams::AsCandidateKey => true, - }, - - // Placeholder consts don't unify with anything on their own - ty::ConstKind::Placeholder(_) => false, - - // As we don't necessarily eagerly evaluate constants, - // they might unify with any value. - ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { - true - } - ty::ConstKind::Value(_, obl_val) => obl_val == impl_val, - - ty::ConstKind::Infer(_) => true, - - ty::ConstKind::Bound(..) => { - bug!("unexpected obl const: {:?}", obligation_ct) - } - } - } -} +pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<DefId>; diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 6467689a8aa..11ed0bdaa70 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -14,7 +14,7 @@ use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; pub enum GenericParamDefKind { Lifetime, Type { has_default: bool, synthetic: bool }, - Const { has_default: bool, is_host_effect: bool }, + Const { has_default: bool, is_host_effect: bool, synthetic: bool }, } impl GenericParamDefKind { @@ -371,6 +371,7 @@ impl<'tcx> Generics { pub struct GenericPredicates<'tcx> { pub parent: Option<DefId>, pub predicates: &'tcx [(Clause<'tcx>, Span)], + pub effects_min_tys: &'tcx ty::List<Ty<'tcx>>, } impl<'tcx> GenericPredicates<'tcx> { @@ -393,7 +394,7 @@ impl<'tcx> GenericPredicates<'tcx> { } pub fn instantiate_own_identity(&self) -> impl Iterator<Item = (Clause<'tcx>, Span)> { - EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied() + EarlyBinder::bind(self.predicates).iter_identity_copied() } #[instrument(level = "debug", skip(self, tcx))] diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index efcf428c213..9be7370a1c2 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -4,7 +4,6 @@ use crate::middle::region; use crate::mir; use crate::ty; -use crate::ty::fast_reject::SimplifiedType; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::HashingControls; @@ -57,18 +56,6 @@ where } } -impl<'a> ToStableHashKey<StableHashingContext<'a>> for SimplifiedType { - type KeyType = Fingerprint; - - #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint { - let mut hasher = StableHasher::new(); - let mut hcx: StableHashingContext<'a> = hcx.clone(); - self.hash_stable(&mut hcx, &mut hasher); - hasher.finish() - } -} - impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::GenericArg<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.unpack().hash_stable(hcx, hasher); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1ba8820e0e1..c50a98e88fd 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,23 +1,26 @@ +use crate::error; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; -use crate::ty::{EarlyBinder, GenericArgs, GenericArgsRef, TypeVisitableExt}; +use crate::ty::print::{shrunk_instance_name, FmtPrinter, Printer}; +use crate::ty::{ + self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; -use rustc_macros::{ - Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable, TypeVisitable, -}; +use rustc_macros::{Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable}; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol, DUMMY_SP}; use tracing::{debug, instrument}; use std::assert_matches::assert_matches; use std::fmt; +use std::path::PathBuf; /// An `InstanceKind` along with the args that are needed to substitute the instance. /// @@ -385,7 +388,40 @@ impl<'tcx> InstanceKind<'tcx> { } } -fn fmt_instance( +fn type_length<'tcx>(item: impl TypeVisitable<TyCtxt<'tcx>>) -> usize { + struct Visitor<'tcx> { + type_length: usize, + cache: FxHashMap<Ty<'tcx>, usize>, + } + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) { + if let Some(&value) = self.cache.get(&t) { + self.type_length += value; + return; + } + + let prev = self.type_length; + self.type_length += 1; + t.super_visit_with(self); + + // We don't try to use the cache if the type is fairly small. + if self.type_length > 16 { + self.cache.insert(t, self.type_length - prev); + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) { + self.type_length += 1; + ct.super_visit_with(self); + } + } + let mut visitor = Visitor { type_length: 0, cache: Default::default() }; + item.visit_with(&mut visitor); + + visitor.type_length +} + +pub fn fmt_instance( f: &mut fmt::Formatter<'_>, instance: Instance<'_>, type_length: Option<rustc_session::Limit>, @@ -485,19 +521,30 @@ impl<'tcx> Instance<'tcx> { /// /// Presuming that coherence and type-check have succeeded, if this method is invoked /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return - /// `Ok(Some(instance))`. + /// `Ok(Some(instance))`, **except** for when the instance's inputs hit the type size limit, + /// in which case it may bail out and return `Ok(None)`. /// /// Returns `Err(ErrorGuaranteed)` when the `Instance` resolution process /// couldn't complete due to errors elsewhere - this is distinct /// from `Ok(None)` to avoid misleading diagnostics when an error /// has already been/will be emitted, for the original cause #[instrument(level = "debug", skip(tcx), ret)] - pub fn resolve( + pub fn try_resolve( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>, ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> { + // Rust code can easily create exponentially-long types using only a + // polynomial recursion depth. Even with the default recursion + // depth, you can easily get cases that take >2^60 steps to run, + // which means that rustc basically hangs. + // + // Bail out in these cases to avoid that bad user experience. + if !tcx.type_length_limit().value_within_limit(type_length(args)) { + return Ok(None); + } + // All regions in the result of this query are erased, so it's // fine to erase all of the input regions. @@ -505,7 +552,7 @@ impl<'tcx> Instance<'tcx> { // below is more likely to ignore the bounds in scope (e.g. if the only // generic parameters mentioned by `args` were lifetime ones). let args = tcx.erase_regions(args); - tcx.resolve_instance(tcx.erase_regions(param_env.and((def_id, args)))) + tcx.resolve_instance_raw(tcx.erase_regions(param_env.and((def_id, args)))) } pub fn expect_resolve( @@ -513,10 +560,48 @@ impl<'tcx> Instance<'tcx> { param_env: ty::ParamEnv<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>, + span: Span, ) -> Instance<'tcx> { - match ty::Instance::resolve(tcx, param_env, def_id, args) { + // We compute the span lazily, to avoid unnecessary query calls. + // If `span` is a DUMMY_SP, and the def id is local, then use the + // def span of the def id. + let span_or_local_def_span = + || if span.is_dummy() && def_id.is_local() { tcx.def_span(def_id) } else { span }; + + match ty::Instance::try_resolve(tcx, param_env, def_id, args) { Ok(Some(instance)) => instance, - instance => bug!( + Ok(None) => { + let type_length = type_length(args); + if !tcx.type_length_limit().value_within_limit(type_length) { + let (shrunk, written_to_path) = + shrunk_instance_name(tcx, Instance::new(def_id, args)); + let mut path = PathBuf::new(); + let was_written = if let Some(path2) = written_to_path { + path = path2; + Some(()) + } else { + None + }; + tcx.dcx().emit_fatal(error::TypeLengthLimit { + // We don't use `def_span(def_id)` so that diagnostics point + // to the crate root during mono instead of to foreign items. + // This is arguably better. + span: span_or_local_def_span(), + shrunk, + was_written, + path, + type_length, + }); + } else { + span_bug!( + span_or_local_def_span(), + "failed to resolve instance for {}", + tcx.def_path_str_with_args(def_id, args) + ) + } + } + instance => span_bug!( + span_or_local_def_span(), "failed to resolve instance for {}: {instance:#?}", tcx.def_path_str_with_args(def_id, args) ), @@ -533,7 +618,7 @@ impl<'tcx> Instance<'tcx> { // Use either `resolve_closure` or `resolve_for_vtable` assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}"); let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr); - Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { + Instance::try_resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceKind::Item(def) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); @@ -571,77 +656,82 @@ impl<'tcx> Instance<'tcx> { }) } - pub fn resolve_for_vtable( + pub fn expect_resolve_for_vtable( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>, - ) -> Option<Instance<'tcx>> { + span: Span, + ) -> Instance<'tcx> { debug!("resolve_for_vtable(def_id={:?}, args={:?})", def_id, args); let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty() && fn_sig.input(0).skip_binder().is_param(0) && tcx.generics_of(def_id).has_self; + if is_vtable_shim { debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceKind::VTableShim(def_id), args }) - } else { - let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable); - Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { - match resolved.def { - InstanceKind::Item(def) => { - // We need to generate a shim when we cannot guarantee that - // the caller of a trait object method will be aware of - // `#[track_caller]` - this ensures that the caller - // and callee ABI will always match. - // - // The shim is generated when all of these conditions are met: - // - // 1) The underlying method expects a caller location parameter - // in the ABI - if resolved.def.requires_caller_location(tcx) - // 2) The caller location parameter comes from having `#[track_caller]` - // on the implementation, and *not* on the trait method. - && !tcx.should_inherit_track_caller(def) - // If the method implementation comes from the trait definition itself - // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), - // then we don't need to generate a shim. This check is needed because - // `should_inherit_track_caller` returns `false` if our method - // implementation comes from the trait block, and not an impl block - && !matches!( - tcx.opt_associated_item(def), - Some(ty::AssocItem { - container: ty::AssocItemContainer::TraitContainer, - .. - }) - ) - { - if tcx.is_closure_like(def) { - debug!(" => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", - def, def_id, args); - - // Create a shim for the `FnOnce/FnMut/Fn` method we are calling - // - unlike functions, invoking a closure always goes through a - // trait. - resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }; - } else { - debug!( - " => vtable fn pointer created for function with #[track_caller]: {:?}", def - ); - resolved.def = InstanceKind::ReifyShim(def, reason); - } - } - } - InstanceKind::Virtual(def_id, _) => { - debug!(" => vtable fn pointer created for virtual call"); - resolved.def = InstanceKind::ReifyShim(def_id, reason) + return Instance { def: InstanceKind::VTableShim(def_id), args }; + } + + let mut resolved = Instance::expect_resolve(tcx, param_env, def_id, args, span); + + let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable); + match resolved.def { + InstanceKind::Item(def) => { + // We need to generate a shim when we cannot guarantee that + // the caller of a trait object method will be aware of + // `#[track_caller]` - this ensures that the caller + // and callee ABI will always match. + // + // The shim is generated when all of these conditions are met: + // + // 1) The underlying method expects a caller location parameter + // in the ABI + if resolved.def.requires_caller_location(tcx) + // 2) The caller location parameter comes from having `#[track_caller]` + // on the implementation, and *not* on the trait method. + && !tcx.should_inherit_track_caller(def) + // If the method implementation comes from the trait definition itself + // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), + // then we don't need to generate a shim. This check is needed because + // `should_inherit_track_caller` returns `false` if our method + // implementation comes from the trait block, and not an impl block + && !matches!( + tcx.opt_associated_item(def), + Some(ty::AssocItem { + container: ty::AssocItemContainer::TraitContainer, + .. + }) + ) + { + if tcx.is_closure_like(def) { + debug!( + " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", + def, def_id, args + ); + + // Create a shim for the `FnOnce/FnMut/Fn` method we are calling + // - unlike functions, invoking a closure always goes through a + // trait. + resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }; + } else { + debug!( + " => vtable fn pointer created for function with #[track_caller]: {:?}", + def + ); + resolved.def = InstanceKind::ReifyShim(def, reason); } - _ => {} } - - resolved - }) + } + InstanceKind::Virtual(def_id, _) => { + debug!(" => vtable fn pointer created for virtual call"); + resolved.def = InstanceKind::ReifyShim(def_id, reason) + } + _ => {} } + + resolved } pub fn resolve_closure( @@ -661,13 +751,25 @@ impl<'tcx> Instance<'tcx> { pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { let def_id = tcx.require_lang_item(LangItem::DropInPlace, None); let args = tcx.mk_args(&[ty.into()]); - Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + Instance::expect_resolve( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + args, + ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP), + ) } pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None); let args = tcx.mk_args(&[ty.into()]); - Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + Instance::expect_resolve( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + args, + ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP), + ) } #[instrument(level = "debug", skip(tcx), ret)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9c2bfc12a18..e1bd3ad8ad3 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -148,6 +148,7 @@ mod closure; mod consts; mod context; mod diagnostics; +mod elaborate_impl; mod erase_regions; mod generic_args; mod generics; @@ -1608,7 +1609,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// If the def-id is an associated type that was desugared from a + /// If the `def_id` is an associated type that was desugared from a /// return-position `impl Trait` from a trait, then provide the source info /// about where that RPITIT came from. pub fn opt_rpitit_info(self, def_id: DefId) -> Option<ImplTraitInTraitData> { @@ -1619,6 +1620,16 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Whether the `def_id` is an associated type that was desugared from a + /// `#[const_trait]` or `impl_const`. + pub fn is_effects_desugared_assoc_ty(self, def_id: DefId) -> bool { + if let DefKind::AssocTy = self.def_kind(def_id) { + self.associated_item(def_id).is_effects_desugaring + } else { + false + } + } + pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<FieldIdx> { variant.fields.iter_enumerated().find_map(|(i, field)| { self.hygienic_eq(ident, field.ident(self), variant.def_id).then_some(i) @@ -1957,8 +1968,13 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] + pub fn is_const_trait(self, def_id: DefId) -> bool { + self.trait_def(def_id).constness == hir::Constness::Const + } + + #[inline] pub fn is_const_default_method(self, def_id: DefId) -> bool { - matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait)) + matches!(self.trait_of_item(def_id), Some(trait_id) if self.is_const_trait(trait_id)) } pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index e9b37503bb3..5d6352c57ce 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -46,6 +46,10 @@ pub struct Predicate<'tcx>( ); impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> { + fn as_clause(self) -> Option<ty::Clause<'tcx>> { + self.as_clause() + } + fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { self.is_coinductive(interner) } @@ -173,7 +177,11 @@ pub struct Clause<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>, ); -impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {} +impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> { + fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self { + self.instantiate_supertrait(tcx, trait_ref) + } +} impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> { type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>; @@ -341,6 +349,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> { _ => None, }) } + + pub fn without_auto_traits( + &self, + ) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ { + self.iter().filter(|predicate| { + !matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_)) + }) + } } pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>; diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 3c27df9529a..c165790548d 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -1,5 +1,7 @@ +use std::path::PathBuf; + use crate::ty::GenericArg; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, ShortInstance, Ty, TyCtxt}; use hir::def::Namespace; use rustc_data_structures::fx::FxHashSet; @@ -356,3 +358,31 @@ where with_no_trimmed_paths!(Self::print(t, fmt)) } } + +/// Format instance name that is already known to be too long for rustc. +/// Show only the first 2 types if it is longer than 32 characters to avoid blasting +/// the user's terminal with thousands of lines of type-name. +/// +/// If the type name is longer than before+after, it will be written to a file. +pub fn shrunk_instance_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: ty::Instance<'tcx>, +) -> (String, Option<PathBuf>) { + let s = instance.to_string(); + + // Only use the shrunk version if it's really shorter. + // This also avoids the case where before and after slices overlap. + if s.chars().nth(33).is_some() { + let shrunk = format!("{}", ShortInstance(instance, 4)); + if shrunk == s { + return (s, None); + } + + let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None); + let written_to_path = std::fs::write(&path, s).ok().map(|_| path); + + (shrunk, written_to_path) + } else { + (s, None) + } +} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 19700353f59..df080b2887b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,7 +1,6 @@ use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::query::IntoQueryParam; use crate::query::Providers; -use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing}; use crate::ty::GenericArgKind; use crate::ty::{ ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, @@ -23,6 +22,7 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::FileNameDisplayPreference; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use rustc_type_ir::{elaborate, Upcast as _}; use smallvec::SmallVec; use std::cell::Cell; @@ -1255,14 +1255,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { entry.has_fn_once = true; return; } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) { - let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) + let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref); return; } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) { - let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) + let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); @@ -1343,10 +1343,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let bound_principal_with_self = bound_principal .with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self); - let super_projections: Vec<_> = - super_predicates_for_pretty_printing(cx.tcx(), bound_principal_with_self) - .filter_map(|clause| clause.as_projection_clause()) - .collect(); + let clause: ty::Clause<'tcx> = bound_principal_with_self.upcast(cx.tcx()); + let super_projections: Vec<_> = elaborate::elaborate(cx.tcx(), [clause]) + .filter_only_self() + .filter_map(|clause| clause.as_projection_clause()) + .collect(); let mut projections: Vec<_> = predicates .projection_bounds() @@ -1667,7 +1668,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Some(GlobalAlloc::Static(def_id)) => { p!(write("<static({:?})>", def_id)) } - Some(GlobalAlloc::Function(_)) => p!("<function>"), + Some(GlobalAlloc::Function { .. }) => p!("<function>"), Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), None => p!("<dangling pointer>"), } @@ -1679,7 +1680,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::FnPtr(_) => { // FIXME: We should probably have a helper method to share code with the "Byte strings" // printing above (which also has to handle pointers to all sorts of things). - if let Some(GlobalAlloc::Function(instance)) = + if let Some(GlobalAlloc::Function { instance, .. }) = self.tcx().try_get_global_alloc(prov.alloc_id()) { self.typed_value( @@ -1710,22 +1711,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Bool if int == ScalarInt::FALSE => p!("false"), ty::Bool if int == ScalarInt::TRUE => p!("true"), // Float - ty::Float(ty::FloatTy::F16) => { - let val = Half::try_from(int).unwrap(); - p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" })) - } - ty::Float(ty::FloatTy::F32) => { - let val = Single::try_from(int).unwrap(); - p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" })) - } - ty::Float(ty::FloatTy::F64) => { - let val = Double::try_from(int).unwrap(); - p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" })) - } - ty::Float(ty::FloatTy::F128) => { - let val = Quad::try_from(int).unwrap(); - p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" })) - } + ty::Float(fty) => match fty { + ty::FloatTy::F16 => { + let val = Half::try_from(int).unwrap(); + p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" })) + } + ty::FloatTy::F32 => { + let val = Single::try_from(int).unwrap(); + p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" })) + } + ty::FloatTy::F64 => { + let val = Double::try_from(int).unwrap(); + p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" })) + } + ty::FloatTy::F128 => { + let val = Quad::try_from(int).unwrap(); + p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" })) + } + }, // Int ty::Uint(_) | ty::Int(_) => { let int = diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index b169d672a84..ebf0d7ed737 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -10,18 +10,6 @@ use crate::ty::{self as ty, Ty, TyCtxt}; pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>; -/// Whether aliases should be related structurally or not. Used -/// to adjust the behavior of generalization and combine. -/// -/// This should always be `No` unless in a few special-cases when -/// instantiating canonical responses and in the new solver. Each -/// such case should have a comment explaining why it is used. -#[derive(Debug, Copy, Clone)] -pub enum StructurallyRelateAliases { - Yes, - No, -} - impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> { #[inline] fn relate<R: TypeRelation<TyCtxt<'tcx>>>( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ff40a726fbc..d2b444a066b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -68,6 +68,10 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> { const RETURNED: usize = 1; /// Coroutine has been poisoned. const POISONED: usize = 2; + /// Number of variants to reserve in coroutine state. Corresponds to + /// `UNRESUMED` (beginning of a coroutine) and `RETURNED`/`POISONED` + /// (end of a coroutine) states. + const RESERVED_VARIANTS: usize = 3; const UNRESUMED_NAME: &'static str = "Unresumed"; const RETURNED_NAME: &'static str = "Returned"; @@ -116,7 +120,7 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> { Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME), Self::RETURNED => Cow::from(Self::RETURNED_NAME), Self::POISONED => Cow::from(Self::POISONED_NAME), - _ => Cow::from(format!("Suspend{}", v.as_usize() - 3)), + _ => Cow::from(format!("Suspend{}", v.as_usize() - Self::RESERVED_VARIANTS)), } } @@ -807,6 +811,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { Ty::new_var(tcx, vid) } + fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamTy) -> Self { + Ty::new_param(tcx, param.index, param.name) + } + + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType) -> Self { + Ty::new_placeholder(tcx, placeholder) + } + fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self { Ty::new_bound(interner, debruijn, var) } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 4dba97c3b5b..da5860043c9 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -18,6 +18,9 @@ pub struct TraitDef { pub safety: hir::Safety, + /// Whether this trait has been annotated with `#[const_trait]`. + pub constness: hir::Constness, + /// If `true`, then this trait had the `#[rustc_paren_sugar]` /// attribute, indicating that it should be used with `Foo()` /// sugar. This is a temporary thing -- eventually any trait will @@ -31,7 +34,7 @@ pub struct TraitDef { /// and thus `impl`s of it are allowed to overlap. pub is_marker: bool, - /// If `true`, then this trait has to `#[rustc_coinductive]` attribute or + /// If `true`, then this trait has the `#[rustc_coinductive]` attribute or /// is an auto trait. This indicates that trait solver cycles involving an /// `X: ThisTrait` goal are accepted. /// @@ -40,6 +43,11 @@ pub struct TraitDef { /// also have already switched to the new trait solver. pub is_coinductive: bool, + /// If `true`, then this trait has the `#[fundamental]` attribute. This + /// affects how conherence computes whether a trait may have trait implementations + /// added in the future. + pub is_fundamental: bool, + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]` /// attribute, indicating that editions before 2021 should not consider this trait /// during method dispatch if the receiver is an array. diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index dc3c84f9e43..466c3b93f8e 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -3,8 +3,6 @@ use std::fmt; use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; use rustc_ast::Mutability; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; use rustc_macros::HashStable; #[derive(Clone, Copy, PartialEq, HashStable)] @@ -42,45 +40,12 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> { impl<'tcx> TyCtxt<'tcx> { pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = &[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign]; - - pub fn supertrait_def_ids(self, trait_def_id: DefId) -> SupertraitDefIds<'tcx> { - SupertraitDefIds { - tcx: self, - stack: vec![trait_def_id], - visited: Some(trait_def_id).into_iter().collect(), - } - } } pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0; pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1; pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2; -pub struct SupertraitDefIds<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec<DefId>, - visited: FxHashSet<DefId>, -} - -impl Iterator for SupertraitDefIds<'_> { - type Item = DefId; - - fn next(&mut self) -> Option<DefId> { - let def_id = self.stack.pop()?; - let predicates = self.tcx.super_predicates_of(def_id); - let visited = &mut self.visited; - self.stack.extend( - predicates - .predicates - .iter() - .filter_map(|(pred, _)| pred.as_trait_clause()) - .map(|trait_ref| trait_ref.def_id()) - .filter(|&super_def_id| visited.insert(super_def_id)), - ); - Some(def_id) - } -} - // Note that we don't have access to a self type here, this has to be purely based on the trait (and // supertrait) definitions. That means we can't call into the same vtable_entries code since that // returns a specific instantiation (e.g., with Vacant slots when bounds aren't satisfied). The goal diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index e0f204a687f..efcaf89081f 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -78,23 +78,6 @@ impl<'tcx> GenericArg<'tcx> { pub fn walk(self) -> TypeWalker<'tcx> { TypeWalker::new(self) } - - /// Iterator that walks the immediate children of `self`. Hence - /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]` - /// (but not `i32`, like `walk`). - /// - /// Iterator only walks items once. - /// It accepts visited set, updates it with all visited types - /// and skips any types that are already there. - pub fn walk_shallow( - self, - visited: &mut SsoHashSet<GenericArg<'tcx>>, - ) -> impl Iterator<Item = GenericArg<'tcx>> { - let mut stack = SmallVec::new(); - push_inner(&mut stack, self); - stack.retain(|a| visited.insert(*a)); - stack.into_iter() - } } impl<'tcx> Ty<'tcx> { diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs index dc1d73684f4..0815c291173 100644 --- a/compiler/rustc_middle/src/util/call_kind.rs +++ b/compiler/rustc_middle/src/util/call_kind.rs @@ -98,7 +98,7 @@ pub fn call_kind<'tcx>( Some(CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) }) } else if is_deref { let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::resolve(tcx, param_env, deref_target, method_args).transpose() + Instance::try_resolve(tcx, param_env, deref_target, method_args).transpose() }); if let Some(Ok(instance)) = deref_target { let deref_target_ty = instance.ty(tcx, param_env); diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index c1d645aa42c..5ccbd7c59cf 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -1,3 +1,4 @@ +use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops}; use crate::build::ForGuard::OutsideGuard; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use rustc_middle::middle::region::Scope; @@ -189,38 +190,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let initializer_span = this.thir[*initializer].span; let scope = (*init_scope, source_info); - let failure = unpack!( - block = this.in_scope(scope, *lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - None, - Some((Some(&destination), initializer_span)), - ); - this.visit_primary_bindings( - pattern, - UserTypeProjections::none(), - &mut |this, _, _, node, span, _, _| { - this.storage_live_binding( - block, - node, - span, - OutsideGuard, - true, - ); - }, - ); - this.ast_let_else( - block, - *initializer, - initializer_span, - *else_block, - &last_remainder_scope, - pattern, - ) - }) - ); + let failure_and_block = this.in_scope(scope, *lint_level, |this| { + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + None, + Some((Some(&destination), initializer_span)), + ); + this.visit_primary_bindings( + pattern, + UserTypeProjections::none(), + &mut |this, _, _, node, span, _, _| { + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); + }, + ); + let else_block_span = this.thir[*else_block].span; + let (matching, failure) = + this.in_if_then_scope(last_remainder_scope, else_block_span, |this| { + this.lower_let_expr( + block, + *initializer, + pattern, + None, + initializer_span, + DeclareLetBindings::No, + EmitStorageLive::No, + ) + }); + matching.and(failure) + }); + let failure = unpack!(block = failure_and_block); this.cfg.goto(failure, source_info, failure_entry); if let Some(source_scope) = visibility_scope { @@ -292,7 +298,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern, UserTypeProjections::none(), &mut |this, _, _, node, span, _, _| { - this.storage_live_binding(block, node, span, OutsideGuard, true); + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); this.schedule_drop_for_binding(node, span, OutsideGuard); }, ) diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 876faca5172..204ee45bfa2 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -2,7 +2,7 @@ use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind}; use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir}; use rustc_middle::ty::TyCtxt; @@ -13,16 +13,25 @@ use crate::build::{Builder, CFG}; mod mcdc; -pub(crate) struct BranchInfoBuilder { +/// Collects coverage-related information during MIR building, to eventually be +/// turned into a function's [`CoverageInfoHi`] when MIR building is complete. +pub(crate) struct CoverageInfoBuilder { /// Maps condition expressions to their enclosing `!`, for better instrumentation. nots: FxHashMap<ExprId, NotInfo>, markers: BlockMarkerGen, - branch_spans: Vec<BranchSpan>, + /// Present if branch coverage is enabled. + branch_info: Option<BranchInfo>, + /// Present if MC/DC coverage is enabled. mcdc_info: Option<MCDCInfoBuilder>, } +#[derive(Default)] +struct BranchInfo { + branch_spans: Vec<BranchSpan>, +} + #[derive(Clone, Copy)] struct NotInfo { /// When visiting the associated expression as a branch condition, treat this @@ -62,20 +71,20 @@ impl BlockMarkerGen { } } -impl BranchInfoBuilder { - /// Creates a new branch info builder, but only if branch coverage instrumentation +impl CoverageInfoBuilder { + /// Creates a new coverage info builder, but only if coverage instrumentation /// is enabled and `def_id` represents a function that is eligible for coverage. pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> { - if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { - Some(Self { - nots: FxHashMap::default(), - markers: BlockMarkerGen::default(), - branch_spans: vec![], - mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new), - }) - } else { - None + if !tcx.sess.instrument_coverage() || !tcx.is_eligible_for_coverage(def_id) { + return None; } + + Some(Self { + nots: FxHashMap::default(), + markers: BlockMarkerGen::default(), + branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default), + mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new), + }) } /// Unary `!` expressions inside an `if` condition are lowered by lowering @@ -88,6 +97,12 @@ impl BranchInfoBuilder { pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) { assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. }); + // The information collected by this visitor is only needed when branch + // coverage or higher is enabled. + if self.branch_info.is_none() { + return; + } + self.visit_with_not_info( thir, unary_not, @@ -137,40 +152,40 @@ impl BranchInfoBuilder { false_block, inject_block_marker, ); - } else { - let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); - let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); - - self.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); + return; } + + // Bail out if branch coverage is not enabled. + let Some(branch_info) = self.branch_info.as_mut() else { return }; + + let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); + + branch_info.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); } - pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> { - let Self { - nots: _, - markers: BlockMarkerGen { num_block_markers }, - branch_spans, - mcdc_info, - } = self; + pub(crate) fn into_done(self) -> Box<CoverageInfoHi> { + let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info, mcdc_info } = + self; - if num_block_markers == 0 { - assert!(branch_spans.is_empty()); - return None; - } + let branch_spans = + branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default(); let (mcdc_decision_spans, mcdc_branch_spans) = mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default(); - Some(Box::new(mir::coverage::BranchInfo { + // For simplicity, always return an info struct (without Option), even + // if there's nothing interesting in it. + Box::new(CoverageInfoHi { num_block_markers, branch_spans, mcdc_branch_spans, mcdc_decision_spans, - })) + }) } } @@ -184,7 +199,7 @@ impl<'tcx> Builder<'_, 'tcx> { block: &mut BasicBlock, ) { // Bail out if condition coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; + let Some(coverage_info) = self.coverage_info.as_mut() else { return }; if !self.tcx.sess.instrument_coverage_condition() { return; }; @@ -224,7 +239,7 @@ impl<'tcx> Builder<'_, 'tcx> { ); // Separate path for handling branches when MC/DC is enabled. - branch_info.register_two_way_branch( + coverage_info.register_two_way_branch( self.tcx, &mut self.cfg, source_info, @@ -247,12 +262,12 @@ impl<'tcx> Builder<'_, 'tcx> { mut then_block: BasicBlock, mut else_block: BasicBlock, ) { - // Bail out if branch coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; + // Bail out if coverage is not enabled for this function. + let Some(coverage_info) = self.coverage_info.as_mut() else { return }; // If this condition expression is nested within one or more `!` expressions, // replace it with the enclosing `!` collected by `visit_unary_not`. - if let Some(&NotInfo { enclosing_not, is_flipped }) = branch_info.nots.get(&expr_id) { + if let Some(&NotInfo { enclosing_not, is_flipped }) = coverage_info.nots.get(&expr_id) { expr_id = enclosing_not; if is_flipped { std::mem::swap(&mut then_block, &mut else_block); @@ -261,7 +276,7 @@ impl<'tcx> Builder<'_, 'tcx> { let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - branch_info.register_two_way_branch( + coverage_info.register_two_way_branch( self.tcx, &mut self.cfg, source_info, @@ -280,13 +295,11 @@ impl<'tcx> Builder<'_, 'tcx> { true_block: BasicBlock, false_block: BasicBlock, ) { - // Bail out if branch coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; - - // FIXME(#124144) This may need special handling when MC/DC is enabled. + // Bail out if coverage is not enabled for this function. + let Some(coverage_info) = self.coverage_info.as_mut() else { return }; let source_info = SourceInfo { span: pattern.span, scope: self.source_scope }; - branch_info.register_two_way_branch( + coverage_info.register_two_way_branch( self.tcx, &mut self.cfg, source_info, diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs index f97e9ef60a2..3aa6e708476 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs @@ -250,24 +250,24 @@ impl MCDCInfoBuilder { impl Builder<'_, '_> { pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { - if let Some(branch_info) = self.coverage_branch_info.as_mut() - && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + if let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() { mcdc_info.state.record_conditions(logical_op, span); } } pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) { - if let Some(branch_info) = self.coverage_branch_info.as_mut() - && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + if let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() { mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default()); }; } pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) { - if let Some(branch_info) = self.coverage_branch_info.as_mut() - && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + if let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() { if mcdc_info.state.decision_ctx_stack.pop().is_none() { bug!("Unexpected empty decision stack"); diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index a0a512a2eff..f6ebcbcbdc9 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -62,7 +62,7 @@ pub(super) fn build_custom_mir<'tcx>( tainted_by_errors: None, injection_phase: None, pass_count: 0, - coverage_branch_info: None, + coverage_info_hi: None, function_coverage_info: None, }; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 4b62afa61bb..91a3b53cc79 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -130,7 +130,7 @@ fn convert_to_hir_projections_and_truncate_for_capture( /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of /// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`. /// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`. -/// 2. Since we only look at the projections here function will return `bar.x` as an a valid +/// 2. Since we only look at the projections here function will return `bar.x` as a valid /// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections /// list are being applied to the same root variable. fn is_ancestor_or_same_capture( diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 76bdc26a501..942c69b5c0a 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -1,6 +1,7 @@ //! See docs in build/expr/mod.rs use crate::build::expr::category::{Category, RvalueFunc}; +use crate::build::matches::DeclareLetBindings; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary}; use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxHashMap; @@ -86,7 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { cond, Some(condition_scope), // Temp scope source_info, - true, // Declare `let` bindings normally + DeclareLetBindings::Yes, // Declare `let` bindings normally )); // Lower the `then` arm into its block. @@ -163,7 +164,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, // This flag controls how inner `let` expressions are lowered, // but either way there shouldn't be any of those in here. - true, + DeclareLetBindings::LetNotPermitted, ) }); let (short_circuit, continuation, constant) = match op { diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 2bdeb579a02..88b76c46c90 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -2,7 +2,9 @@ use crate::build::scope::BreakableTarget; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use rustc_middle::middle::region; use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::thir::*; +use rustc_span::source_map::Spanned; use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -91,9 +93,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Return { value } => { this.break_scope(block, value, BreakableTarget::Return, source_info) } - // FIXME(explicit_tail_calls): properly lower tail calls here ExprKind::Become { value } => { - this.break_scope(block, Some(value), BreakableTarget::Return, source_info) + let v = &this.thir[value]; + let ExprKind::Scope { value, lint_level, region_scope } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + let v = &this.thir[value]; + let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + this.in_scope((region_scope, source_info), lint_level, |this| { + let fun = unpack!(block = this.as_local_operand(block, fun)); + let args: Box<[_]> = args + .into_iter() + .copied() + .map(|arg| Spanned { + node: unpack!(block = this.as_local_call_operand(block, arg)), + span: this.thir.exprs[arg].span, + }) + .collect(); + + this.record_operands_moved(&args); + + debug!("expr_into_dest: fn_span={:?}", fn_span); + + unpack!(block = this.break_for_tail_call(block, &args, source_info)); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::TailCall { func: fun, args, fn_span }, + ); + + this.cfg.start_new_block().unit() + }) } _ => { assert!( diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 932406fd1aa..5695c881ecc 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -21,12 +21,14 @@ use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use rustc_target::abi::VariantIdx; use tracing::{debug, instrument}; +use util::visit_bindings; // helper functions, broken out by category: mod simplify; mod test; mod util; +use std::assert_matches::assert_matches; use std::borrow::Borrow; use std::mem; @@ -38,9 +40,50 @@ struct ThenElseArgs { /// `self.local_scope()` is used. temp_scope_override: Option<region::Scope>, variable_source_info: SourceInfo, + /// Determines how bindings should be handled when lowering `let` expressions. + /// /// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`]. - /// When false (for match guards), `let` bindings won't be declared. - declare_let_bindings: bool, + declare_let_bindings: DeclareLetBindings, +} + +/// Should lowering a `let` expression also declare its bindings? +/// +/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`]. +#[derive(Clone, Copy)] +pub(crate) enum DeclareLetBindings { + /// Yes, declare `let` bindings as normal for `if` conditions. + Yes, + /// No, don't declare `let` bindings, because the caller declares them + /// separately due to special requirements. + /// + /// Used for match guards and let-else. + No, + /// Let expressions are not permitted in this context, so it is a bug to + /// try to lower one (e.g inside lazy-boolean-or or boolean-not). + LetNotPermitted, +} + +/// Used by [`Builder::bind_matched_candidate_for_arm_body`] to determine +/// whether or not to call [`Builder::storage_live_binding`] to emit +/// [`StatementKind::StorageLive`]. +#[derive(Clone, Copy)] +pub(crate) enum EmitStorageLive { + /// Yes, emit `StorageLive` as normal. + Yes, + /// No, don't emit `StorageLive`. The caller has taken responsibility for + /// emitting `StorageLive` as appropriate. + No, +} + +/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`] +/// to decide whether to schedule drops. +#[derive(Clone, Copy, Debug)] +pub(crate) enum ScheduleDrops { + /// Yes, the relevant functions should also schedule drops as appropriate. + Yes, + /// No, don't schedule drops. The caller has taken responsibility for any + /// appropriate drops. + No, } impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -56,7 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_id: ExprId, temp_scope_override: Option<region::Scope>, variable_source_info: SourceInfo, - declare_let_bindings: bool, + declare_let_bindings: DeclareLetBindings, ) -> BlockAnd<()> { self.then_else_break_inner( block, @@ -90,13 +133,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.then_else_break_inner( block, lhs, - ThenElseArgs { declare_let_bindings: true, ..args }, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, ) }); let rhs_success_block = unpack!(this.then_else_break_inner( failure_block, rhs, - ThenElseArgs { declare_let_bindings: true, ..args }, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, )); // Make the LHS and RHS success arms converge to a common block. @@ -111,8 +160,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Improve branch coverage instrumentation by noting conditions // nested within one or more `!` expressions. // (Skipped if branch coverage is not enabled.) - if let Some(branch_info) = this.coverage_branch_info.as_mut() { - branch_info.visit_unary_not(this.thir, expr_id); + if let Some(coverage_info) = this.coverage_info.as_mut() { + coverage_info.visit_unary_not(this.thir, expr_id); } let local_scope = this.local_scope(); @@ -126,7 +175,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.then_else_break_inner( block, arg, - ThenElseArgs { declare_let_bindings: true, ..args }, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, ) }); this.break_for_else(success_block, args.variable_source_info); @@ -146,6 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(args.variable_source_info.scope), args.variable_source_info.span, args.declare_let_bindings, + EmitStorageLive::Yes, ), _ => { let mut block = block; @@ -314,13 +367,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_start_span = span.shrink_to_lo().to(scrutinee_span); - let fake_borrow_temps = self.lower_match_tree( + // The set of places that we are creating fake borrows of. If there are no match guards then + // we don't need any fake borrows, so don't track them. + let fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard { + util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base()) + } else { + Vec::new() + }; + + self.lower_match_tree( block, scrutinee_span, &scrutinee_place, match_start_span, - match_has_guard, &mut candidates, + false, ); self.lower_match_arms( @@ -375,89 +436,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .collect() } - /// Create the decision tree for the match expression, starting from `block`. - /// - /// Modifies `candidates` to store the bindings and type ascriptions for - /// that candidate. - /// - /// Returns the places that need fake borrows because we bind or test them. - fn lower_match_tree<'pat>( - &mut self, - block: BasicBlock, - scrutinee_span: Span, - scrutinee_place_builder: &PlaceBuilder<'tcx>, - match_start_span: Span, - match_has_guard: bool, - candidates: &mut [&mut Candidate<'pat, 'tcx>], - ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { - // The set of places that we are creating fake borrows of. If there are no match guards then - // we don't need any fake borrows, so don't track them. - let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard { - util::collect_fake_borrows( - self, - candidates, - scrutinee_span, - scrutinee_place_builder.base(), - ) - } else { - Vec::new() - }; - - // See the doc comment on `match_candidates` for why we have an - // otherwise block. Match checking will ensure this is actually - // unreachable. - let otherwise_block = self.cfg.start_new_block(); - - // This will generate code to test scrutinee_place and - // branch to the appropriate arm block - self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates); - - let source_info = self.source_info(scrutinee_span); - - // Matching on a `scrutinee_place` with an uninhabited type doesn't - // generate any memory reads by itself, and so if the place "expression" - // contains unsafe operations like raw pointer dereferences or union - // field projections, we wouldn't know to require an `unsafe` block - // around a `match` equivalent to `std::intrinsics::unreachable()`. - // See issue #47412 for this hole being discovered in the wild. - // - // HACK(eddyb) Work around the above issue by adding a dummy inspection - // of `scrutinee_place`, specifically by applying `ReadForMatch`. - // - // NOTE: ReadForMatch also checks that the scrutinee is initialized. - // This is currently needed to not allow matching on an uninitialized, - // uninhabited value. If we get never patterns, those will check that - // the place is initialized, and so this read would only be used to - // check safety. - let cause_matched_place = FakeReadCause::ForMatchedPlace(None); - - if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { - self.cfg.push_fake_read( - otherwise_block, - source_info, - cause_matched_place, - scrutinee_place, - ); - } - - self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); - - // Link each leaf candidate to the `pre_binding_block` of the next one. - let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; - - for candidate in candidates { - candidate.visit_leaves(|leaf_candidate| { - if let Some(ref mut prev) = previous_candidate { - assert!(leaf_candidate.false_edge_start_block.is_some()); - prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; - } - previous_candidate = Some(leaf_candidate); - }); - } - - fake_borrows - } - /// Lower the bindings, guards and arm bodies of a `match` expression. /// /// The decision tree should have already been created @@ -513,7 +491,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &fake_borrow_temps, scrutinee_span, Some((arm, match_scope)), - false, + EmitStorageLive::Yes, ); this.fixed_temps_scope = old_dedup_scope; @@ -558,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, - storages_alive: bool, + emit_storage_live: EmitStorageLive, ) -> BasicBlock { if candidate.subcandidates.is_empty() { // Avoid generating another `BasicBlock` when we only have one @@ -569,8 +547,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrow_temps, scrutinee_span, arm_match_scope, - true, - storages_alive, + ScheduleDrops::Yes, + emit_storage_live, ) } else { // It's helpful to avoid scheduling drops multiple times to save @@ -588,7 +566,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // To handle this we instead unschedule it's drop after each time // we lower the guard. let target_block = self.cfg.start_new_block(); - let mut schedule_drops = true; + let mut schedule_drops = ScheduleDrops::Yes; let arm = arm_match_scope.unzip().0; // We keep a stack of all of the bindings and type ascriptions // from the parent candidates that we visit, that also need to @@ -607,10 +585,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span, arm_match_scope, schedule_drops, - storages_alive, + emit_storage_live, ); if arm.is_none() { - schedule_drops = false; + schedule_drops = ScheduleDrops::No; } self.cfg.goto(binding_end, outer_source_info, target_block); }, @@ -636,8 +614,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match irrefutable_pat.kind { // Optimize the case of `let x = ...` to write directly into `x` PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => { - let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); + let place = self.storage_live_binding( + block, + var, + irrefutable_pat.span, + OutsideGuard, + ScheduleDrops::Yes, + ); unpack!(block = self.expr_into_dest(place, block, initializer_id)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. @@ -670,8 +653,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ascription: thir::Ascription { ref annotation, variance: _ }, } => { - let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); + let place = self.storage_live_binding( + block, + var, + irrefutable_pat.span, + OutsideGuard, + ScheduleDrops::Yes, + ); unpack!(block = self.expr_into_dest(place, block, initializer_id)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. @@ -728,62 +716,56 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { set_match_place: bool, ) -> BlockAnd<()> { let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self); - let fake_borrow_temps = self.lower_match_tree( - block, - irrefutable_pat.span, - &initializer, - irrefutable_pat.span, - false, - &mut [&mut candidate], - ); // For matches and function arguments, the place that is being matched // can be set when creating the variables. But the place for // let PATTERN = ... might not even exist until we do the assignment. // so we set it here instead. if set_match_place { - let mut next = Some(&candidate); - while let Some(candidate_ref) = next.take() { - for binding in &candidate_ref.extra_data.bindings { + // `try_to_place` may fail if it is unable to resolve the given `PlaceBuilder` inside a + // closure. In this case, we don't want to include a scrutinee place. + // `scrutinee_place_builder` will fail for destructured assignments. This is because a + // closure only captures the precise places that it will read and as a result a closure + // may not capture the entire tuple/struct and rather have individual places that will + // be read in the final MIR. + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // let (v1, v2) = foo; + // }; + // ``` + if let Some(place) = initializer.try_to_place(self) { + visit_bindings(&[&mut candidate], |binding: &Binding<'_>| { let local = self.var_local_id(binding.var_id, OutsideGuard); - // `try_to_place` may fail if it is unable to resolve the given - // `PlaceBuilder` inside a closure. In this case, we don't want to include - // a scrutinee place. `scrutinee_place_builder` will fail for destructured - // assignments. This is because a closure only captures the precise places - // that it will read and as a result a closure may not capture the entire - // tuple/struct and rather have individual places that will be read in the - // final MIR. - // Example: - // ``` - // let foo = (0, 1); - // let c = || { - // let (v1, v2) = foo; - // }; - // ``` - if let Some(place) = initializer.try_to_place(self) { - let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((ref mut match_place, _)), - .. - })) = **self.local_decls[local].local_info.as_mut().assert_crate_local() - else { - bug!("Let binding to non-user variable.") - }; + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((ref mut match_place, _)), + .. + })) = **self.local_decls[local].local_info.as_mut().assert_crate_local() + { *match_place = Some(place); - } - } - // All of the subcandidates should bind the same locals, so we - // only visit the first one. - next = candidate_ref.subcandidates.get(0) + } else { + bug!("Let binding to non-user variable.") + }; + }); } } + self.lower_match_tree( + block, + irrefutable_pat.span, + &initializer, + irrefutable_pat.span, + &mut [&mut candidate], + false, + ); self.bind_pattern( self.source_info(irrefutable_pat.span), candidate, - fake_borrow_temps.as_slice(), + &[], irrefutable_pat.span, None, - false, + EmitStorageLive::Yes, ) .unit() } @@ -859,13 +841,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + /// Emits a [`StatementKind::StorageLive`] for the given var, and also + /// schedules a drop if requested (and possible). pub(crate) fn storage_live_binding( &mut self, block: BasicBlock, var: LocalVarId, span: Span, for_guard: ForGuard, - schedule_drop: bool, + schedule_drop: ScheduleDrops, ) -> Place<'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); @@ -873,7 +857,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Although there is almost always scope for given variable in corner cases // like #92893 we might get variable with no scope. if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) - && schedule_drop + && matches!(schedule_drop, ScheduleDrops::Yes) { self.schedule_drop(span, region_scope, local_id, DropKind::Storage); } @@ -1306,6 +1290,79 @@ pub(crate) struct ArmHasGuard(pub(crate) bool); // Main matching algorithm impl<'a, 'tcx> Builder<'a, 'tcx> { + /// The entrypoint of the matching algorithm. Create the decision tree for the match expression, + /// starting from `block`. + /// + /// Modifies `candidates` to store the bindings and type ascriptions for + /// that candidate. + /// + /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`) + /// or not (for `let` and `match`). In the refutable case we return the block to which we branch + /// on failure. + fn lower_match_tree<'pat>( + &mut self, + block: BasicBlock, + scrutinee_span: Span, + scrutinee_place_builder: &PlaceBuilder<'tcx>, + match_start_span: Span, + candidates: &mut [&mut Candidate<'pat, 'tcx>], + refutable: bool, + ) -> BasicBlock { + // See the doc comment on `match_candidates` for why we have an otherwise block. + let otherwise_block = self.cfg.start_new_block(); + + // This will generate code to test scrutinee_place and branch to the appropriate arm block + self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates); + + // Link each leaf candidate to the `false_edge_start_block` of the next one. + let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; + for candidate in candidates { + candidate.visit_leaves(|leaf_candidate| { + if let Some(ref mut prev) = previous_candidate { + assert!(leaf_candidate.false_edge_start_block.is_some()); + prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; + } + previous_candidate = Some(leaf_candidate); + }); + } + + if refutable { + // In refutable cases there's always at least one candidate, and we want a false edge to + // the failure block. + previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block) + } else { + // Match checking ensures `otherwise_block` is actually unreachable in irrefutable + // cases. + let source_info = self.source_info(scrutinee_span); + + // Matching on a scrutinee place of an uninhabited type doesn't generate any memory + // reads by itself, and so if the place is uninitialized we wouldn't know. In order to + // disallow the following: + // ```rust + // let x: !; + // match x {} + // ``` + // we add a dummy read on the place. + // + // NOTE: If we require never patterns for empty matches, those will check that the place + // is initialized, and so this read would no longer be needed. + let cause_matched_place = FakeReadCause::ForMatchedPlace(None); + + if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { + self.cfg.push_fake_read( + otherwise_block, + source_info, + cause_matched_place, + scrutinee_place, + ); + } + + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + } + + otherwise_block + } + /// The main match algorithm. It begins with a set of candidates /// `candidates` and has the job of generating code to determine /// which of these candidates, if any, is the correct one. The @@ -1411,6 +1468,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { break; } } + if expand_until != 0 { + expand_until = i + 1; + } } let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until); @@ -1997,53 +2057,69 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Pat binding - used for `let` and function parameters as well. impl<'a, 'tcx> Builder<'a, 'tcx> { - /// If the bindings have already been declared, set `declare_bindings` to - /// `false` to avoid duplicated bindings declaration. Used for if-let guards. + /// Lowers a `let` expression that appears in a suitable context + /// (e.g. an `if` condition or match guard). + /// + /// Also used for lowering let-else statements, since they have similar + /// needs despite not actually using `let` expressions. + /// + /// Use [`DeclareLetBindings`] to control whether the `let` bindings are + /// declared or not. pub(crate) fn lower_let_expr( &mut self, mut block: BasicBlock, expr_id: ExprId, pat: &Pat<'tcx>, source_scope: Option<SourceScope>, - span: Span, - declare_bindings: bool, + scope_span: Span, + declare_let_bindings: DeclareLetBindings, + emit_storage_live: EmitStorageLive, ) -> BlockAnd<()> { let expr_span = self.thir[expr_id].span; - let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span)); - let wildcard = Pat::wildcard_from_ty(pat.ty); - let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self); - let mut otherwise_candidate = - Candidate::new(expr_place_builder.clone(), &wildcard, false, self); - let fake_borrow_temps = self.lower_match_tree( + let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span)); + let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self); + let otherwise_block = self.lower_match_tree( block, + expr_span, + &scrutinee, pat.span, - &expr_place_builder, - pat.span, - false, - &mut [&mut guard_candidate, &mut otherwise_candidate], + &mut [&mut candidate], + true, ); - let expr_place = expr_place_builder.try_to_place(self); - let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span)); - let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap(); - self.break_for_else(otherwise_post_guard_block, self.source_info(expr_span)); - if declare_bindings { - self.declare_bindings(source_scope, pat.span.to(span), pat, None, opt_expr_place); + self.break_for_else(otherwise_block, self.source_info(expr_span)); + + match declare_let_bindings { + DeclareLetBindings::Yes => { + let expr_place = scrutinee.try_to_place(self); + let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span)); + self.declare_bindings( + source_scope, + pat.span.to(scope_span), + pat, + None, + opt_expr_place, + ); + } + DeclareLetBindings::No => {} // Caller is responsible for bindings. + DeclareLetBindings::LetNotPermitted => { + self.tcx.dcx().span_bug(expr_span, "let expression not expected in this context") + } } - let post_guard_block = self.bind_pattern( + let success = self.bind_pattern( self.source_info(pat.span), - guard_candidate, - fake_borrow_temps.as_slice(), + candidate, + &[], expr_span, None, - false, + emit_storage_live, ); // If branch coverage is enabled, record this branch. - self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block); + self.visit_coverage_conditional_let(pat, success, otherwise_block); - post_guard_block.unit() + success.unit() } /// Initializes each of the bindings from the candidate by @@ -2061,8 +2137,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, - schedule_drops: bool, - storages_alive: bool, + schedule_drops: ScheduleDrops, + emit_storage_live: EmitStorageLive, ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); @@ -2091,14 +2167,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return self.cfg.start_new_block(); } - self.ascribe_types( - block, - parent_data - .iter() - .flat_map(|d| &d.ascriptions) - .cloned() - .chain(candidate.extra_data.ascriptions), - ); + let ascriptions = parent_data + .iter() + .flat_map(|d| &d.ascriptions) + .cloned() + .chain(candidate.extra_data.ascriptions); + let bindings = + parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); + + self.ascribe_types(block, ascriptions); // rust-lang/rust#27282: The `autoref` business deserves some // explanation here. @@ -2185,12 +2262,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && let Some(guard) = arm.guard { let tcx = self.tcx; - let bindings = - parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); - let guard_frame = - GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() }; + let guard_frame = GuardFrame { + locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(), + }; debug!("entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); @@ -2211,7 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard, None, // Use `self.local_scope()` as the temp scope this.source_info(arm.span), - false, // For guards, `let` bindings are declared separately + DeclareLetBindings::No, // For guards, `let` bindings are declared separately ) }); @@ -2263,11 +2339,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` // // and that is clearly not correct. - let by_value_bindings = parent_data - .iter() - .flat_map(|d| &d.bindings) - .chain(&candidate.extra_data.bindings) - .filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); + let by_value_bindings = + bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. for binding in by_value_bindings.clone() { @@ -2275,12 +2348,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause = FakeReadCause::ForGuardBinding; self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id)); } - assert!(schedule_drops, "patterns with guards must schedule drops"); + assert_matches!( + schedule_drops, + ScheduleDrops::Yes, + "patterns with guards must schedule drops" + ); self.bind_matched_candidate_for_arm_body( post_guard_block, - true, + ScheduleDrops::Yes, by_value_bindings, - storages_alive, + emit_storage_live, ); post_guard_block @@ -2291,8 +2368,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.bind_matched_candidate_for_arm_body( block, schedule_drops, - parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings), - storages_alive, + bindings, + emit_storage_live, ); block } @@ -2328,7 +2405,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_matched_candidate_for_guard<'b>( &mut self, block: BasicBlock, - schedule_drops: bool, + schedule_drops: ScheduleDrops, bindings: impl IntoIterator<Item = &'b Binding<'tcx>>, ) where 'tcx: 'b, @@ -2381,9 +2458,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_matched_candidate_for_arm_body<'b>( &mut self, block: BasicBlock, - schedule_drops: bool, + schedule_drops: ScheduleDrops, bindings: impl IntoIterator<Item = &'b Binding<'tcx>>, - storages_alive: bool, + emit_storage_live: EmitStorageLive, ) where 'tcx: 'b, { @@ -2393,21 +2470,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Assign each of the bindings. This may trigger moves out of the candidate. for binding in bindings { let source_info = self.source_info(binding.span); - let local = if storages_alive { + let local = match emit_storage_live { // Here storages are already alive, probably because this is a binding // from let-else. // We just need to schedule drop for the value. - self.var_local_id(binding.var_id, OutsideGuard).into() - } else { - self.storage_live_binding( + EmitStorageLive::No => self.var_local_id(binding.var_id, OutsideGuard).into(), + EmitStorageLive::Yes => self.storage_live_binding( block, binding.var_id, binding.span, OutsideGuard, schedule_drops, - ) + ), }; - if schedule_drops { + if matches!(schedule_drops, ScheduleDrops::Yes) { self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); } let rvalue = match binding.binding_mode.0 { @@ -2493,55 +2569,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!(?locals); self.var_indices.insert(var_id, locals); } - - pub(crate) fn ast_let_else( - &mut self, - mut block: BasicBlock, - init_id: ExprId, - initializer_span: Span, - else_block: BlockId, - let_else_scope: ®ion::Scope, - pattern: &Pat<'tcx>, - ) -> BlockAnd<BasicBlock> { - let else_block_span = self.thir[else_block].span; - let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| { - let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span)); - let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild }; - let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this); - let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this); - let fake_borrow_temps = this.lower_match_tree( - block, - initializer_span, - &scrutinee, - pattern.span, - false, - &mut [&mut candidate, &mut wildcard], - ); - // This block is for the matching case - let matching = this.bind_pattern( - this.source_info(pattern.span), - candidate, - fake_borrow_temps.as_slice(), - initializer_span, - None, - true, - ); - // This block is for the failure case - let failure = this.bind_pattern( - this.source_info(else_block_span), - wildcard, - fake_borrow_temps.as_slice(), - initializer_span, - None, - true, - ); - - // If branch coverage is enabled, record this branch. - this.visit_coverage_conditional_let(pattern, matching, failure); - - this.break_for_else(failure, this.source_info(initializer_span)); - matching.unit() - }); - matching.and(failure) - } } diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 630d0b9438d..3bec154e1df 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase}; use crate::build::Builder; @@ -269,18 +271,6 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { } } -pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> { - cx: &'a mut Builder<'b, 'tcx>, - /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from - /// bindings inside deref patterns. - scrutinee_base: PlaceBase, - /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest - /// borrow (i.e. Deep > Shallow). - /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are - /// dereferences are also borrowed with the same of stronger borrow kind. - fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>, -} - /// Determine the set of places that have to be stable across match guards. /// /// Returns a list of places that need a fake borrow along with a local to store it. @@ -344,6 +334,18 @@ pub(super) fn collect_fake_borrows<'tcx>( .collect() } +pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> { + cx: &'a mut Builder<'b, 'tcx>, + /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from + /// bindings inside deref patterns. + scrutinee_base: PlaceBase, + /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest + /// borrow (i.e. Deep > Shallow). + /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are + /// dereferences are also borrowed with the same of stronger borrow kind. + fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>, +} + impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { // Fake borrow this place and its dereference prefixes. fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) { @@ -457,6 +459,57 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } +/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we +/// only explore the first one of each or-pattern. +pub(super) fn visit_bindings<'tcx>( + candidates: &[&mut Candidate<'_, 'tcx>], + f: impl FnMut(&Binding<'tcx>), +) { + let mut visitor = BindingsVisitor { f, phantom: PhantomData }; + for candidate in candidates.iter() { + visitor.visit_candidate(candidate); + } +} + +pub(super) struct BindingsVisitor<'tcx, F> { + f: F, + phantom: PhantomData<&'tcx ()>, +} + +impl<'tcx, F> BindingsVisitor<'tcx, F> +where + F: FnMut(&Binding<'tcx>), +{ + fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { + for binding in &candidate.extra_data.bindings { + (self.f)(binding) + } + for match_pair in &candidate.match_pairs { + self.visit_match_pair(match_pair); + } + } + + fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) { + for binding in &flat_pat.extra_data.bindings { + (self.f)(binding) + } + for match_pair in &flat_pat.match_pairs { + self.visit_match_pair(match_pair); + } + } + + fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) { + if let TestCase::Or { pats, .. } = &match_pair.test_case { + // All the or-alternatives should bind the same locals, so we only visit the first one. + self.visit_flat_pat(&pats[0]) + } else { + for subpair in &match_pair.subpairs { + self.visit_match_pair(subpair); + } + } + } +} + #[must_use] pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { match ref_mutability { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 601e5d4d3dc..0f9746cb719 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -218,8 +218,8 @@ struct Builder<'a, 'tcx> { lint_level_roots_cache: GrowableBitSet<hir::ItemLocalId>, /// Collects additional coverage information during MIR building. - /// Only present if branch coverage is enabled and this function is eligible. - coverage_branch_info: Option<coverageinfo::BranchInfoBuilder>, + /// Only present if coverage is enabled and this function is eligible. + coverage_info: Option<coverageinfo::CoverageInfoBuilder>, } type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>; @@ -773,7 +773,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unit_temp: None, var_debug_info: vec![], lint_level_roots_cache: GrowableBitSet::new_empty(), - coverage_branch_info: coverageinfo::BranchInfoBuilder::new_if_enabled(tcx, def), + coverage_info: coverageinfo::CoverageInfoBuilder::new_if_enabled(tcx, def), }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -802,7 +802,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.coroutine, None, ); - body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done()); + body.coverage_info_hi = self.coverage_info.map(|b| b.into_done()); body } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 5b6de39bb2e..948301e2ece 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -745,6 +745,91 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume); } + /// Sets up the drops for explict tail calls. + /// + /// Unlike other kinds of early exits, tail calls do not go through the drop tree. + /// Instead, all scheduled drops are immediately added to the CFG. + pub(crate) fn break_for_tail_call( + &mut self, + mut block: BasicBlock, + args: &[Spanned<Operand<'tcx>>], + source_info: SourceInfo, + ) -> BlockAnd<()> { + let arg_drops: Vec<_> = args + .iter() + .rev() + .filter_map(|arg| match &arg.node { + Operand::Copy(_) => bug!("copy op in tail call args"), + Operand::Move(place) => { + let local = + place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); + + Some(DropData { source_info, local, kind: DropKind::Value }) + } + Operand::Constant(_) => None, + }) + .collect(); + + let mut unwind_to = self.diverge_cleanup_target( + self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope, + DUMMY_SP, + ); + let unwind_drops = &mut self.scopes.unwind_drops; + + // the innermost scope contains only the destructors for the tail call arguments + // we only want to drop these in case of a panic, so we skip it + for scope in self.scopes.scopes[1..].iter().rev().skip(1) { + // FIXME(explicit_tail_calls) code duplication with `build_scope_drops` + for drop_data in scope.drops.iter().rev() { + let source_info = drop_data.source_info; + let local = drop_data.local; + + match drop_data.kind { + DropKind::Value => { + // `unwind_to` should drop the value that we're about to + // schedule. If dropping this value panics, then we continue + // with the *next* value on the unwind path. + debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].next; + + let mut unwind_entry_point = unwind_to; + + // the tail call arguments must be dropped if any of these drops panic + for drop in arg_drops.iter().copied() { + unwind_entry_point = unwind_drops.add_drop(drop, unwind_entry_point); + } + + unwind_drops.add_entry_point(block, unwind_entry_point); + + let next = self.cfg.start_new_block(); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }, + ); + block = next; + } + DropKind::Storage => { + // Only temps and vars need their storage dead. + assert!(local.index() > self.arg_count); + self.cfg.push( + block, + Statement { source_info, kind: StatementKind::StorageDead(local) }, + ); + } + } + } + } + + block.unit() + } + fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. @@ -1523,6 +1608,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } | TerminatorKind::CoroutineDrop diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 2c817d605af..263e777d03a 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -141,7 +141,7 @@ impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> { return false; }; let (callee, call_args) = if let Ok(Some(instance)) = - Instance::resolve(tcx, param_env, callee, normalized_args) + Instance::try_resolve(tcx, param_env, callee, normalized_args) { (instance.def_id(), instance.args) } else { @@ -217,12 +217,28 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()), + + // Note that tail call terminator technically returns to the caller, + // but for purposes of this lint it makes sense to count it as possibly recursive, + // since it's still a call. + // + // If this'll be repurposed for something else, this might need to be changed. + TerminatorKind::TailCall { .. } => ControlFlow::Continue(()), } } fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> { // When we examine a node for the last time, remember it if it is a recursive call. let terminator = self.body[bb].terminator(); + + // FIXME(explicit_tail_calls): highlight tail calls as "recursive call site" + // + // We don't want to lint functions that recurse only through tail calls + // (such as `fn g() { become () }`), so just adding `| TailCall { ... }` + // here won't work. + // + // But at the same time we would like to highlight both calls in a function like + // `fn f() { if false { become f() } else { f() } }`, so we need to figure something out. if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) { self.reachable_recursive_calls.push(terminator.source_info.span); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 8c6c9e10cdf..5745dc0969c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -138,7 +138,7 @@ impl<'tcx> ConstToPat<'tcx> { // lints, but no errors), double-check that all types in the const implement // `PartialEq`. Even if we have a valtree, we may have found something // in there with non-structural-equality, meaning we match using `PartialEq` - // and we hence have to check that that impl exists. + // and we hence have to check if that impl exists. // This is all messy but not worth cleaning up: at some point we'll emit // a hard error when we don't have a valtree or when we find something in // the valtree that is not structural; then this can all be made a lot simpler. diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 93db1f61853..fd778ef78a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -558,7 +558,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let args = self .tcx .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_args(id)); - let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, args) { + let instance = match ty::Instance::try_resolve(self.tcx, param_env_reveal_all, def_id, args) + { Ok(Some(i)) => i, Ok(None) => { // It should be assoc consts if there's no error but we cannot resolve it. diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index 1c2b475a43c..23738f7a4a5 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -76,6 +76,8 @@ pub trait MeetSemiLattice: Eq { /// A set that has a "bottom" element, which is less than or equal to any other element. pub trait HasBottom { const BOTTOM: Self; + + fn is_bottom(&self) -> bool; } /// A set that has a "top" element, which is greater than or equal to any other element. @@ -114,6 +116,10 @@ impl MeetSemiLattice for bool { impl HasBottom for bool { const BOTTOM: Self = false; + + fn is_bottom(&self) -> bool { + !self + } } impl HasTop for bool { @@ -267,6 +273,10 @@ impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> { impl<T> HasBottom for FlatSet<T> { const BOTTOM: Self = Self::Bottom; + + fn is_bottom(&self) -> bool { + matches!(self, Self::Bottom) + } } impl<T> HasTop for FlatSet<T> { @@ -291,6 +301,10 @@ impl<T> MaybeReachable<T> { impl<T> HasBottom for MaybeReachable<T> { const BOTTOM: Self = MaybeReachable::Unreachable; + + fn is_bottom(&self) -> bool { + matches!(self, Self::Unreachable) + } } impl<T: HasTop> HasTop for MaybeReachable<T> { diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 706bb796349..574da949b0e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -145,6 +145,7 @@ where | TerminatorKind::InlineAsm { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } => {} diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index f0b79dab0c9..ffcf630b653 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -50,15 +50,19 @@ use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable}; /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedPlaces` yields the set of /// places that would require a dynamic drop-flag at that statement. -pub struct MaybeInitializedPlaces<'a, 'tcx> { +pub struct MaybeInitializedPlaces<'a, 'mir, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &'mir Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>, skip_unreachable_unwind: bool, } -impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { +impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + body: &'mir Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, + ) -> Self { MaybeInitializedPlaces { tcx, body, mdpe, skip_unreachable_unwind: false } } @@ -84,7 +88,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { } } -impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { +impl<'a, 'mir, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'mir, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } @@ -125,17 +129,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedPlaces` yields the set of /// places that would require a dynamic drop-flag at that statement. -pub struct MaybeUninitializedPlaces<'a, 'tcx> { +pub struct MaybeUninitializedPlaces<'a, 'mir, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &'mir Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>, mark_inactive_variants_as_uninit: bool, skip_unreachable_unwind: BitSet<mir::BasicBlock>, } -impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { +impl<'a, 'mir, 'tcx> MaybeUninitializedPlaces<'a, 'mir, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + body: &'mir Body<'tcx>, + mdpe: &'a MoveDataParamEnv<'tcx>, + ) -> Self { MaybeUninitializedPlaces { tcx, body, @@ -164,7 +172,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { } } -impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { +impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, '_, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } @@ -250,24 +258,24 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { /// c = S; // {a, b, c, d } /// } /// ``` -pub struct EverInitializedPlaces<'a, 'tcx> { - body: &'a Body<'tcx>, +pub struct EverInitializedPlaces<'a, 'mir, 'tcx> { + body: &'mir Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>, } -impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { +impl<'a, 'mir, 'tcx> EverInitializedPlaces<'a, 'mir, 'tcx> { + pub fn new(body: &'mir Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { EverInitializedPlaces { body, mdpe } } } -impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> { +impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, '_, 'tcx> { fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data } } -impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { +impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> { fn update_bits( trans: &mut impl GenKill<MovePathIndex>, path: MovePathIndex, @@ -280,7 +288,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { } } -impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { +impl<'a, 'tcx> MaybeUninitializedPlaces<'a, '_, 'tcx> { fn update_bits( trans: &mut impl GenKill<MovePathIndex>, path: MovePathIndex, @@ -306,7 +314,7 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>; @@ -328,7 +336,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> { type Idx = MovePathIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { @@ -441,7 +449,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> { /// There can be many more `MovePathIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet<MovePathIndex>; @@ -465,7 +473,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> { type Idx = MovePathIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { @@ -642,7 +650,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } } -impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { +impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { /// There can be many more `InitIndex` than there are locals in a MIR body. /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet<InitIndex>; @@ -661,7 +669,7 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } -impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { +impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { type Idx = InitIndex; fn domain_size(&self, _: &Body<'tcx>) -> usize { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 29169c31263..f850a710277 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -288,6 +288,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } @@ -325,6 +326,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 1fb77bef3d4..7b39db821d8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -489,6 +489,12 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } + TerminatorKind::TailCall { ref func, ref args, .. } => { + self.gather_operand(func); + for arg in args { + self.gather_operand(&arg.node); + } + } TerminatorKind::InlineAsm { template: _, ref operands, diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 5e2d88f94ca..1582c2e8a90 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -36,10 +36,10 @@ use std::collections::VecDeque; use std::fmt::{Debug, Formatter}; use std::ops::Range; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::bit_set::BitSet; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -269,6 +269,9 @@ pub trait ValueAnalysis<'tcx> { TerminatorKind::SwitchInt { discr, targets } => { return self.handle_switch_int(discr, targets, state); } + TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): determine if we need to do something here (probably not) + } TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) @@ -336,14 +339,13 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper const NAME: &'static str = T::NAME; fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain { - State(StateData::Unreachable) + State::Unreachable } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥. - assert!(matches!(state.0, StateData::Unreachable)); - let values = IndexVec::from_elem_n(T::Value::BOTTOM, self.0.map().value_count); - *state = State(StateData::Reachable(values)); + assert!(matches!(state, State::Unreachable)); + *state = State::new_reachable(); for arg in body.args_iter() { state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map()); } @@ -415,27 +417,54 @@ rustc_index::newtype_index!( /// See [`State`]. #[derive(PartialEq, Eq, Debug)] -enum StateData<V> { - Reachable(IndexVec<ValueIndex, V>), - Unreachable, +pub struct StateData<V> { + bottom: V, + /// This map only contains values that are not `⊥`. + map: FxHashMap<ValueIndex, V>, +} + +impl<V: HasBottom> StateData<V> { + fn new() -> StateData<V> { + StateData { bottom: V::BOTTOM, map: FxHashMap::default() } + } + + fn get(&self, idx: ValueIndex) -> &V { + self.map.get(&idx).unwrap_or(&self.bottom) + } + + fn insert(&mut self, idx: ValueIndex, elem: V) { + if elem.is_bottom() { + self.map.remove(&idx); + } else { + self.map.insert(idx, elem); + } + } } impl<V: Clone> Clone for StateData<V> { fn clone(&self) -> Self { - match self { - Self::Reachable(x) => Self::Reachable(x.clone()), - Self::Unreachable => Self::Unreachable, - } + StateData { bottom: self.bottom.clone(), map: self.map.clone() } } fn clone_from(&mut self, source: &Self) { - match (&mut *self, source) { - (Self::Reachable(x), Self::Reachable(y)) => { - // We go through `raw` here, because `IndexVec` currently has a naive `clone_from`. - x.raw.clone_from(&y.raw); + self.map.clone_from(&source.map) + } +} + +impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for StateData<V> { + fn join(&mut self, other: &Self) -> bool { + let mut changed = false; + #[allow(rustc::potential_query_instability)] + for (i, v) in other.map.iter() { + match self.map.entry(*i) { + StdEntry::Vacant(e) => { + e.insert(v.clone()); + changed = true + } + StdEntry::Occupied(e) => changed |= e.into_mut().join(v), } - _ => *self = source.clone(), } + changed } } @@ -450,33 +479,47 @@ impl<V: Clone> Clone for StateData<V> { /// /// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place. #[derive(PartialEq, Eq, Debug)] -pub struct State<V>(StateData<V>); +pub enum State<V> { + Unreachable, + Reachable(StateData<V>), +} impl<V: Clone> Clone for State<V> { fn clone(&self) -> Self { - Self(self.0.clone()) + match self { + Self::Reachable(x) => Self::Reachable(x.clone()), + Self::Unreachable => Self::Unreachable, + } } fn clone_from(&mut self, source: &Self) { - self.0.clone_from(&source.0); + match (&mut *self, source) { + (Self::Reachable(x), Self::Reachable(y)) => { + x.clone_from(&y); + } + _ => *self = source.clone(), + } } } -impl<V: Clone> State<V> { - pub fn new(init: V, map: &Map) -> State<V> { - let values = IndexVec::from_elem_n(init, map.value_count); - State(StateData::Reachable(values)) +impl<V: Clone + HasBottom> State<V> { + pub fn new_reachable() -> State<V> { + State::Reachable(StateData::new()) } - pub fn all(&self, f: impl Fn(&V) -> bool) -> bool { - match self.0 { - StateData::Unreachable => true, - StateData::Reachable(ref values) => values.iter().all(f), + pub fn all_bottom(&self) -> bool { + match self { + State::Unreachable => false, + State::Reachable(ref values) => + { + #[allow(rustc::potential_query_instability)] + values.map.values().all(V::is_bottom) + } } } fn is_reachable(&self) -> bool { - matches!(&self.0, StateData::Reachable(_)) + matches!(self, State::Reachable(_)) } /// Assign `value` to all places that are contained in `place` or may alias one. @@ -519,10 +562,8 @@ impl<V: Clone> State<V> { map: &Map, value: V, ) { - let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, tail_elem, &mut |vi| { - values[vi] = value.clone(); - }); + let State::Reachable(values) = self else { return }; + map.for_each_aliasing_place(place, tail_elem, &mut |vi| values.insert(vi, value.clone())); } /// Low-level method that assigns to a place. @@ -541,9 +582,9 @@ impl<V: Clone> State<V> { /// /// The target place must have been flooded before calling this method. pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map) { - let StateData::Reachable(values) = &mut self.0 else { return }; + let State::Reachable(values) = self else { return }; if let Some(value_index) = map.places[target].value_index { - values[value_index] = value; + values.insert(value_index, value) } } @@ -555,14 +596,14 @@ impl<V: Clone> State<V> { /// /// The target place must have been flooded before calling this method. pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) { - let StateData::Reachable(values) = &mut self.0 else { return }; + let State::Reachable(values) = self else { return }; // If both places are tracked, we copy the value to the target. // If the target is tracked, but the source is not, we do nothing, as invalidation has // already been performed. if let Some(target_value) = map.places[target].value_index { if let Some(source_value) = map.places[source].value_index { - values[target_value] = values[source_value].clone(); + values.insert(target_value, values.get(source_value).clone()); } } for target_child in map.children(target) { @@ -616,11 +657,11 @@ impl<V: Clone> State<V> { /// Retrieve the value stored for a place index, or `None` if it is not tracked. pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> { - match &self.0 { - StateData::Reachable(values) => { - map.places[place].value_index.map(|v| values[v].clone()) + match self { + State::Reachable(values) => { + map.places[place].value_index.map(|v| values.get(v).clone()) } - StateData::Unreachable => None, + State::Unreachable => None, } } @@ -631,10 +672,10 @@ impl<V: Clone> State<V> { where V: HasBottom + HasTop, { - match &self.0 { - StateData::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP), + match self { + State::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP), // Because this is unreachable, we can return any value we want. - StateData::Unreachable => V::BOTTOM, + State::Unreachable => V::BOTTOM, } } @@ -645,10 +686,10 @@ impl<V: Clone> State<V> { where V: HasBottom + HasTop, { - match &self.0 { - StateData::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP), + match self { + State::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP), // Because this is unreachable, we can return any value we want. - StateData::Unreachable => V::BOTTOM, + State::Unreachable => V::BOTTOM, } } @@ -659,10 +700,10 @@ impl<V: Clone> State<V> { where V: HasBottom + HasTop, { - match &self.0 { - StateData::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP), + match self { + State::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP), // Because this is unreachable, we can return any value we want. - StateData::Unreachable => V::BOTTOM, + State::Unreachable => V::BOTTOM, } } @@ -673,11 +714,11 @@ impl<V: Clone> State<V> { where V: HasBottom + HasTop, { - match &self.0 { - StateData::Reachable(values) => { - map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP) + match self { + State::Reachable(values) => { + map.places[place].value_index.map(|v| values.get(v).clone()).unwrap_or(V::TOP) } - StateData::Unreachable => { + State::Unreachable => { // Because this is unreachable, we can return any value we want. V::BOTTOM } @@ -685,15 +726,15 @@ impl<V: Clone> State<V> { } } -impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> { +impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> { fn join(&mut self, other: &Self) -> bool { - match (&mut self.0, &other.0) { - (_, StateData::Unreachable) => false, - (StateData::Unreachable, _) => { + match (&mut *self, other) { + (_, State::Unreachable) => false, + (State::Unreachable, _) => { *self = other.clone(); true } - (StateData::Reachable(this), StateData::Reachable(other)) => this.join(other), + (State::Reachable(this), State::Reachable(ref other)) => this.join(other), } } } @@ -846,9 +887,10 @@ impl Map { if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind() && let ty::Slice(..) = ref_ty.kind() + // The user may have written a predicate like `[T]: Sized` in their where clauses, + // which makes slices scalars. + && self.places[place].value_index.is_none() { - assert!(self.places[place].value_index.is_none(), "slices are not scalars"); - // Prepend new child to the linked list. let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen))); self.places[len].next_sibling = self.places[place].first_child; @@ -1193,9 +1235,9 @@ where T::Value: Debug, { fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result { - match &self.0 { - StateData::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f), - StateData::Unreachable => write!(f, "unreachable"), + match self { + State::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f), + State::Unreachable => write!(f, "unreachable"), } } @@ -1205,8 +1247,8 @@ where ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>, ) -> std::fmt::Result { - match (&self.0, &old.0) { - (StateData::Reachable(this), StateData::Reachable(old)) => { + match (self, old) { + (State::Reachable(this), State::Reachable(old)) => { debug_with_context(this, Some(old), ctxt.0.map(), f) } _ => Ok(()), // Consider printing something here. @@ -1214,21 +1256,21 @@ where } } -fn debug_with_context_rec<V: Debug + Eq>( +fn debug_with_context_rec<V: Debug + Eq + HasBottom>( place: PlaceIndex, place_str: &str, - new: &IndexSlice<ValueIndex, V>, - old: Option<&IndexSlice<ValueIndex, V>>, + new: &StateData<V>, + old: Option<&StateData<V>>, map: &Map, f: &mut Formatter<'_>, ) -> std::fmt::Result { if let Some(value) = map.places[place].value_index { match old { - None => writeln!(f, "{}: {:?}", place_str, new[value])?, + None => writeln!(f, "{}: {:?}", place_str, new.get(value))?, Some(old) => { - if new[value] != old[value] { - writeln!(f, "\u{001f}-{}: {:?}", place_str, old[value])?; - writeln!(f, "\u{001f}+{}: {:?}", place_str, new[value])?; + if new.get(value) != old.get(value) { + writeln!(f, "\u{001f}-{}: {:?}", place_str, old.get(value))?; + writeln!(f, "\u{001f}+{}: {:?}", place_str, new.get(value))?; } } } @@ -1260,9 +1302,9 @@ fn debug_with_context_rec<V: Debug + Eq>( Ok(()) } -fn debug_with_context<V: Debug + Eq>( - new: &IndexSlice<ValueIndex, V>, - old: Option<&IndexSlice<ValueIndex, V>>, +fn debug_with_context<V: Debug + Eq + HasBottom>( + new: &StateData<V>, + old: Option<&StateData<V>>, map: &Map, f: &mut Formatter<'_>, ) -> std::fmt::Result { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 05674792426..261dcd52d71 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -58,7 +58,7 @@ use crate::deref_separator::deref_finder; use crate::errors; use crate::pass_manager as pm; use crate::simplify; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; @@ -81,8 +81,8 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::PanicStrategy; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; use rustc_trait_selection::infer::TyCtxtInferExt as _; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use std::{iter, ops}; @@ -208,11 +208,8 @@ const UNRESUMED: usize = CoroutineArgs::UNRESUMED; const RETURNED: usize = CoroutineArgs::RETURNED; /// Coroutine has panicked and is poisoned. const POISONED: usize = CoroutineArgs::POISONED; - -/// Number of variants to reserve in coroutine state. Corresponds to -/// `UNRESUMED` (beginning of a coroutine) and `RETURNED`/`POISONED` -/// (end of a coroutine) states. -const RESERVED_VARIANTS: usize = 3; +/// Number of reserved variants of coroutine state. +const RESERVED_VARIANTS: usize = CoroutineArgs::RESERVED_VARIANTS; /// A `yield` point in the coroutine. struct SuspensionPoint<'tcx> { @@ -236,8 +233,7 @@ struct TransformVisitor<'tcx> { discr_ty: Ty<'tcx>, // Mapping from Local to (type of local, coroutine struct index) - // FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`. - remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>, + remap: IndexVec<Local, Option<(Ty<'tcx>, VariantIdx, FieldIdx)>>, // A map from a suspension point in a block to the locals which have live storage at that point storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>, @@ -485,7 +481,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_eq!(self.remap.get(local), None); + assert!(!self.remap.contains(*local)); } fn visit_place( @@ -495,7 +491,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { _location: Location, ) { // Replace an Local in the remap with a coroutine struct access - if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) { + if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); } } @@ -504,7 +500,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { // Remove StorageLive and StorageDead statements for remapped locals data.retain_statements(|s| match s.kind { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - !self.remap.contains_key(&l) + !self.remap.contains(l) } _ => true, }); @@ -529,13 +525,9 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { // The resume arg target location might itself be remapped if its base local is // live across a yield. - let resume_arg = - if let Some(&(ty, variant, idx)) = self.remap.get(&resume_arg.local) { - replace_base(&mut resume_arg, self.make_field(variant, idx, ty), self.tcx); - resume_arg - } else { - resume_arg - }; + if let Some(&Some((ty, variant, idx))) = self.remap.get(resume_arg.local) { + replace_base(&mut resume_arg, self.make_field(variant, idx, ty), self.tcx); + } let storage_liveness: GrowableBitSet<Local> = self.storage_liveness[block].clone().unwrap().into(); @@ -543,7 +535,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { for i in 0..self.always_live_locals.domain_size() { let l = Local::new(i); let needs_storage_dead = storage_liveness.contains(l) - && !self.remap.contains_key(&l) + && !self.remap.contains(l) && !self.always_live_locals.contains(l); if needs_storage_dead { data.statements @@ -1037,7 +1029,7 @@ fn compute_layout<'tcx>( liveness: LivenessInfo, body: &Body<'tcx>, ) -> ( - FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>, + IndexVec<Local, Option<(Ty<'tcx>, VariantIdx, FieldIdx)>>, CoroutineLayout<'tcx>, IndexVec<BasicBlock, Option<BitSet<Local>>>, ) { @@ -1098,7 +1090,7 @@ fn compute_layout<'tcx>( // Create a map from local indices to coroutine struct indices. let mut variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, CoroutineSavedLocal>> = iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect(); - let mut remap = FxHashMap::default(); + let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size()); for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() { let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx); let mut fields = IndexVec::new(); @@ -1109,7 +1101,7 @@ fn compute_layout<'tcx>( // around inside coroutines, so it doesn't matter which variant // index we access them by. let idx = FieldIdx::from_usize(idx); - remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx)); + remap[locals[saved_local]] = Some((tys[saved_local].ty, variant_index, idx)); } variant_fields.push(fields); variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]); @@ -1121,7 +1113,9 @@ fn compute_layout<'tcx>( for var in &body.var_debug_info { let VarDebugInfoContents::Place(place) = &var.value else { continue }; let Some(local) = place.as_local() else { continue }; - let Some(&(_, variant, field)) = remap.get(&local) else { continue }; + let Some(&Some((_, variant, field))) = remap.get(local) else { + continue; + }; let saved_local = variant_fields[variant][field]; field_names.get_or_insert_with(saved_local, || var.name); @@ -1373,6 +1367,10 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { | TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } | TerminatorKind::Assert { .. } => return true, + + TerminatorKind::TailCall { .. } => { + unreachable!("tail calls can't be present in generators") + } } } @@ -1524,7 +1522,7 @@ fn create_cases<'tcx>( for i in 0..(body.local_decls.len()) { let l = Local::new(i); let needs_storage_live = point.storage_liveness.contains(l) - && !transform.remap.contains_key(&l) + && !transform.remap.contains(l) && !transform.always_live_locals.contains(l); if needs_storage_live { statements @@ -1922,6 +1920,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Assert { .. } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 7e401b5482f..a1c1422912e 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -31,6 +31,39 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { CostChecker { tcx, param_env, callee_body, instance, penalty: 0, bonus: 0 } } + /// Add function-level costs not well-represented by the block-level costs. + /// + /// Needed because the `CostChecker` is used sometimes for just blocks, + /// and even the full `Inline` doesn't call `visit_body`, so there's nowhere + /// to put this logic in the visitor. + pub fn add_function_level_costs(&mut self) { + fn is_call_like(bbd: &BasicBlockData<'_>) -> bool { + use TerminatorKind::*; + match bbd.terminator().kind { + Call { .. } | TailCall { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => { + true + } + + Goto { .. } + | SwitchInt { .. } + | UnwindResume + | UnwindTerminate(_) + | Return + | Unreachable => false, + + Yield { .. } | CoroutineDrop | FalseEdge { .. } | FalseUnwind { .. } => { + unreachable!() + } + } + } + + // If the only has one Call (or similar), inlining isn't increasing the total + // number of calls, so give extra encouragement to inlining that. + if self.callee_body.basic_blocks.iter().filter(|bbd| is_call_like(bbd)).count() == 1 { + self.bonus += CALL_PENALTY; + } + } + pub fn cost(&self) -> usize { usize::saturating_sub(self.penalty, self.bonus) } @@ -106,6 +139,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += LANDINGPAD_PENALTY; } } + TerminatorKind::TailCall { .. } => { + self.penalty += CALL_PENALTY; + } TerminatorKind::SwitchInt { discr, targets } => { if discr.constant().is_some() { // Not only will this become a `Goto`, but likely other diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 360dccb240d..83fb9ff9743 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -358,9 +358,12 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera } // These terminators have no coverage-relevant successors. - CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => { - CoverageSuccessors::NotChainable(&[]) - } + CoroutineDrop + | Return + | TailCall { .. } + | Unreachable + | UnwindResume + | UnwindTerminate(_) => CoverageSuccessors::NotChainable(&[]), } } diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 759bb7c1f9d..25297245172 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -3,15 +3,16 @@ use std::collections::BTreeSet; use rustc_data_structures::graph::DirectedGraph; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind}; +use rustc_middle::mir::coverage::{ + BlockMarkerId, BranchSpan, ConditionInfo, CoverageInfoHi, CoverageKind, +}; use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; -use crate::coverage::spans::{ - extract_refined_covspans, unexpand_into_body_span_with_visible_macro, -}; +use crate::coverage::spans::extract_refined_covspans; +use crate::coverage::unexpand::unexpand_into_body_span; use crate::coverage::ExtractedHirInfo; /// Associates an ordinary executable code span with its corresponding BCB. @@ -158,12 +159,12 @@ impl ExtractedMappings { } fn resolve_block_markers( - branch_info: &mir::coverage::BranchInfo, + coverage_info_hi: &CoverageInfoHi, mir_body: &mir::Body<'_>, ) -> IndexVec<BlockMarkerId, Option<BasicBlock>> { let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n( None, - branch_info.num_block_markers, + coverage_info_hi.num_block_markers, ); // Fill out the mapping from block marker IDs to their enclosing blocks. @@ -189,11 +190,11 @@ pub(super) fn extract_branch_pairs( hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, ) -> Vec<BranchPair> { - let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; + let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] }; - let block_markers = resolve_block_markers(branch_info, mir_body); + let block_markers = resolve_block_markers(coverage_info_hi, mir_body); - branch_info + coverage_info_hi .branch_spans .iter() .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { @@ -202,8 +203,7 @@ pub(super) fn extract_branch_pairs( if !raw_span.ctxt().outer_expn_data().is_root() { return None; } - let (span, _) = - unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; + let span = unexpand_into_body_span(raw_span, hir_info.body_span)?; let bcb_from_marker = |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); @@ -224,9 +224,9 @@ pub(super) fn extract_mcdc_mappings( mcdc_branches: &mut impl Extend<MCDCBranch>, mcdc_decisions: &mut impl Extend<MCDCDecision>, ) { - let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return }; + let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return }; - let block_markers = resolve_block_markers(branch_info, mir_body); + let block_markers = resolve_block_markers(coverage_info_hi, mir_body); let bcb_from_marker = |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); @@ -238,14 +238,14 @@ pub(super) fn extract_mcdc_mappings( if !raw_span.ctxt().outer_expn_data().is_root() { return None; } - let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + let span = unexpand_into_body_span(raw_span, body_span)?; let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; Some((span, true_bcb, false_bcb)) }; - mcdc_branches.extend(branch_info.mcdc_branch_spans.iter().filter_map( + mcdc_branches.extend(coverage_info_hi.mcdc_branch_spans.iter().filter_map( |&mir::coverage::MCDCBranchSpan { span: raw_span, condition_info, @@ -259,9 +259,9 @@ pub(super) fn extract_mcdc_mappings( }, )); - mcdc_decisions.extend(branch_info.mcdc_decision_spans.iter().filter_map( + mcdc_decisions.extend(coverage_info_hi.mcdc_decision_spans.iter().filter_map( |decision: &mir::coverage::MCDCDecisionSpan| { - let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?; + let span = unexpand_into_body_span(decision.span, body_span)?; let end_bcbs = decision .end_markers diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 4a64d21f3d1..2efca40d180 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -6,7 +6,12 @@ mod mappings; mod spans; #[cfg(test)] mod tests; +mod unexpand; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::mir::coverage::{ CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, }; @@ -464,6 +469,9 @@ struct ExtractedHirInfo { /// Must have the same context and filename as the body span. fn_sig_span_extended: Option<Span>, body_span: Span, + /// "Holes" are regions within the body span that should not be included in + /// coverage spans for this function (e.g. closures and nested items). + hole_spans: Vec<Span>, } fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo { @@ -479,7 +487,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir let mut body_span = hir_body.value.span; - use rustc_hir::{Closure, Expr, ExprKind, Node}; + use hir::{Closure, Expr, ExprKind, Node}; // Unexpand a closure's body span back to the context of its declaration. // This helps with closure bodies that consist of just a single bang-macro, // and also with closure bodies produced by async desugaring. @@ -506,11 +514,78 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir let function_source_hash = hash_mir_source(tcx, hir_body); - ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span_extended, body_span } + let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body); + + ExtractedHirInfo { + function_source_hash, + is_async_fn, + fn_sig_span_extended, + body_span, + hole_spans, + } } -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { // FIXME(cjgillot) Stop hashing HIR manually here. let owner = hir_body.id().hir_id.owner; tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() } + +fn extract_hole_spans_from_hir<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, // Usually `hir_body.value.span`, but not always + hir_body: &hir::Body<'tcx>, +) -> Vec<Span> { + struct HolesVisitor<'hir, F> { + hir: Map<'hir>, + visit_hole_span: F, + } + + impl<'hir, F: FnMut(Span)> Visitor<'hir> for HolesVisitor<'hir, F> { + /// - We need `NestedFilter::INTRA = true` so that `visit_item` will be called. + /// - Bodies of nested items don't actually get visited, because of the + /// `visit_item` override. + /// - For nested bodies that are not part of an item, we do want to visit any + /// items contained within them. + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.hir + } + + fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { + (self.visit_hole_span)(item.span); + // Having visited this item, we don't care about its children, + // so don't call `walk_item`. + } + + // We override `visit_expr` instead of the more specific expression + // visitors, so that we have direct access to the expression span. + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + match expr.kind { + hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => { + (self.visit_hole_span)(expr.span); + // Having visited this expression, we don't care about its + // children, so don't call `walk_expr`. + } + + // For other expressions, recursively visit as normal. + _ => walk_expr(self, expr), + } + } + } + + let mut hole_spans = vec![]; + let mut visitor = HolesVisitor { + hir: tcx.hir(), + visit_hole_span: |hole_span| { + // Discard any holes that aren't directly visible within the body span. + if body_span.contains(hole_span) && body_span.eq_ctxt(hole_span) { + hole_spans.push(hole_span); + } + }, + }; + + visitor.visit_body(hir_body); + hole_spans +} diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 84a70d1f02d..dbc26a2808e 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -8,25 +8,20 @@ use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::mappings; use crate::coverage::spans::from_mir::{ - extract_covspans_and_holes_from_mir, ExtractedCovspans, Hole, SpanFromMir, + extract_covspans_from_mir, ExtractedCovspans, Hole, SpanFromMir, }; use crate::coverage::ExtractedHirInfo; mod from_mir; -// FIXME(#124545) It's awkward that we have to re-export this, because it's an -// internal detail of `from_mir` that is also needed when handling branch and -// MC/DC spans. Ideally we would find a more natural home for it. -pub(super) use from_mir::unexpand_into_body_span_with_visible_macro; - pub(super) fn extract_refined_covspans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, code_mappings: &mut impl Extend<mappings::CodeMapping>, ) { - let ExtractedCovspans { mut covspans, mut holes } = - extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks); + let ExtractedCovspans { mut covspans } = + extract_covspans_from_mir(mir_body, hir_info, basic_coverage_blocks); // First, perform the passes that need macro information. covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); @@ -50,6 +45,7 @@ pub(super) fn extract_refined_covspans( covspans.dedup_by(|b, a| a.span.source_equal(b.span)); // Sort the holes, and merge overlapping/adjacent holes. + let mut holes = hir_info.hole_spans.iter().map(|&span| Hole { span }).collect::<Vec<_>>(); holes.sort_by(|a, b| compare_spans(a.span, b.span)); holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 09deb7534bf..32bd25bf4b9 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,26 +1,25 @@ use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ - self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, + self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; +use rustc_span::{Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; use crate::coverage::spans::Covspan; +use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro; use crate::coverage::ExtractedHirInfo; pub(crate) struct ExtractedCovspans { pub(crate) covspans: Vec<SpanFromMir>, - pub(crate) holes: Vec<Hole>, } /// Traverses the MIR body to produce an initial collection of coverage-relevant /// spans, each associated with a node in the coverage graph (BCB) and possibly /// other metadata. -pub(crate) fn extract_covspans_and_holes_from_mir( +pub(crate) fn extract_covspans_from_mir( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, @@ -28,21 +27,13 @@ pub(crate) fn extract_covspans_and_holes_from_mir( let &ExtractedHirInfo { body_span, .. } = hir_info; let mut covspans = vec![]; - let mut holes = vec![]; for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { - bcb_to_initial_coverage_spans( - mir_body, - body_span, - bcb, - bcb_data, - &mut covspans, - &mut holes, - ); + bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans); } // Only add the signature span if we found at least one span in the body. - if !covspans.is_empty() || !holes.is_empty() { + if !covspans.is_empty() { // If there is no usable signature span, add a fake one (before refinement) // to avoid an ugly gap between the body start and the first real span. // FIXME: Find a more principled way to solve this problem. @@ -50,7 +41,7 @@ pub(crate) fn extract_covspans_and_holes_from_mir( covspans.push(SpanFromMir::for_fn_sig(fn_sig_span)); } - ExtractedCovspans { covspans, holes } + ExtractedCovspans { covspans } } // Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of @@ -64,7 +55,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData, initial_covspans: &mut Vec<SpanFromMir>, - holes: &mut Vec<Hole>, ) { for &bb in &bcb_data.basic_blocks { let data = &mir_body[bb]; @@ -80,13 +70,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let expn_span = filtered_statement_span(statement)?; let (span, visible_macro) = unexpand(expn_span)?; - // A statement that looks like the assignment of a closure expression - // is treated as a "hole" span, to be carved out of other spans. - if is_closure_like(statement) { - holes.push(Hole { span }); - } else { - initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); - } + initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); Some(()) }; for statement in data.statements.iter() { @@ -104,18 +88,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( } } -fn is_closure_like(statement: &Statement<'_>) -> bool { - match statement.kind { - StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind { - AggregateKind::Closure(_, _) - | AggregateKind::Coroutine(_, _) - | AggregateKind::CoroutineClosure(..) => true, - _ => false, - }, - _ => false, - } -} - /// If the MIR `Statement` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { @@ -192,7 +164,8 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { | TerminatorKind::Goto { .. } => None, // Call `func` operand can have a more specific span when part of a chain of calls - | TerminatorKind::Call { ref func, .. } => { + TerminatorKind::Call { ref func, .. } + | TerminatorKind::TailCall { ref func, .. } => { let mut span = terminator.source_info.span; if let mir::Operand::Constant(box constant) = func { if constant.span.lo() > span.lo() { @@ -215,59 +188,6 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { } } -/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range -/// within the function's body source. This span is guaranteed to be contained -/// within, or equal to, the `body_span`. If the extrapolated span is not -/// contained within the `body_span`, `None` is returned. -/// -/// [^1]Expansions result from Rust syntax including macros, syntactic sugar, -/// etc.). -pub(crate) fn unexpand_into_body_span_with_visible_macro( - original_span: Span, - body_span: Span, -) -> Option<(Span, Option<Symbol>)> { - let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?; - - let visible_macro = prev - .map(|prev| match prev.ctxt().outer_expn_data().kind { - ExpnKind::Macro(MacroKind::Bang, name) => Some(name), - _ => None, - }) - .flatten(); - - Some((span, visible_macro)) -} - -/// Walks through the expansion ancestors of `original_span` to find a span that -/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`. -/// The ancestor that was traversed just before the matching span (if any) is -/// also returned. -/// -/// For example, a return value of `Some((ancestor, Some(prev))` means that: -/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)` -/// - `ancestor == prev.parent_callsite()` -/// -/// [`SyntaxContext`]: rustc_span::SyntaxContext -fn unexpand_into_body_span_with_prev( - original_span: Span, - body_span: Span, -) -> Option<(Span, Option<Span>)> { - let mut prev = None; - let mut curr = original_span; - - while !body_span.contains(curr) || !curr.eq_ctxt(body_span) { - prev = Some(curr); - curr = curr.parent_callsite()?; - } - - debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span)); - if let Some(prev) = prev { - debug_assert_eq!(Some(curr), prev.parent_callsite()); - } - - Some((curr, prev)) -} - #[derive(Debug)] pub(crate) struct Hole { pub(crate) span: Span, diff --git a/compiler/rustc_mir_transform/src/coverage/unexpand.rs b/compiler/rustc_mir_transform/src/coverage/unexpand.rs new file mode 100644 index 00000000000..8cde291b907 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/unexpand.rs @@ -0,0 +1,60 @@ +use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; + +/// Walks through the expansion ancestors of `original_span` to find a span that +/// is contained in `body_span` and has the same [syntax context] as `body_span`. +pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> Option<Span> { + // Because we don't need to return any extra ancestor information, + // we can just delegate directly to `find_ancestor_inside_same_ctxt`. + original_span.find_ancestor_inside_same_ctxt(body_span) +} + +/// Walks through the expansion ancestors of `original_span` to find a span that +/// is contained in `body_span` and has the same [syntax context] as `body_span`. +/// +/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`), +/// the returned symbol will be the name of that macro (e.g. `foo`). +pub(crate) fn unexpand_into_body_span_with_visible_macro( + original_span: Span, + body_span: Span, +) -> Option<(Span, Option<Symbol>)> { + let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?; + + let visible_macro = prev + .map(|prev| match prev.ctxt().outer_expn_data().kind { + ExpnKind::Macro(MacroKind::Bang, name) => Some(name), + _ => None, + }) + .flatten(); + + Some((span, visible_macro)) +} + +/// Walks through the expansion ancestors of `original_span` to find a span that +/// is contained in `body_span` and has the same [syntax context] as `body_span`. +/// The ancestor that was traversed just before the matching span (if any) is +/// also returned. +/// +/// For example, a return value of `Some((ancestor, Some(prev)))` means that: +/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)` +/// - `prev.parent_callsite() == ancestor` +/// +/// [syntax context]: rustc_span::SyntaxContext +fn unexpand_into_body_span_with_prev( + original_span: Span, + body_span: Span, +) -> Option<(Span, Option<Span>)> { + let mut prev = None; + let mut curr = original_span; + + while !body_span.contains(curr) || !curr.eq_ctxt(body_span) { + prev = Some(curr); + curr = curr.parent_callsite()?; + } + + debug_assert_eq!(Some(curr), original_span.find_ancestor_inside_same_ctxt(body_span)); + if let Some(prev) = prev { + debug_assert_eq!(Some(curr), prev.parent_callsite()); + } + + Some((curr, prev)) +} diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 0fd85eb345d..8b965f4d18e 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -181,11 +181,6 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { state.insert_value_idx(value_target, val, self.map()); } if let Some(overflow_target) = overflow_target { - let overflow = match overflow { - FlatSet::Top => FlatSet::Top, - FlatSet::Elem(overflow) => FlatSet::Elem(overflow), - FlatSet::Bottom => FlatSet::Bottom, - }; // We have flooded `target` earlier. state.insert_value_idx(overflow_target, overflow, self.map()); } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b1016c0867c..ab73a8af317 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -628,6 +628,12 @@ impl WriteInfo { self.add_operand(&arg.node); } } + TerminatorKind::TailCall { func, args, .. } => { + self.add_operand(func); + for arg in args { + self.add_operand(&arg.node); + } + } TerminatorKind::InlineAsm { operands, .. } => { for asm_operand in operands { match asm_operand { diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 665b2260294..25bebb0539a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -33,16 +33,16 @@ use std::fmt; /// as it would allow running a destructor on a place behind a reference: /// /// ```text -// fn drop_term<T>(t: &mut T) { -// mir! { -// { -// Drop(*t, exit) -// } -// exit = { -// Return() -// } -// } -// } +/// fn drop_term<T>(t: &mut T) { +/// mir! { +/// { +/// Drop(*t, exit) +/// } +/// exit = { +/// Return() +/// } +/// } +/// } /// ``` pub struct ElaborateDrops; @@ -97,7 +97,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { #[instrument(level = "trace", skip(body, flow_inits), ret)] fn compute_dead_unwinds<'mir, 'tcx>( body: &'mir Body<'tcx>, - flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, + flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>, ) -> BitSet<BasicBlock> { // We only need to do this pass once, because unwind edges can only // reach cleanup blocks, which can't have unwind edges themselves. @@ -118,12 +118,12 @@ fn compute_dead_unwinds<'mir, 'tcx>( dead_unwinds } -struct InitializationData<'mir, 'tcx> { - inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, - uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, +struct InitializationData<'a, 'mir, 'tcx> { + inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'a, 'mir, 'tcx>>, + uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>, } -impl InitializationData<'_, '_> { +impl InitializationData<'_, '_, '_> { fn seek_before(&mut self, loc: Location) { self.inits.seek_before_primary_effect(loc); self.uninits.seek_before_primary_effect(loc); @@ -134,17 +134,17 @@ impl InitializationData<'_, '_> { } } -struct Elaborator<'a, 'b, 'tcx> { - ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>, +struct Elaborator<'a, 'b, 'mir, 'tcx> { + ctxt: &'a mut ElaborateDropsCtxt<'b, 'mir, 'tcx>, } -impl fmt::Debug for Elaborator<'_, '_, '_> { +impl fmt::Debug for Elaborator<'_, '_, '_, '_> { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { Ok(()) } } -impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> { +impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, '_, 'tcx> { type Path = MovePathIndex; fn patch(&mut self) -> &mut MirPatch<'tcx> { @@ -238,16 +238,16 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> { } } -struct ElaborateDropsCtxt<'a, 'tcx> { +struct ElaborateDropsCtxt<'a, 'mir, 'tcx> { tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &'mir Body<'tcx>, env: &'a MoveDataParamEnv<'tcx>, - init_data: InitializationData<'a, 'tcx>, + init_data: InitializationData<'a, 'mir, 'tcx>, drop_flags: IndexVec<MovePathIndex, Option<Local>>, patch: MirPatch<'tcx>, } -impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { +impl<'b, 'mir, 'tcx> ElaborateDropsCtxt<'b, 'mir, 'tcx> { fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 936a7e2d9de..1002746e553 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -8,7 +8,7 @@ //! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values. //! //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available -//! values, the locals in which they are stored, and a the assignment location. +//! values, the locals in which they are stored, and the assignment location. //! //! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we @@ -1074,11 +1074,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { lhs = *lhs_value; rhs = *rhs_value; - if let Some(op) = self.try_as_operand(lhs, location) { - *lhs_operand = op; - } - if let Some(op) = self.try_as_operand(rhs, location) { - *rhs_operand = op; + if let Some(lhs_op) = self.try_as_operand(lhs, location) + && let Some(rhs_op) = self.try_as_operand(rhs, location) + { + *lhs_operand = lhs_op; + *rhs_operand = rhs_op; } } @@ -1391,11 +1391,15 @@ fn op_to_prop_const<'tcx>( let (prov, offset) = pointer.into_parts(); let alloc_id = prov.alloc_id(); intern_const_alloc_for_constprop(ecx, alloc_id).ok()?; - if matches!(ecx.tcx.global_alloc(alloc_id), GlobalAlloc::Memory(_)) { - // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything - // by `GlobalAlloc::Memory`, so do fall through to copying if needed. - // FIXME: find a way to treat this more uniformly - // (probably by fixing codegen) + + // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything + // by `GlobalAlloc::Memory`, so do fall through to copying if needed. + // FIXME: find a way to treat this more uniformly (probably by fixing codegen) + if let GlobalAlloc::Memory(alloc) = ecx.tcx.global_alloc(alloc_id) + // Transmuting a constant is just an offset in the allocation. If the alignment of the + // allocation is not enough, fallback to copying into a properly aligned value. + && alloc.inner().align >= op.layout.align.abi + { return Some(ConstValue::Indirect { alloc_id, offset }); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0a5fc697d03..fd9f0fec88d 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs} use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt, TypeFlags}; use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -41,6 +41,12 @@ struct CallSite<'tcx> { impl<'tcx> MirPass<'tcx> for Inline { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined + // MIR correctly when Modified Condition/Decision Coverage is enabled. + if sess.instrument_coverage_mcdc() { + return false; + } + if let Some(enabled) = sess.opts.unstable_opts.inline_mir { return enabled; } @@ -85,13 +91,18 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { } let param_env = tcx.param_env_reveal_all_normalized(def_id); + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); let mut this = Inliner { tcx, param_env, - codegen_fn_attrs: tcx.codegen_fn_attrs(def_id), + codegen_fn_attrs, history: Vec::new(), changed: false, + caller_is_inline_forwarder: matches!( + codegen_fn_attrs.inline, + InlineAttr::Hint | InlineAttr::Always + ) && body_is_forwarder(body), }; let blocks = START_BLOCK..body.basic_blocks.next_index(); this.process_blocks(body, blocks); @@ -111,6 +122,9 @@ struct Inliner<'tcx> { history: Vec<DefId>, /// Indicates that the caller body has been modified. changed: bool, + /// Indicates that the caller is #[inline] and just calls another function, + /// and thus we can inline less into it as it'll be inlined itself. + caller_is_inline_forwarder: bool, } impl<'tcx> Inliner<'tcx> { @@ -306,6 +320,16 @@ impl<'tcx> Inliner<'tcx> { InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { return Err("instance without MIR (intrinsic / virtual)"); } + + // FIXME(#127030): `ConstParamHasTy` has bad interactions with + // the drop shim builder, which does not evaluate predicates in + // the correct param-env for types being dropped. Stall resolving + // the MIR for this instance until all of its const params are + // substituted. + InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { + return Err("still needs substitution"); + } + // This cannot result in an immediate cycle since the callee MIR is a shim, which does // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we // do not need to catch this here, we can wait until the inliner decides to continue @@ -365,13 +389,15 @@ impl<'tcx> Inliner<'tcx> { ) -> Option<CallSite<'tcx>> { // Only consider direct calls to functions let terminator = bb_data.terminator(); + + // FIXME(explicit_tail_calls): figure out if we can inline tail calls if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { let func_ty = func.ty(caller_body, self.tcx); if let ty::FnDef(def_id, args) = *func_ty.kind() { // To resolve an instance its args have to be fully normalized. let args = self.tcx.try_normalize_erasing_regions(self.param_env, args).ok()?; let callee = - Instance::resolve(self.tcx, self.param_env, def_id, args).ok().flatten()?; + Instance::try_resolve(self.tcx, self.param_env, def_id, args).ok().flatten()?; if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { return None; @@ -475,7 +501,9 @@ impl<'tcx> Inliner<'tcx> { ) -> Result<(), &'static str> { let tcx = self.tcx; - let mut threshold = if cross_crate_inlinable { + let mut threshold = if self.caller_is_inline_forwarder { + self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) + } else if cross_crate_inlinable { self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) } else { self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) @@ -494,6 +522,8 @@ impl<'tcx> Inliner<'tcx> { let mut checker = CostChecker::new(self.tcx, self.param_env, Some(callsite.callee), callee_body); + checker.add_function_level_costs(); + // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; let mut visited = BitSet::new_empty(callee_body.basic_blocks.len()); @@ -528,6 +558,9 @@ impl<'tcx> Inliner<'tcx> { // inline-asm is detected. LLVM will still possibly do an inline later on // if the no-attribute function ends up with the same instruction set anyway. return Err("Cannot move inline-asm across instruction sets"); + } else if let TerminatorKind::TailCall { .. } = term.kind { + // FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should) + return Err("can't inline functions with tail calls"); } else { work_list.extend(term.successors()) } @@ -1016,6 +1049,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { *target = self.map_block(*target); *unwind = self.map_unwind(*unwind); } + TerminatorKind::TailCall { .. } => { + // check_mir_body forbids tail calls + unreachable!() + } TerminatorKind::Call { ref mut target, ref mut unwind, .. } => { if let Some(ref mut tgt) = *target { *tgt = self.map_block(*tgt); @@ -1081,3 +1118,37 @@ fn try_instance_mir<'tcx>( } Ok(tcx.instance_mir(instance)) } + +fn body_is_forwarder(body: &Body<'_>) -> bool { + let TerminatorKind::Call { target, .. } = body.basic_blocks[START_BLOCK].terminator().kind + else { + return false; + }; + if let Some(target) = target { + let TerminatorKind::Return = body.basic_blocks[target].terminator().kind else { + return false; + }; + } + + let max_blocks = if !body.is_polymorphic { + 2 + } else if target.is_none() { + 3 + } else { + 4 + }; + if body.basic_blocks.len() > max_blocks { + return false; + } + + body.basic_blocks.iter_enumerated().all(|(bb, bb_data)| { + bb == START_BLOCK + || matches!( + bb_data.terminator().kind, + TerminatorKind::Return + | TerminatorKind::Drop { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + ) + }) +} diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 35bcd24ce95..d4477563e3a 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -53,7 +53,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( trace!(?caller, ?param_env, ?args, "cannot normalize, skipping"); continue; }; - let Ok(Some(callee)) = ty::Instance::resolve(tcx, param_env, callee, args) else { + let Ok(Some(callee)) = ty::Instance::try_resolve(tcx, param_env, callee, args) else { trace!(?callee, "cannot resolve, skipping"); continue; }; diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 23cc0c46e73..97ec0cb39de 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -47,6 +47,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ScalarInt, TyCtxt}; +use rustc_mir_dataflow::lattice::HasBottom; use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; @@ -91,43 +92,8 @@ impl<'tcx> MirPass<'tcx> for JumpThreading { opportunities: Vec::new(), }; - for (bb, bbdata) in body.basic_blocks.iter_enumerated() { - debug!(?bb, term = ?bbdata.terminator()); - if bbdata.is_cleanup || loop_headers.contains(bb) { - continue; - } - let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue }; - let Some(discr) = discr.place() else { continue }; - debug!(?discr, ?bb); - - let discr_ty = discr.ty(body, tcx).ty; - let Ok(discr_layout) = finder.ecx.layout_of(discr_ty) else { continue }; - - let Some(discr) = finder.map.find(discr.as_ref()) else { continue }; - debug!(?discr); - - let cost = CostChecker::new(tcx, param_env, None, body); - - let mut state = State::new(ConditionSet::default(), finder.map); - - let conds = if let Some((value, then, else_)) = targets.as_static_if() { - let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { - continue; - }; - arena.alloc_from_iter([ - Condition { value, polarity: Polarity::Eq, target: then }, - Condition { value, polarity: Polarity::Ne, target: else_ }, - ]) - } else { - arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| { - let value = ScalarInt::try_from_uint(value, discr_layout.size)?; - Some(Condition { value, polarity: Polarity::Eq, target }) - })) - }; - let conds = ConditionSet(conds); - state.insert_value_idx(discr, conds, finder.map); - - finder.find_opportunity(bb, state, cost, 0); + for bb in body.basic_blocks.indices() { + finder.start_from_switch(bb); } let opportunities = finder.opportunities; @@ -193,9 +159,17 @@ impl Condition { } } -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug)] struct ConditionSet<'a>(&'a [Condition]); +impl HasBottom for ConditionSet<'_> { + const BOTTOM: Self = ConditionSet(&[]); + + fn is_bottom(&self) -> bool { + self.0.is_empty() + } +} + impl<'a> ConditionSet<'a> { fn iter(self) -> impl Iterator<Item = Condition> + 'a { self.0.iter().copied() @@ -212,10 +186,50 @@ impl<'a> ConditionSet<'a> { impl<'tcx, 'a> TOFinder<'tcx, 'a> { fn is_empty(&self, state: &State<ConditionSet<'a>>) -> bool { - state.all(|cs| cs.0.is_empty()) + state.all_bottom() } /// Recursion entry point to find threading opportunities. + #[instrument(level = "trace", skip(self))] + fn start_from_switch(&mut self, bb: BasicBlock) -> Option<!> { + let bbdata = &self.body[bb]; + if bbdata.is_cleanup || self.loop_headers.contains(bb) { + return None; + } + let (discr, targets) = bbdata.terminator().kind.as_switch()?; + let discr = discr.place()?; + debug!(?discr, ?bb); + + let discr_ty = discr.ty(self.body, self.tcx).ty; + let discr_layout = self.ecx.layout_of(discr_ty).ok()?; + + let discr = self.map.find(discr.as_ref())?; + debug!(?discr); + + let cost = CostChecker::new(self.tcx, self.param_env, None, self.body); + let mut state = State::new_reachable(); + + let conds = if let Some((value, then, else_)) = targets.as_static_if() { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + self.arena.alloc_from_iter([ + Condition { value, polarity: Polarity::Eq, target: then }, + Condition { value, polarity: Polarity::Ne, target: else_ }, + ]) + } else { + self.arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + Some(Condition { value, polarity: Polarity::Eq, target }) + })) + }; + let conds = ConditionSet(conds); + state.insert_value_idx(discr, conds, self.map); + + self.find_opportunity(bb, state, cost, 0); + None + } + + /// Recursively walk statements backwards from this bb's terminator to find threading + /// opportunities. #[instrument(level = "trace", skip(self, cost), ret)] fn find_opportunity( &mut self, @@ -250,7 +264,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`. // _1 = 6 if let Some((lhs, tail)) = self.mutated_statement(stmt) { - state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::default()); + state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::BOTTOM); } } @@ -270,12 +284,13 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { self.process_switch_int(discr, targets, bb, &mut state); self.find_opportunity(pred, state, cost, depth + 1); } - _ => self.recurse_through_terminator(pred, &state, &cost, depth), + _ => self.recurse_through_terminator(pred, || state, &cost, depth), } - } else { + } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] { for &pred in predecessors { - self.recurse_through_terminator(pred, &state, &cost, depth); + self.recurse_through_terminator(pred, || state.clone(), &cost, depth); } + self.recurse_through_terminator(last_pred, || state, &cost, depth); } let new_tos = &mut self.opportunities[last_non_rec..]; @@ -566,11 +581,12 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { None } - #[instrument(level = "trace", skip(self, cost))] + #[instrument(level = "trace", skip(self, state, cost))] fn recurse_through_terminator( &mut self, bb: BasicBlock, - state: &State<ConditionSet<'a>>, + // Pass a closure that may clone the state, as we don't want to do it each time. + state: impl FnOnce() -> State<ConditionSet<'a>>, cost: &CostChecker<'_, 'tcx>, depth: usize, ) { @@ -580,6 +596,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"), // Disallowed during optimizations. @@ -600,9 +617,9 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { }; // We can recurse through this terminator. - let mut state = state.clone(); + let mut state = state(); if let Some(place_to_flood) = place_to_flood { - state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::default()); + state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::BOTTOM); } self.find_opportunity(bb, state, cost.clone(), depth + 1); } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8d6c00bbedb..82ad8879d17 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -799,6 +799,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Yield { .. } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f7056702cb4..5d253d7384d 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -519,7 +519,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &add_subtyping_projections::Subtyper, // calling this after reveal_all ensures that we don't deal with opaque types &elaborate_drops::ElaborateDrops, // This will remove extraneous landing pads which are no longer - // necessary as well as well as forcing any call in a non-unwinding + // necessary as well as forcing any call in a non-unwinding // function calling a possibly-unwinding function to abort the process. &abort_unwinding_calls::AbortUnwindingCalls, // AddMovesForPackedDrops needs to run after drop diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index db2bb60bdac..d928d7cf764 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -38,7 +38,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { self.super_terminator(terminator, location); let span = || self.body.source_info(location).span; match &terminator.kind { - mir::TerminatorKind::Call { func, .. } => { + mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => { let callee_ty = func.ty(self.body, self.tcx); self.mentioned_items .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 3f4d2b65ff2..736647fb64b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -816,7 +816,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { mut func, mut args, call_source: desugar, fn_span, .. } => { // This promoted involves a function call, so it may fail to evaluate. - // Let's make sure it is added to `required_consts` so that that failure cannot get lost. + // Let's make sure it is added to `required_consts` so that failure cannot get lost. self.add_to_required = true; self.visit_operand(&mut func, loc); diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index fb52bfa468a..1df5737e859 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -75,6 +75,7 @@ impl RemoveNoopLandingPads { | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Unreachable | TerminatorKind::Call { .. } + | TerminatorKind::TailCall { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::InlineAsm { .. } => false, diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 25577e88e28..6835a39cf36 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1,18 +1,17 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; - -use rustc_index::{Idx, IndexVec}; - use rustc_span::{source_map::Spanned, Span, DUMMY_SP}; +use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_target::spec::abi::Abi; +use std::assert_matches::assert_matches; use std::fmt; use std::iter; @@ -1020,21 +1019,19 @@ fn build_construct_coroutine_by_move_shim<'tcx>( receiver_by_ref: bool, ) -> Body<'tcx> { let mut self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity(); + let mut self_local: Place<'tcx> = Local::from_usize(1).into(); let ty::CoroutineClosure(_, args) = *self_ty.kind() else { bug!(); }; - // We use `&mut Self` here because we only need to emit an ABI-compatible shim body, - // rather than match the signature exactly (which might take `&self` instead). + // We use `&Self` here because we only need to emit an ABI-compatible shim body, + // rather than match the signature exactly (which might take `&mut self` instead). // - // The self type here is a coroutine-closure, not a coroutine, and we never read from - // it because it never has any captures, because this is only true in the Fn/FnMut - // implementation, not the AsyncFn/AsyncFnMut implementation, which is implemented only - // if the coroutine-closure has no captures. + // We adjust the `self_local` to be a deref since we want to copy fields out of + // a reference to the closure. if receiver_by_ref { - // Triple-check that there's no captures here. - assert_eq!(args.as_coroutine_closure().tupled_upvars_ty(), tcx.types.unit); - self_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, self_ty); + self_local = tcx.mk_place_deref(self_local); + self_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, self_ty); } let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { @@ -1067,11 +1064,27 @@ fn build_construct_coroutine_by_move_shim<'tcx>( fields.push(Operand::Move(Local::from_usize(idx + 1).into())); } for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() { - fields.push(Operand::Move(tcx.mk_place_field( - Local::from_usize(1).into(), - FieldIdx::from_usize(idx), - ty, - ))); + if receiver_by_ref { + // The only situation where it's possible is when we capture immuatable references, + // since those don't need to be reborrowed with the closure's env lifetime. Since + // references are always `Copy`, just emit a copy. + assert_matches!( + ty.kind(), + ty::Ref(_, _, hir::Mutability::Not), + "field should be captured by immutable ref if we have an `Fn` instance" + ); + fields.push(Operand::Copy(tcx.mk_place_field( + self_local, + FieldIdx::from_usize(idx), + ty, + ))); + } else { + fields.push(Operand::Move(tcx.mk_place_field( + self_local, + FieldIdx::from_usize(idx), + ty, + ))); + } } let source_info = SourceInfo::outermost(span); diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 2018a8fe667..ab5c25c4937 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -400,40 +400,44 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::Call { args, destination, target, unwind, .. } => { - if let Some(target) = target { - self.check_edge(location, *target, EdgeKind::Normal); - } - self.check_unwind_edge(location, *unwind); + TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => { + // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks + if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind { + if let Some(target) = target { + self.check_edge(location, target, EdgeKind::Normal); + } + self.check_unwind_edge(location, unwind); + + // The code generation assumes that there are no critical call edges. The assumption + // is used to simplify inserting code that should be executed along the return edge + // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, + // the code generation should be responsible for handling it. + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && self.is_critical_call_edge(target, unwind) + { + self.fail( + location, + format!( + "encountered critical edge in `Call` terminator {:?}", + terminator.kind, + ), + ); + } - // The code generation assumes that there are no critical call edges. The assumption - // is used to simplify inserting code that should be executed along the return edge - // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, - // the code generation should be responsible for handling it. - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) - && self.is_critical_call_edge(*target, *unwind) - { - self.fail( - location, - format!( - "encountered critical edge in `Call` terminator {:?}", - terminator.kind, - ), - ); + // The call destination place and Operand::Move place used as an argument might be + // passed by a reference to the callee. Consequently they cannot be packed. + if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() { + // This is bad! The callee will expect the memory to be aligned. + self.fail( + location, + format!( + "encountered packed place in `Call` terminator destination: {:?}", + terminator.kind, + ), + ); + } } - // The call destination place and Operand::Move place used as an argument might be - // passed by a reference to the callee. Consequently they cannot be packed. - if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() { - // This is bad! The callee will expect the memory to be aligned. - self.fail( - location, - format!( - "encountered packed place in `Call` terminator destination: {:?}", - terminator.kind, - ), - ); - } for arg in args { if let Operand::Move(place) = &arg.node { if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() { @@ -1498,15 +1502,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - TerminatorKind::Call { func, .. } => { + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, - format!("encountered non-callable type {func_ty} in `Call` terminator"), + format!( + "encountered non-callable type {func_ty} in `{}` terminator", + terminator.kind.name() + ), ), } + + if let TerminatorKind::TailCall { .. } = terminator.kind { + // FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc) + } } TerminatorKind::Assert { cond, .. } => { let cond_ty = cond.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 94b553a07a7..7210701d482 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -1,6 +1,3 @@ -monomorphize_consider_type_length_limit = - consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate - monomorphize_couldnt_dump_mono_stats = unexpected error occurred while dumping monomorphization stats: {$error} @@ -25,8 +22,6 @@ monomorphize_start_not_found = using `fn main` requires the standard library monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined -monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` - monomorphize_unknown_cgu_collection_mode = unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 235743fccc8..bfd505c0672 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -222,12 +222,12 @@ use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; -use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; +use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{ self, AssocKind, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, VtblEntry, }; -use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_middle::{bug, span_bug}; use rustc_session::config::EntryFnType; use rustc_session::Limit; @@ -238,9 +238,7 @@ use rustc_target::abi::Size; use std::path::PathBuf; use tracing::{debug, instrument, trace}; -use crate::errors::{ - self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit, TypeLengthLimit, -}; +use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit}; use move_check::MoveCheckState; #[derive(PartialEq)] @@ -443,7 +441,6 @@ fn collect_items_rec<'tcx>( recursion_depths, recursion_limit, )); - check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { collect_items_of_instance( @@ -554,34 +551,6 @@ fn collect_items_rec<'tcx>( } } -/// Format instance name that is already known to be too long for rustc. -/// Show only the first 2 types if it is longer than 32 characters to avoid blasting -/// the user's terminal with thousands of lines of type-name. -/// -/// If the type name is longer than before+after, it will be written to a file. -fn shrunk_instance_name<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, -) -> (String, Option<PathBuf>) { - let s = instance.to_string(); - - // Only use the shrunk version if it's really shorter. - // This also avoids the case where before and after slices overlap. - if s.chars().nth(33).is_some() { - let shrunk = format!("{}", ty::ShortInstance(instance, 4)); - if shrunk == s { - return (s, None); - } - - let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None); - let written_to_path = std::fs::write(&path, s).ok().map(|_| path); - - (shrunk, written_to_path) - } else { - (s, None) - } -} - fn check_recursion_limit<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, @@ -630,38 +599,6 @@ fn check_recursion_limit<'tcx>( (def_id, recursion_depth) } -fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { - let type_length = instance - .args - .iter() - .flat_map(|arg| arg.walk()) - .filter(|arg| match arg.unpack() { - GenericArgKind::Type(_) | GenericArgKind::Const(_) => true, - GenericArgKind::Lifetime(_) => false, - }) - .count(); - debug!(" => type length={}", type_length); - - // Rust code can easily create exponentially-long types using only a - // polynomial recursion depth. Even with the default recursion - // depth, you can easily get cases that take >2^60 steps to run, - // which means that rustc basically hangs. - // - // Bail out in these cases to avoid that bad user experience. - if !tcx.type_length_limit().value_within_limit(type_length) { - let (shrunk, written_to_path) = shrunk_instance_name(tcx, instance); - let span = tcx.def_span(instance.def_id()); - let mut path = PathBuf::new(); - let was_written = if let Some(path2) = written_to_path { - path = path2; - Some(()) - } else { - None - }; - tcx.dcx().emit_fatal(TypeLengthLimit { span, shrunk, was_written, path, type_length }); - } -} - struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, @@ -818,7 +755,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); @@ -916,7 +854,7 @@ fn visit_fn_use<'tcx>( ) { if let ty::FnDef(def_id, args) = *ty.kind() { let instance = if is_direct_call { - ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, source) } else { match ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, args) { Some(instance) => instance, @@ -1223,10 +1161,10 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt }); } } - GlobalAlloc::Function(fn_instance) => { - if should_codegen_locally(tcx, fn_instance) { - trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); + GlobalAlloc::Function { instance, .. } => { + if should_codegen_locally(tcx, instance) { + trace!("collecting {:?} with {:#?}", alloc_id, instance); + output.push(create_fn_mono_item(tcx, instance, DUMMY_SP)); } } GlobalAlloc::VTable(ty, trait_ref) => { @@ -1319,7 +1257,7 @@ fn visit_mentioned_item<'tcx>( MentionedItem::Fn(ty) => { if let ty::FnDef(def_id, args) = *ty.kind() { let instance = - Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, span); // `visit_instance_use` was written for "used" item collection but works just as well // for "mentioned" item collection. // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway @@ -1544,6 +1482,7 @@ impl<'v> RootCollector<'_, 'v> { ty::ParamEnv::reveal_all(), start_def_id, self.tcx.mk_args(&[main_ret_ty.into()]), + DUMMY_SP, ); self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); @@ -1612,9 +1551,10 @@ fn create_mono_items_for_default_impls<'tcx>( } // As mentioned above, the method is legal to eagerly instantiate if it - // only has lifetime generic parameters. This is validated by + // only has lifetime generic parameters. This is validated by calling + // `own_requires_monomorphization` on both the impl and method. let args = trait_ref.args.extend_to(tcx, method.def_id, only_region_params); - let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args); + let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args, DUMMY_SP); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, instance) { diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index c0d1efd96c5..9548c46e6fa 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -20,19 +20,6 @@ pub struct RecursionLimit { } #[derive(Diagnostic)] -#[diag(monomorphize_type_length_limit)] -#[help(monomorphize_consider_type_length_limit)] -pub struct TypeLengthLimit { - #[primary_span] - pub span: Span, - pub shrunk: String, - #[note(monomorphize_written_to_path)] - pub was_written: Option<()>, - pub path: PathBuf, - pub type_length: usize, -} - -#[derive(Diagnostic)] #[diag(monomorphize_no_optimized_mir)] pub struct NoOptimizedMir { #[note] diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 695d02705ab..82488088e30 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -4,7 +4,8 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{ - self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Interner, + self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, + Interner, }; use crate::delegate::SolverDelegate; diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs new file mode 100644 index 00000000000..55f602d907b --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -0,0 +1,469 @@ +use std::fmt::Debug; +use std::ops::ControlFlow; + +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use tracing::instrument; + +/// Whether we do the orphan check relative to this crate or to some remote crate. +#[derive(Copy, Clone, Debug)] +pub enum InCrate { + Local { mode: OrphanCheckMode }, + Remote, +} + +#[derive(Copy, Clone, Debug)] +pub enum OrphanCheckMode { + /// Proper orphan check. + Proper, + /// Improper orphan check for backward compatibility. + /// + /// In this mode, type params inside projections are considered to be covered + /// even if the projection may normalize to a type that doesn't actually cover + /// them. This is unsound. See also [#124559] and [#99554]. + /// + /// [#124559]: https://github.com/rust-lang/rust/issues/124559 + /// [#99554]: https://github.com/rust-lang/rust/issues/99554 + Compat, +} + +#[derive(Debug, Copy, Clone)] +pub enum Conflict { + Upstream, + Downstream, +} + +/// Returns whether all impls which would apply to the `trait_ref` +/// e.g. `Ty: Trait<Arg>` are already known in the local crate. +/// +/// This both checks whether any downstream or sibling crates could +/// implement it and whether an upstream crate can add this impl +/// without breaking backwards compatibility. +#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] +pub fn trait_ref_is_knowable<Infcx, I, E>( + infcx: &Infcx, + trait_ref: ty::TraitRef<I>, + mut lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, +) -> Result<Result<(), Conflict>, E> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + E: Debug, +{ + if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() + { + // A downstream or cousin crate is allowed to implement some + // generic parameters of this trait-ref. + return Ok(Err(Conflict::Downstream)); + } + + if trait_ref_is_local_or_fundamental(infcx.cx(), trait_ref) { + // This is a local or fundamental trait, so future-compatibility + // is no concern. We know that downstream/cousin crates are not + // allowed to implement a generic parameter of this trait ref, + // which means impls could only come from dependencies of this + // crate, which we already know about. + return Ok(Ok(())); + } + + // This is a remote non-fundamental trait, so if another crate + // can be the "final owner" of the generic parameters of this trait-ref, + // they are allowed to implement it future-compatibly. + // + // However, if we are a final owner, then nobody else can be, + // and if we are an intermediate owner, then we don't care + // about future-compatibility, which means that we're OK if + // we are an owner. + if orphan_check_trait_ref( + infcx, + trait_ref, + InCrate::Local { mode: OrphanCheckMode::Proper }, + &mut lazily_normalize_ty, + )? + .is_ok() + { + Ok(Ok(())) + } else { + Ok(Err(Conflict::Upstream)) + } +} + +pub fn trait_ref_is_local_or_fundamental<I: Interner>(tcx: I, trait_ref: ty::TraitRef<I>) -> bool { + trait_ref.def_id.is_local() || tcx.trait_is_fundamental(trait_ref.def_id) +} + +#[derive(Debug, Copy, Clone)] +pub enum IsFirstInputType { + No, + Yes, +} + +impl From<bool> for IsFirstInputType { + fn from(b: bool) -> IsFirstInputType { + match b { + false => IsFirstInputType::No, + true => IsFirstInputType::Yes, + } + } +} + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "T: Debug"))] +pub enum OrphanCheckErr<I: Interner, T> { + NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>), + UncoveredTyParams(UncoveredTyParams<I, T>), +} + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "T: Debug"))] +pub struct UncoveredTyParams<I: Interner, T> { + pub uncovered: T, + pub local_ty: Option<I::Ty>, +} + +/// Checks whether a trait-ref is potentially implementable by a crate. +/// +/// The current rule is that a trait-ref orphan checks in a crate C: +/// +/// 1. Order the parameters in the trait-ref in generic parameters order +/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). +/// 2. Of these type parameters, there is at least one type parameter +/// in which, walking the type as a tree, you can reach a type local +/// to C where all types in-between are fundamental types. Call the +/// first such parameter the "local key parameter". +/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType +/// going through `Box`, which is fundamental. +/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for +/// the same reason. +/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's +/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between +/// the local type and the type parameter. +/// 3. Before this local type, no generic type parameter of the impl must +/// be reachable through fundamental types. +/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. +/// - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is +/// reachable through the fundamental type `Box`. +/// 4. Every type in the local key parameter not known in C, going +/// through the parameter's type tree, must appear only as a subtree of +/// a type local to C, with only fundamental types between the type +/// local to C and the local key parameter. +/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) +/// is bad, because the only local type with `T` as a subtree is +/// `LocalType<T>`, and `Vec<->` is between it and the type parameter. +/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because +/// the second occurrence of `T` is not a subtree of *any* local type. +/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of +/// `LocalType<Vec<T>>`, which is local and has no types between it and +/// the type parameter. +/// +/// The orphan rules actually serve several different purposes: +/// +/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where +/// every type local to one crate is unknown in the other) can't implement +/// the same trait-ref. This follows because it can be seen that no such +/// type can orphan-check in 2 such crates. +/// +/// To check that a local impl follows the orphan rules, we check it in +/// InCrate::Local mode, using type parameters for the "generic" types. +/// +/// In InCrate::Local mode the orphan check succeeds if the current crate +/// is definitely allowed to implement the given trait (no false positives). +/// +/// 2. They ground negative reasoning for coherence. If a user wants to +/// write both a conditional blanket impl and a specific impl, we need to +/// make sure they do not overlap. For example, if we write +/// ```ignore (illustrative) +/// impl<T> IntoIterator for Vec<T> +/// impl<T: Iterator> IntoIterator for T +/// ``` +/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. +/// We can observe that this holds in the current crate, but we need to make +/// sure this will also hold in all unknown crates (both "independent" crates, +/// which we need for link-safety, and also child crates, because we don't want +/// child crates to get error for impl conflicts in a *dependency*). +/// +/// For that, we only allow negative reasoning if, for every assignment to the +/// inference variables, every unknown crate would get an orphan error if they +/// try to implement this trait-ref. To check for this, we use InCrate::Remote +/// mode. That is sound because we already know all the impls from known crates. +/// +/// In InCrate::Remote mode the orphan check succeeds if a foreign crate +/// *could* implement the given trait (no false negatives). +/// +/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can +/// add "non-blanket" impls without breaking negative reasoning in dependent +/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. +/// +/// For that, we only allow a crate to perform negative reasoning on +/// non-local-non-`#[fundamental]` if there's a local key parameter as per (2). +/// +/// Because we never perform negative reasoning generically (coherence does +/// not involve type parameters), this can be interpreted as doing the full +/// orphan check (using InCrate::Local mode), instantiating non-local known +/// types for all inference variables. +/// +/// This allows for crates to future-compatibly add impls as long as they +/// can't apply to types with a key parameter in a child crate - applying +/// the rules, this basically means that every type parameter in the impl +/// must appear behind a non-fundamental type (because this is not a +/// type-system requirement, crate owners might also go for "semantic +/// future-compatibility" involving things such as sealed traits, but +/// the above requirement is sufficient, and is necessary in "open world" +/// cases). +/// +/// Note that this function is never called for types that have both type +/// parameters and inference variables. +#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] +pub fn orphan_check_trait_ref<Infcx, I, E: Debug>( + infcx: &Infcx, + trait_ref: ty::TraitRef<I>, + in_crate: InCrate, + lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, +) -> Result<Result<(), OrphanCheckErr<I, I::Ty>>, E> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + E: Debug, +{ + if trait_ref.has_param() { + panic!("orphan check only expects inference variables: {trait_ref:?}"); + } + + let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); + Ok(match trait_ref.visit_with(&mut checker) { + ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), + ControlFlow::Break(residual) => match residual { + OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), + OrphanCheckEarlyExit::UncoveredTyParam(ty) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + let local_ty = match trait_ref.visit_with(&mut checker) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), + _ => None, + }; + Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { + uncovered: ty, + local_ty, + })) + } + OrphanCheckEarlyExit::LocalTy(_) => Ok(()), + }, + }) +} + +struct OrphanChecker<'a, Infcx, I: Interner, F> { + infcx: &'a Infcx, + in_crate: InCrate, + in_self_ty: bool, + lazily_normalize_ty: F, + /// Ignore orphan check failures and exclusively search for the first local type. + search_first_local_ty: bool, + non_local_tys: Vec<(I::Ty, IsFirstInputType)>, +} + +impl<'a, Infcx, I, F, E> OrphanChecker<'a, Infcx, I, F> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + F: FnOnce(I::Ty) -> Result<I::Ty, E>, +{ + fn new(infcx: &'a Infcx, in_crate: InCrate, lazily_normalize_ty: F) -> Self { + OrphanChecker { + infcx, + in_crate, + in_self_ty: true, + lazily_normalize_ty, + search_first_local_ty: false, + non_local_tys: Vec::new(), + } + } + + fn found_non_local_ty(&mut self, t: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { + self.non_local_tys.push((t, self.in_self_ty.into())); + ControlFlow::Continue(()) + } + + fn found_uncovered_ty_param(&mut self, ty: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { + if self.search_first_local_ty { + return ControlFlow::Continue(()); + } + + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) + } + + fn def_id_is_local(&mut self, def_id: I::DefId) -> bool { + match self.in_crate { + InCrate::Local { .. } => def_id.is_local(), + InCrate::Remote => false, + } + } +} + +enum OrphanCheckEarlyExit<I: Interner, E> { + NormalizationFailure(E), + UncoveredTyParam(I::Ty), + LocalTy(I::Ty), +} + +impl<'a, Infcx, I, F, E> TypeVisitor<I> for OrphanChecker<'a, Infcx, I, F> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + F: FnMut(I::Ty) -> Result<I::Ty, E>, +{ + type Result = ControlFlow<OrphanCheckEarlyExit<I, E>>; + + fn visit_region(&mut self, _r: I::Region) -> Self::Result { + ControlFlow::Continue(()) + } + + fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + let ty = self.infcx.shallow_resolve(ty); + let ty = match (self.lazily_normalize_ty)(ty) { + Ok(norm_ty) if norm_ty.is_ty_var() => ty, + Ok(norm_ty) => norm_ty, + Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), + }; + + let result = match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::Pat(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) => self.found_non_local_ty(ty), + + ty::Param(..) => panic!("unexpected ty param"), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + match self.in_crate { + InCrate::Local { .. } => self.found_uncovered_ty_param(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + } + } + + // A rigid alias may normalize to anything. + // * If it references an infer var, placeholder or bound ty, it may + // normalize to that, so we have to treat it as an uncovered ty param. + // * Otherwise it may normalize to any non-type-generic type + // be it local or non-local. + ty::Alias(kind, _) => { + if ty.has_type_flags( + ty::TypeFlags::HAS_TY_PLACEHOLDER + | ty::TypeFlags::HAS_TY_BOUND + | ty::TypeFlags::HAS_TY_INFER, + ) { + match self.in_crate { + InCrate::Local { mode } => match kind { + ty::Projection => { + if let OrphanCheckMode::Compat = mode { + ControlFlow::Continue(()) + } else { + self.found_uncovered_ty_param(ty) + } + } + _ => self.found_uncovered_ty_param(ty), + }, + InCrate::Remote => { + // The inference variable might be unified with a local + // type in that remote crate. + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + } + } else { + // Regarding *opaque types* specifically, we choose to treat them as non-local, + // even those that appear within the same crate. This seems somewhat surprising + // at first, but makes sense when you consider that opaque types are supposed + // to hide the underlying type *within the same crate*. When an opaque type is + // used from outside the module where it is declared, it should be impossible to + // observe anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type to determine whether + // or not the opaque type itself should be considered local. + // + // However, this could make it a breaking change to switch the underlying hidden + // type from a local type to a remote type. This would violate the rule that + // opaque types should be completely opaque apart from the traits that they + // implement, so we don't use this behavior. + // Addendum: Moreover, revealing the underlying type is likely to cause cycle + // errors as we rely on coherence / the specialization graph during typeck. + + self.found_non_local_ty(ty) + } + } + + // For fundamental types, we just look inside of them. + ty::Ref(_, ty, _) => ty.visit_with(self), + ty::Adt(def, args) => { + if self.def_id_is_local(def.def_id()) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else if def.is_fundamental() { + args.visit_with(self) + } else { + self.found_non_local_ty(ty) + } + } + ty::Foreign(def_id) => { + if self.def_id_is_local(def_id) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Dynamic(tt, ..) => { + let principal = tt.principal().map(|p| p.def_id()); + if principal.is_some_and(|p| self.def_id_is_local(p)) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => { + if self.def_id_is_local(did) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + // This should only be created when checking whether we have to check whether some + // auto trait impl applies. There will never be multiple impls, so we can just + // act as if it were a local type here. + ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + }; + // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so + // the first type we visit is always the self type. + self.in_self_ty = false; + result + } + + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. + fn visit_const(&mut self, _c: I::Const) -> Self::Result { + ControlFlow::Continue(()) + } +} diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 6626acfe963..6a3d58b5906 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,18 +1,19 @@ -use std::fmt::Debug; +use std::ops::Deref; use rustc_type_ir::fold::TypeFoldable; -use rustc_type_ir::relate::Relate; use rustc_type_ir::solve::{Certainty, Goal, NoSolution, SolverMode}; -use rustc_type_ir::{self as ty, Interner}; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -pub trait SolverDelegate: Sized { +pub trait SolverDelegate: + Deref<Target: InferCtxtLike<Interner = <Self as SolverDelegate>::Interner>> + Sized +{ type Interner: Interner; - fn cx(&self) -> Self::Interner; + fn cx(&self) -> Self::Interner { + (**self).cx() + } type Span: Copy; - fn solver_mode(&self) -> SolverMode; - fn build_with_canonical<V>( cx: Self::Interner, solver_mode: SolverMode, @@ -21,109 +22,21 @@ pub trait SolverDelegate: Sized { where V: TypeFoldable<Self::Interner>; - fn universe(&self) -> ty::UniverseIndex; - fn create_next_universe(&self) -> ty::UniverseIndex; - - fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>; - fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>; - fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>; - - fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; - fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; - - fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_float_var( - &self, - vid: ty::FloatVid, - ) -> <Self::Interner as Interner>::Ty; - fn opportunistic_resolve_ct_var( - &self, - vid: ty::ConstVid, - ) -> <Self::Interner as Interner>::Const; - fn opportunistic_resolve_effect_var( - &self, - vid: ty::EffectVid, - ) -> <Self::Interner as Interner>::Const; - fn opportunistic_resolve_lt_var( - &self, - vid: ty::RegionVid, - ) -> <Self::Interner as Interner>::Region; - - fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes; - - fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty; - fn next_const_infer(&self) -> <Self::Interner as Interner>::Const; - fn fresh_args_for_item( - &self, - def_id: <Self::Interner as Interner>::DefId, - ) -> <Self::Interner as Interner>::GenericArgs; - fn fresh_var_for_kind_with_span( &self, arg: <Self::Interner as Interner>::GenericArg, span: Self::Span, ) -> <Self::Interner as Interner>::GenericArg; - fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>( - &self, - value: ty::Binder<Self::Interner, T>, - ) -> T; - - fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>( - &self, - value: ty::Binder<Self::Interner, T>, - f: impl FnOnce(T) -> U, - ) -> U; - - fn relate<T: Relate<Self::Interner>>( - &self, - param_env: <Self::Interner as Interner>::ParamEnv, - lhs: T, - variance: ty::Variance, - rhs: T, - ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; - - fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>( - &self, - param_env: <Self::Interner as Interner>::ParamEnv, - lhs: T, - rhs: T, - ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; - - fn resolve_vars_if_possible<T>(&self, value: T) -> T - where - T: TypeFoldable<Self::Interner>; - - fn probe<T>(&self, probe: impl FnOnce() -> T) -> T; - // FIXME: Uplift the leak check into this crate. fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; - // FIXME: This is only here because elaboration lives in `rustc_infer`! - fn elaborate_supertraits( - cx: Self::Interner, - trait_ref: ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>, - ) -> impl Iterator<Item = ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>>; - fn try_const_eval_resolve( &self, param_env: <Self::Interner as Interner>::ParamEnv, unevaluated: ty::UnevaluatedConst<Self::Interner>, ) -> Option<<Self::Interner as Interner>::Const>; - fn sub_regions( - &self, - sub: <Self::Interner as Interner>::Region, - sup: <Self::Interner as Interner>::Region, - ); - - fn register_ty_outlives( - &self, - ty: <Self::Interner as Interner>::Ty, - r: <Self::Interner as Interner>::Region, - ); - // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! fn well_formed_goals( &self, @@ -179,14 +92,6 @@ pub trait SolverDelegate: Sized { fn reset_opaque_types(&self); - fn trait_ref_is_knowable<E: Debug>( - &self, - trait_ref: ty::TraitRef<Self::Interner>, - lazily_normalize_ty: impl FnMut( - <Self::Interner as Interner>::Ty, - ) -> Result<<Self::Interner as Interner>::Ty, E>, - ) -> Result<bool, E>; - fn fetch_eligible_assoc_item( &self, param_env: <Self::Interner as Interner>::ParamEnv, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index a6a9c01faaa..0a5b4278058 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -5,6 +5,8 @@ //! So if you got to this crate from the old solver, it's totally normal. pub mod canonicalizer; +pub mod coherence; pub mod delegate; +pub mod relate; pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/relate.rs b/compiler/rustc_next_trait_solver/src/relate.rs new file mode 100644 index 00000000000..db819961bbd --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/relate.rs @@ -0,0 +1,15 @@ +pub use rustc_type_ir::relate::*; + +pub mod combine; + +/// Whether aliases should be related structurally or not. Used +/// to adjust the behavior of generalization and combine. +/// +/// This should always be `No` unless in a few special-cases when +/// instantiating canonical responses and in the new solver. Each +/// such case should have a comment explaining why it is used. +#[derive(Debug, Copy, Clone)] +pub enum StructurallyRelateAliases { + Yes, + No, +} diff --git a/compiler/rustc_next_trait_solver/src/relate/combine.rs b/compiler/rustc_next_trait_solver/src/relate/combine.rs new file mode 100644 index 00000000000..96968327d8e --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/relate/combine.rs @@ -0,0 +1,34 @@ +pub use rustc_type_ir::relate::*; +use rustc_type_ir::solve::Goal; +use rustc_type_ir::{InferCtxtLike, Interner, Upcast}; + +use super::StructurallyRelateAliases; + +pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>: + TypeRelation<I> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, +{ + fn span(&self) -> I::Span; + + fn param_env(&self) -> I::ParamEnv; + + /// Whether aliases should be related structurally. This is pretty much + /// always `No` unless you're equating in some specific locations of the + /// new solver. See the comments in these use-cases for more details. + fn structurally_relate_aliases(&self) -> StructurallyRelateAliases; + + /// Register obligations that must hold in order for this relation to hold + fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>); + + /// Register predicates that must hold in order for this relation to hold. + /// This uses the default `param_env` of the obligation. + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>, + ); + + /// Register `AliasRelate` obligation(s) that both types must be related to each other. + fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty); +} diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 6ed58d0e4fb..254ee514f8b 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -2,7 +2,7 @@ use crate::delegate::SolverDelegate; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; -use rustc_type_ir::{self as ty, Interner}; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; /////////////////////////////////////////////////////////////////////////// // EAGER RESOLUTION diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 21439530c08..01dde9ca587 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use rustc_type_ir::elaborate; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; @@ -269,6 +270,11 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, ) -> Vec<Candidate<I>>; + + fn consider_builtin_effects_intersection_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution>; } impl<D, I> EvalCtxt<'_, D> @@ -382,46 +388,83 @@ where G::consider_auto_trait_candidate(self, goal) } else if cx.trait_is_alias(trait_def_id) { G::consider_trait_alias_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) { - G::consider_builtin_sized_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy) - || cx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone) - { - G::consider_builtin_copy_clone_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) { - G::consider_builtin_pointer_like_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) { - G::consider_builtin_fn_ptr_trait_candidate(self, goal) - } else if let Some(kind) = self.cx().fn_trait_kind_from_def_id(trait_def_id) { - G::consider_builtin_fn_trait_candidates(self, goal, kind) - } else if let Some(kind) = self.cx().async_fn_trait_kind_from_def_id(trait_def_id) { - G::consider_builtin_async_fn_trait_candidates(self, goal, kind) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) { - G::consider_builtin_async_fn_kind_helper_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) { - G::consider_builtin_tuple_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) { - G::consider_builtin_pointee_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) { - G::consider_builtin_future_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) { - G::consider_builtin_iterator_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) { - G::consider_builtin_fused_iterator_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) { - G::consider_builtin_async_iterator_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) { - G::consider_builtin_coroutine_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) { - G::consider_builtin_discriminant_kind_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) { - G::consider_builtin_async_destruct_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) { - G::consider_builtin_destruct_candidate(self, goal) - } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) { - G::consider_builtin_transmute_candidate(self, goal) } else { - Err(NoSolution) + match cx.as_lang_item(trait_def_id) { + Some(TraitSolverLangItem::Sized) => G::consider_builtin_sized_candidate(self, goal), + Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + G::consider_builtin_copy_clone_candidate(self, goal) + } + Some(TraitSolverLangItem::Fn) => { + G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::Fn) + } + Some(TraitSolverLangItem::FnMut) => { + G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnMut) + } + Some(TraitSolverLangItem::FnOnce) => { + G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnOnce) + } + Some(TraitSolverLangItem::AsyncFn) => { + G::consider_builtin_async_fn_trait_candidates(self, goal, ty::ClosureKind::Fn) + } + Some(TraitSolverLangItem::AsyncFnMut) => { + G::consider_builtin_async_fn_trait_candidates( + self, + goal, + ty::ClosureKind::FnMut, + ) + } + Some(TraitSolverLangItem::AsyncFnOnce) => { + G::consider_builtin_async_fn_trait_candidates( + self, + goal, + ty::ClosureKind::FnOnce, + ) + } + Some(TraitSolverLangItem::PointerLike) => { + G::consider_builtin_pointer_like_candidate(self, goal) + } + Some(TraitSolverLangItem::FnPtrTrait) => { + G::consider_builtin_fn_ptr_trait_candidate(self, goal) + } + Some(TraitSolverLangItem::AsyncFnKindHelper) => { + G::consider_builtin_async_fn_kind_helper_candidate(self, goal) + } + Some(TraitSolverLangItem::Tuple) => G::consider_builtin_tuple_candidate(self, goal), + Some(TraitSolverLangItem::PointeeTrait) => { + G::consider_builtin_pointee_candidate(self, goal) + } + Some(TraitSolverLangItem::Future) => { + G::consider_builtin_future_candidate(self, goal) + } + Some(TraitSolverLangItem::Iterator) => { + G::consider_builtin_iterator_candidate(self, goal) + } + Some(TraitSolverLangItem::FusedIterator) => { + G::consider_builtin_fused_iterator_candidate(self, goal) + } + Some(TraitSolverLangItem::AsyncIterator) => { + G::consider_builtin_async_iterator_candidate(self, goal) + } + Some(TraitSolverLangItem::Coroutine) => { + G::consider_builtin_coroutine_candidate(self, goal) + } + Some(TraitSolverLangItem::DiscriminantKind) => { + G::consider_builtin_discriminant_kind_candidate(self, goal) + } + Some(TraitSolverLangItem::AsyncDestruct) => { + G::consider_builtin_async_destruct_candidate(self, goal) + } + Some(TraitSolverLangItem::Destruct) => { + G::consider_builtin_destruct_candidate(self, goal) + } + Some(TraitSolverLangItem::TransmuteTrait) => { + G::consider_builtin_transmute_candidate(self, goal) + } + Some(TraitSolverLangItem::EffectsIntersection) => { + G::consider_builtin_effects_intersection_candidate(self, goal) + } + _ => Err(NoSolution), + } }; candidates.extend(result); @@ -625,7 +668,7 @@ where // a projection goal. if let Some(principal) = bounds.principal() { let principal_trait_ref = principal.with_self_ty(cx, self_ty); - for (idx, assumption) in D::elaborate_supertraits(cx, principal_trait_ref).enumerate() { + for (idx, assumption) in elaborate::supertraits(cx, principal_trait_ref).enumerate() { candidates.extend(G::probe_and_consider_object_bound_candidate( self, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)), diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 0cef8d9f4bc..7df14e81ab5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -668,8 +668,11 @@ where { let cx = ecx.cx(); let mut requirements = vec![]; - requirements - .extend(cx.super_predicates_of(trait_ref.def_id).iter_instantiated(cx, trait_ref.args)); + requirements.extend( + cx.explicit_super_predicates_of(trait_ref.def_id) + .iter_instantiated(cx, trait_ref.args) + .map(|(pred, _)| pred), + ); // FIXME(associated_const_equality): Also add associated consts to // the requirements here. diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 0a313c6a951..9474d501d6f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -14,7 +14,7 @@ use std::iter; use rustc_index::IndexVec; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, Interner}; +use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner}; use tracing::{instrument, trace}; use crate::canonicalizer::{CanonicalizeMode, Canonicalizer}; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 87342eefb33..c90f8e76163 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -7,10 +7,11 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; -use rustc_type_ir::{self as ty, CanonicalVarValues, Interner}; +use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{instrument, trace}; +use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; @@ -906,7 +907,8 @@ where ) -> Result<bool, NoSolution> { let delegate = self.delegate; let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty); - delegate.trait_ref_is_knowable(trait_ref, lazily_normalize_ty) + coherence::trait_ref_is_knowable(&**delegate, trait_ref, lazily_normalize_ty) + .map(|is_knowable| is_knowable.is_ok()) } pub(super) fn fetch_eligible_assoc_item( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index e9516c60c70..4258dd9263a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use rustc_type_ir::Interner; +use rustc_type_ir::{InferCtxtLike, Interner}; use tracing::instrument; use crate::delegate::SolverDelegate; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 4e8cb4384f4..a83bd689a80 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -3,6 +3,7 @@ mod inherent; mod opaque_types; mod weak_types; +use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::Upcast as _; @@ -144,7 +145,7 @@ where let goal_trait_ref = goal.predicate.alias.trait_ref(cx); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); - if !ecx.cx().args_may_unify_deep( + if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup).args_may_unify( goal.predicate.alias.trait_ref(cx).args, impl_trait_ref.skip_binder().args, ) { @@ -864,6 +865,68 @@ where ) -> Result<Candidate<I>, NoSolution> { panic!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) } + + fn consider_builtin_effects_intersection_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + let ty::Tuple(types) = goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + let cx = ecx.cx(); + + let mut first_non_maybe = None; + let mut non_maybe_count = 0; + for ty in types.iter() { + if !matches!(ty::EffectKind::try_from_ty(cx, ty), Some(ty::EffectKind::Maybe)) { + first_non_maybe.get_or_insert(ty); + non_maybe_count += 1; + } + } + + match non_maybe_count { + 0 => { + let ty = ty::EffectKind::Maybe.to_ty(cx); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + 1 => { + let ty = first_non_maybe.unwrap(); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + _ => { + let mut min = ty::EffectKind::Maybe; + + for ty in types.iter() { + // We can't find the intersection if the types used are generic. + // + // FIXME(effects) do we want to look at where clauses to get some + // clue for the case where generic types are being used? + let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else { + return Err(NoSolution); + }; + + let Some(result) = ty::EffectKind::intersection(min, kind) else { + return Err(NoSolution); + }; + + min = result; + } + + let ty = min.to_ty(cx); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + } + } } impl<D, I> EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 2bc9d35c2b0..4474bbc2351 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -2,10 +2,11 @@ use rustc_ast_ir::Movability; use rustc_type_ir::data_structures::IndexSet; +use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::visit::TypeVisitableExt as _; -use rustc_type_ir::{self as ty, Interner, TraitPredicate, Upcast as _}; +use rustc_type_ir::{self as ty, elaborate, Interner, TraitPredicate, Upcast as _}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; @@ -46,7 +47,8 @@ where let cx = ecx.cx(); let impl_trait_ref = cx.impl_trait_ref(impl_def_id); - if !cx.args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) + if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup) + .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { return Err(NoSolution); } @@ -702,6 +704,47 @@ where } }) } + + fn consider_builtin_effects_intersection_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let ty::Tuple(types) = goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + let cx = ecx.cx(); + let maybe_count = types + .iter() + .filter_map(|ty| ty::EffectKind::try_from_ty(cx, ty)) + .filter(|&ty| ty == ty::EffectKind::Maybe) + .count(); + + // Don't do concrete type check unless there are more than one type that will influence the result. + // This would allow `(Maybe, T): Min` pass even if we know nothing about `T`. + if types.len() - maybe_count > 1 { + let mut min = ty::EffectKind::Maybe; + + for ty in types.iter() { + let Some(kind) = ty::EffectKind::try_from_ty(ecx.cx(), ty) else { + return Err(NoSolution); + }; + + let Some(result) = ty::EffectKind::intersection(min, kind) else { + return Err(NoSolution); + }; + + min = result; + } + } + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } } impl<D, I> EvalCtxt<'_, D> @@ -744,7 +787,7 @@ where )); } else if let Some(a_principal) = a_data.principal() { for new_a_principal in - D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1) + elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1) { responses.extend(self.consider_builtin_upcast_to_principal( goal, @@ -819,8 +862,7 @@ where .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - self.cx() - .supertrait_def_ids(principal_def_id) + elaborate::supertrait_def_ids(self.cx(), principal_def_id) .into_iter() .filter(|def_id| self.cx().trait_is_auto(*def_id)) })) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f08efe60d96..e4c75ac1145 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -45,10 +45,6 @@ parse_bad_assoc_type_bounds = bounds on associated types do not belong here parse_bad_item_kind = {$descr} is not supported in {$ctx} .help = consider moving the {$descr} out to a nearby module scope -parse_bad_return_type_notation_dotdot = - return type notation uses `()` instead of `(..)` for elided arguments - .suggestion = remove the `..` - parse_bad_return_type_notation_output = return type not allowed with return type notation .suggestion = remove the return type diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8d49887f164..6894f470d88 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2568,14 +2568,6 @@ pub(crate) struct BadReturnTypeNotationOutput { } #[derive(Diagnostic)] -#[diag(parse_bad_return_type_notation_dotdot)] -pub(crate) struct BadReturnTypeNotationDotDot { - #[primary_span] - #[suggestion(code = "", applicability = "maybe-incorrect")] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(parse_bad_assoc_type_bounds)] pub(crate) struct BadAssocTypeBounds { #[primary_span] diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 58fef9b6c45..a8fe35f45b3 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -282,7 +282,7 @@ impl<'a> Parser<'a> { pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> { let mut attrs = ast::AttrVec::new(); loop { - let start_pos: u32 = self.num_bump_calls.try_into().unwrap(); + let start_pos = self.num_bump_calls; // Only try to parse if it is an inner attribute (has `!`). let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) @@ -303,7 +303,7 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { - let end_pos: u32 = self.num_bump_calls.try_into().unwrap(); + let end_pos = self.num_bump_calls; // If we are currently capturing tokens, mark the location of this inner attribute. // If capturing ends up creating a `LazyAttrTokenStream`, we will include // this replace range with it, removing the inner attribute from the final @@ -313,7 +313,7 @@ impl<'a> Parser<'a> { // corresponding macro). let range = start_pos..end_pos; if let Capturing::Yes = self.capture_state.capturing { - self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![])); + self.capture_state.inner_attr_ranges.insert(attr.id, (range, None)); } attrs.push(attr); } else { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b5480b6b7d2..38f18022e3c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,6 +1,6 @@ use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttributesData, DelimSpacing}; +use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing}; use rustc_ast::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, ToAttrTokenStream}; use rustc_ast::{self as ast}; use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens}; @@ -8,7 +8,7 @@ use rustc_errors::PResult; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, DUMMY_SP}; -use std::ops::Range; +use std::{iter, mem}; /// A wrapper type to ensure that the parser handles outer attributes correctly. /// When we parse outer attributes, we need to ensure that we capture tokens @@ -29,15 +29,15 @@ pub struct AttrWrapper { // The start of the outer attributes in the token cursor. // This allows us to create a `ReplaceRange` for the entire attribute // target, including outer attributes. - start_pos: usize, + start_pos: u32, } impl AttrWrapper { - pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper { + pub(super) fn new(attrs: AttrVec, start_pos: u32) -> AttrWrapper { AttrWrapper { attrs, start_pos } } pub fn empty() -> AttrWrapper { - AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX } + AttrWrapper { attrs: AttrVec::new(), start_pos: u32::MAX } } pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec { @@ -53,7 +53,7 @@ impl AttrWrapper { // FIXME: require passing an NT to prevent misuse of this method pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) { let mut self_attrs = self.attrs; - std::mem::swap(attrs, &mut self_attrs); + mem::swap(attrs, &mut self_attrs); attrs.extend(self_attrs); } @@ -87,11 +87,10 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. -#[derive(Clone)] struct LazyAttrTokenStreamImpl { start_token: (Token, Spacing), cursor_snapshot: TokenCursor, - num_calls: usize, + num_calls: u32, break_last_token: bool, replace_ranges: Box<[ReplaceRange]>, } @@ -104,15 +103,16 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // produce an empty `TokenStream` if no calls were made, and omit the // final token otherwise. let mut cursor_snapshot = self.cursor_snapshot.clone(); - let tokens = - std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) - .chain(std::iter::repeat_with(|| { - let token = cursor_snapshot.next(); - (FlatToken::Token(token.0), token.1) - })) - .take(self.num_calls); - - if !self.replace_ranges.is_empty() { + let tokens = iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) + .chain(iter::repeat_with(|| { + let token = cursor_snapshot.next(); + (FlatToken::Token(token.0), token.1) + })) + .take(self.num_calls as usize); + + if self.replace_ranges.is_empty() { + make_attr_token_stream(tokens, self.break_last_token) + } else { let mut tokens: Vec<_> = tokens.collect(); let mut replace_ranges = self.replace_ranges.to_vec(); replace_ranges.sort_by_key(|(range, _)| range.start); @@ -144,29 +144,26 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // start position, we ensure that any replace range which encloses // another replace range will capture the *replaced* tokens for the inner // range, not the original tokens. - for (range, new_tokens) in replace_ranges.into_iter().rev() { + for (range, target) in replace_ranges.into_iter().rev() { assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); - // Replace ranges are only allowed to decrease the number of tokens. - assert!( - range.len() >= new_tokens.len(), - "Range {range:?} has greater len than {new_tokens:?}" - ); - - // Replace any removed tokens with `FlatToken::Empty`. - // This keeps the total length of `tokens` constant throughout the - // replacement process, allowing us to use all of the `ReplaceRanges` entries - // without adjusting indices. - let filler = std::iter::repeat((FlatToken::Empty, Spacing::Alone)) - .take(range.len() - new_tokens.len()); + // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus + // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the + // total length of `tokens` constant throughout the replacement process, allowing + // us to use all of the `ReplaceRanges` entries without adjusting indices. + let target_len = target.is_some() as usize; tokens.splice( (range.start as usize)..(range.end as usize), - new_tokens.into_iter().chain(filler), + target + .into_iter() + .map(|target| (FlatToken::AttrsTarget(target), Spacing::Alone)) + .chain( + iter::repeat((FlatToken::Empty, Spacing::Alone)) + .take(range.len() - target_len), + ), ); } - make_token_stream(tokens.into_iter(), self.break_last_token) - } else { - make_token_stream(tokens, self.break_last_token) + make_attr_token_stream(tokens.into_iter(), self.break_last_token) } } } @@ -218,24 +215,23 @@ impl<'a> Parser<'a> { let start_token = (self.token.clone(), self.token_spacing); let cursor_snapshot = self.token_cursor.clone(); let start_pos = self.num_bump_calls; - let has_outer_attrs = !attrs.attrs.is_empty(); - let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes); let replace_ranges_start = self.capture_state.replace_ranges.len(); - let ret = f(self, attrs.attrs); - - self.capture_state.capturing = prev_capturing; - - let (mut ret, trailing) = ret?; + let (mut ret, trailing) = { + let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes); + let ret_and_trailing = f(self, attrs.attrs); + self.capture_state.capturing = prev_capturing; + ret_and_trailing? + }; // When we're not in `capture-cfg` mode, then bail out early if: // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`) // so there's nothing for us to do. // 2. Our target already has tokens set (e.g. we've parsed something - // like `#[my_attr] $item`. The actual parsing code takes care of prepending - // any attributes to the nonterminal, so we don't need to modify the - // already captured tokens. + // like `#[my_attr] $item`). The actual parsing code takes care of + // prepending any attributes to the nonterminal, so we don't need to + // modify the already captured tokens. // Note that this check is independent of `force_collect`- if we already // have tokens, or can't even store them, then there's never a need to // force collection of new tokens. @@ -276,37 +272,32 @@ impl<'a> Parser<'a> { let replace_ranges_end = self.capture_state.replace_ranges.len(); - let mut end_pos = self.num_bump_calls; - - let mut captured_trailing = false; - // Capture a trailing token if requested by the callback 'f' - match trailing { - TrailingToken::None => {} + let captured_trailing = match trailing { + TrailingToken::None => false, TrailingToken::Gt => { assert_eq!(self.token.kind, token::Gt); + false } TrailingToken::Semi => { assert_eq!(self.token.kind, token::Semi); - end_pos += 1; - captured_trailing = true; + true } - TrailingToken::MaybeComma => { - if self.token.kind == token::Comma { - end_pos += 1; - captured_trailing = true; - } - } - } + TrailingToken::MaybeComma => self.token.kind == token::Comma, + }; - // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), - // then extend the range of captured tokens to include it, since the parser - // was not actually bumped past it. When the `LazyAttrTokenStream` gets converted - // into an `AttrTokenStream`, we will create the proper token. - if self.break_last_token { - assert!(!captured_trailing, "Cannot set break_last_token and have trailing token"); - end_pos += 1; - } + assert!( + !(self.break_last_token && captured_trailing), + "Cannot set break_last_token and have trailing token" + ); + + let end_pos = self.num_bump_calls + + captured_trailing as u32 + // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), then + // extend the range of captured tokens to include it, since the parser was not actually + // bumped past it. When the `LazyAttrTokenStream` gets converted into an + // `AttrTokenStream`, we will create the proper token. + + self.break_last_token as u32; let num_calls = end_pos - start_pos; @@ -318,14 +309,11 @@ impl<'a> Parser<'a> { // Grab any replace ranges that occur *inside* the current AST node. // We will perform the actual replacement when we convert the `LazyAttrTokenStream` // to an `AttrTokenStream`. - let start_calls: u32 = start_pos.try_into().unwrap(); self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] .iter() .cloned() .chain(inner_attr_replace_ranges.iter().cloned()) - .map(|(range, tokens)| { - ((range.start - start_calls)..(range.end - start_calls), tokens) - }) + .map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data)) .collect() }; @@ -340,7 +328,7 @@ impl<'a> Parser<'a> { // If we support tokens at all if let Some(target_tokens) = ret.tokens_mut() { if target_tokens.is_none() { - // Store se our newly captured tokens into the AST node + // Store our newly captured tokens into the AST node. *target_tokens = Some(tokens.clone()); } } @@ -355,18 +343,14 @@ impl<'a> Parser<'a> { && matches!(self.capture_state.capturing, Capturing::Yes) && has_cfg_or_cfg_attr(final_attrs) { - let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens }; + assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - // Replace the entire AST node that we just parsed, including attributes, - // with a `FlatToken::AttrTarget`. If this AST node is inside an item - // that has `#[derive]`, then this will allow us to cfg-expand this - // AST node. + // Replace the entire AST node that we just parsed, including attributes, with + // `target`. If this AST node is inside an item that has `#[derive]`, then this will + // allow us to cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; - let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)]; - - assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap()); - self.capture_state.replace_ranges.push((range, new_tokens)); + let target = AttrsTarget { attrs: final_attrs.iter().cloned().collect(), tokens }; + self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } @@ -382,10 +366,10 @@ impl<'a> Parser<'a> { } } -/// Converts a flattened iterator of tokens (including open and close delimiter tokens) -/// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair -/// of open and close delims. -fn make_token_stream( +/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an +/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and +/// close delims. +fn make_attr_token_stream( mut iter: impl Iterator<Item = (FlatToken, Spacing)>, break_last_token: bool, ) -> AttrTokenStream { @@ -428,11 +412,11 @@ fn make_token_stream( .expect("Bottom token frame is missing!") .inner .push(AttrTokenTree::Token(token, spacing)), - FlatToken::AttrTarget(data) => stack + FlatToken::AttrsTarget(target) => stack .last_mut() .expect("Bottom token frame is missing!") .inner - .push(AttrTokenTree::Attributes(data)), + .push(AttrTokenTree::AttrsTarget(target)), FlatToken::Empty => {} } token_and_spacing = iter.next(); @@ -464,6 +448,6 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); - static_assert_size!(LazyAttrTokenStreamImpl, 104); + static_assert_size!(LazyAttrTokenStreamImpl, 96); // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e0c70884fee..b2df9a14eb0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2327,7 +2327,7 @@ impl<'a> Parser<'a> { let before = self.prev_token.clone(); let binder = if self.check_keyword(kw::For) { let lo = self.token.span; - let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::closure_lifetime_binder, span); @@ -3432,8 +3432,9 @@ impl<'a> Parser<'a> { } } let capture_clause = self.parse_capture_clause()?; + let decl_span = lo.to(self.prev_token.span); let (attrs, body) = self.parse_inner_attrs_and_block()?; - let kind = ExprKind::Gen(capture_clause, body, kind); + let kind = ExprKind::Gen(capture_clause, body, kind, decl_span); Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } @@ -4022,7 +4023,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::Match(_, _, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) - | ExprKind::Gen(_, _, _) + | ExprKind::Gen(_, _, _, _) | ExprKind::TryBlock(_) | ExprKind::Underscore | ExprKind::Path(_, _) diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index fde16ac957d..10c7715c7dc 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -457,7 +457,7 @@ impl<'a> Parser<'a> { // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>` // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>` // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>` - let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; // Parse type with mandatory colon and (possibly empty) bounds, // or with mandatory equality sign and the second type. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index cfd0a72c056..45ca267fe5d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,7 +20,7 @@ use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; +use rustc_ast::tokenstream::{AttrsTarget, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::{ @@ -153,7 +153,7 @@ pub struct Parser<'a> { expected_tokens: Vec<TokenType>, token_cursor: TokenCursor, // The number of calls to `bump`, i.e. the position in the token stream. - num_bump_calls: usize, + num_bump_calls: u32, // During parsing we may sometimes need to 'unglue' a glued token into two // component tokens (e.g. '>>' into '>' and '>), so the parser can consume // them one at a time. This process bypasses the normal capturing mechanism @@ -192,7 +192,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(Parser<'_>, 264); +rustc_data_structures::static_assert_size!(Parser<'_>, 256); /// Stores span information about a closure. #[derive(Clone, Debug)] @@ -203,13 +203,13 @@ struct ClosureSpans { } /// Indicates a range of tokens that should be replaced by -/// the tokens in the provided vector. This is used in two +/// the tokens in the provided `AttrsTarget`. This is used in two /// places during token collection: /// /// 1. During the parsing of an AST node that may have a `#[derive]` /// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]` /// In this case, we use a `ReplaceRange` to replace the entire inner AST node -/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion +/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion /// on an `AttrTokenStream`. /// /// 2. When we parse an inner attribute while collecting tokens. We @@ -219,7 +219,7 @@ struct ClosureSpans { /// the first macro inner attribute to invoke a proc-macro). /// When create a `TokenStream`, the inner attributes get inserted /// into the proper place in the token stream. -type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>); +type ReplaceRange = (Range<u32>, Option<AttrsTarget>); /// Controls how we capture tokens. Capturing can be expensive, /// so we try to avoid performing capturing in cases where @@ -1572,7 +1572,7 @@ impl<'a> Parser<'a> { self.expected_tokens.clear(); } - pub fn approx_token_stream_pos(&self) -> usize { + pub fn approx_token_stream_pos(&self) -> u32 { self.num_bump_calls } } @@ -1608,11 +1608,10 @@ enum FlatToken { /// A token - this holds both delimiter (e.g. '{' and '}') /// and non-delimiter tokens Token(Token), - /// Holds the `AttributesData` for an AST node. The - /// `AttributesData` is inserted directly into the - /// constructed `AttrTokenStream` as - /// an `AttrTokenTree::Attributes`. - AttrTarget(AttributesData), + /// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted + /// directly into the constructed `AttrTokenStream` as an + /// `AttrTokenTree::AttrsTarget`. + AttrsTarget(AttrsTarget), /// A special 'empty' token that is ignored during the conversion /// to an `AttrTokenStream`. This is used to simplify the /// handling of replace ranges. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index da8d1194325..03c647dd527 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -353,18 +353,17 @@ impl<'a> Parser<'a> { })?; let span = lo.to(self.prev_token.span); AngleBracketedArgs { args, span }.into() - } else if self.may_recover() - && self.token.kind == token::OpenDelim(Delimiter::Parenthesis) + } else if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) // FIXME(return_type_notation): Could also recover `...` here. && self.look_ahead(1, |tok| tok.kind == token::DotDot) { - self.bump(); - self.dcx() - .emit_err(errors::BadReturnTypeNotationDotDot { span: self.token.span }); - self.bump(); + self.bump(); // ( + self.bump(); // .. self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; let span = lo.to(self.prev_token.span); + self.psess.gated_spans.gate(sym::return_type_notation, span); + if self.eat_noexpect(&token::RArrow) { let lo = self.prev_token.span; let ty = self.parse_ty()?; @@ -372,13 +371,7 @@ impl<'a> Parser<'a> { .emit_err(errors::BadReturnTypeNotationOutput { span: lo.to(ty.span) }); } - ParenthesizedArgs { - span, - inputs: ThinVec::new(), - inputs_span: span, - output: ast::FnRetTy::Default(self.prev_token.span.shrink_to_hi()), - } - .into() + P(ast::GenericArgs::ParenthesizedElided(span)) } else { // `(T, U) -> R` @@ -733,14 +726,6 @@ impl<'a> Parser<'a> { let span = lo.to(self.prev_token.span); - if let AssocItemConstraintKind::Bound { .. } = kind - && let Some(ast::GenericArgs::Parenthesized(args)) = &gen_args - && args.inputs.is_empty() - && let ast::FnRetTy::Default(..) = args.output - { - self.psess.gated_spans.gate(sym::return_type_notation, span); - } - let constraint = AssocItemConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index d2043c353fe..1e5b227aaa9 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -18,7 +18,7 @@ use rustc_ast::{ }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use thin_vec::{thin_vec, ThinVec}; #[derive(Copy, Clone, PartialEq)] @@ -280,7 +280,7 @@ impl<'a> Parser<'a> { // Function pointer type or bound list (trait object type) starting with a poly-trait. // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` - let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?; if self.check_fn_front_matter(false, Case::Sensitive) { self.parse_ty_bare_fn( lo, @@ -833,12 +833,9 @@ impl<'a> Parser<'a> { let lo = self.token.span; let leading_token = self.prev_token.clone(); let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis)); - let inner_lo = self.token.span; - let modifiers = self.parse_trait_bound_modifiers()?; let bound = if self.token.is_lifetime() { - self.error_lt_bound_with_modifiers(modifiers); - self.parse_generic_lt_bound(lo, inner_lo, has_parens)? + self.parse_generic_lt_bound(lo, has_parens)? } else if self.eat_keyword(kw::Use) { // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of // lifetimes and ident params (including SelfUpper). These are validated later @@ -848,7 +845,7 @@ impl<'a> Parser<'a> { let (args, args_span) = self.parse_precise_capturing_args()?; GenericBound::Use(args, use_span.to(args_span)) } else { - self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)? + self.parse_generic_ty_bound(lo, has_parens, &leading_token)? }; Ok(bound) @@ -858,50 +855,64 @@ impl<'a> Parser<'a> { /// ```ebnf /// LT_BOUND = LIFETIME /// ``` - fn parse_generic_lt_bound( - &mut self, - lo: Span, - inner_lo: Span, - has_parens: bool, - ) -> PResult<'a, GenericBound> { - let bound = GenericBound::Outlives(self.expect_lifetime()); + fn parse_generic_lt_bound(&mut self, lo: Span, has_parens: bool) -> PResult<'a, GenericBound> { + let lt = self.expect_lifetime(); + let bound = GenericBound::Outlives(lt); if has_parens { // FIXME(Centril): Consider not erroring here and accepting `('lt)` instead, // possibly introducing `GenericBound::Paren(P<GenericBound>)`? - self.recover_paren_lifetime(lo, inner_lo)?; + self.recover_paren_lifetime(lo, lt.ident.span)?; } Ok(bound) } /// Emits an error if any trait bound modifiers were present. - fn error_lt_bound_with_modifiers(&self, modifiers: TraitBoundModifiers) { - match modifiers.constness { + fn error_lt_bound_with_modifiers( + &self, + modifiers: TraitBoundModifiers, + binder_span: Option<Span>, + ) -> ErrorGuaranteed { + let TraitBoundModifiers { constness, asyncness, polarity } = modifiers; + + match constness { BoundConstness::Never => {} BoundConstness::Always(span) | BoundConstness::Maybe(span) => { - self.dcx().emit_err(errors::ModifierLifetime { - span, - modifier: modifiers.constness.as_str(), - }); + return self + .dcx() + .emit_err(errors::ModifierLifetime { span, modifier: constness.as_str() }); } } - match modifiers.polarity { + match polarity { BoundPolarity::Positive => {} BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => { - self.dcx().emit_err(errors::ModifierLifetime { - span, - modifier: modifiers.polarity.as_str(), - }); + return self + .dcx() + .emit_err(errors::ModifierLifetime { span, modifier: polarity.as_str() }); + } + } + + match asyncness { + BoundAsyncness::Normal => {} + BoundAsyncness::Async(span) => { + return self + .dcx() + .emit_err(errors::ModifierLifetime { span, modifier: asyncness.as_str() }); } } + + if let Some(span) = binder_span { + return self.dcx().emit_err(errors::ModifierLifetime { span, modifier: "for<...>" }); + } + + unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?") } /// Recover on `('lifetime)` with `(` already eaten. - fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> { - let inner_span = inner_lo.to(self.prev_token.span); + fn recover_paren_lifetime(&mut self, lo: Span, lt_span: Span) -> PResult<'a, ()> { self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; let span = lo.to(self.prev_token.span); - let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) { + let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(lt_span) { (Some(span), snippet) } else { (None, String::new()) @@ -916,7 +927,7 @@ impl<'a> Parser<'a> { /// If no modifiers are present, this does not consume any tokens. /// /// ```ebnf - /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["?" | "!"] + /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"] /// ``` fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> { let constness = if self.eat(&token::Tilde) { @@ -970,15 +981,23 @@ impl<'a> Parser<'a> { /// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH /// ``` /// - /// For example, this grammar accepts `~const ?for<'a: 'b> m::Trait<'a>`. + /// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`. fn parse_generic_ty_bound( &mut self, lo: Span, has_parens: bool, - modifiers: TraitBoundModifiers, leading_token: &Token, ) -> PResult<'a, GenericBound> { - let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let modifiers = self.parse_trait_bound_modifiers()?; + let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?; + + // Recover erroneous lifetime bound with modifiers or binder. + // e.g. `T: for<'a> 'a` or `T: ~const 'a`. + if self.token.is_lifetime() { + let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span); + return self.parse_generic_lt_bound(lo, has_parens); + } + let mut path = if self.token.is_keyword(kw::Fn) && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis)) && let Some(path) = self.recover_path_from_fn() @@ -1094,16 +1113,19 @@ impl<'a> Parser<'a> { } /// Optionally parses `for<$generic_params>`. - pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, ThinVec<GenericParam>> { + pub(super) fn parse_late_bound_lifetime_defs( + &mut self, + ) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> { if self.eat_keyword(kw::For) { + let lo = self.token.span; self.expect_lt()?; let params = self.parse_generic_params()?; self.expect_gt()?; - // We rely on AST validation to rule out invalid cases: There must not be type - // parameters, and the lifetime parameters must not have bounds. - Ok(params) + // We rely on AST validation to rule out invalid cases: There must not be + // type or const parameters, and parameters must not have bounds. + Ok((params, Some(lo.to(self.prev_token.span)))) } else { - Ok(ThinVec::new()) + Ok((ThinVec::new(), None)) } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 5a560325ab9..d7513fbad63 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -782,6 +782,7 @@ passes_used_compiler_linker = passes_used_static = attribute must be applied to a `static` variable + .label = but this is a {$target} passes_useless_assignment = useless assignment of {$is_field_assign -> diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 0c3dd649997..839b96fb3de 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -61,7 +61,7 @@ fn unwrap_fn_abi<'tcx>( fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { let param_env = tcx.param_env(item_def_id); let args = GenericArgs::identity_for_item(tcx, item_def_id); - let instance = match Instance::resolve(tcx, param_env, item_def_id.into(), args) { + let instance = match Instance::try_resolve(tcx, param_env, item_def_id.into(), args) { Ok(Some(instance)) => instance, Ok(None) => { // Not sure what to do here, but `LayoutError::Unknown` seems reasonable? diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5f8e4a8b7a7..ce2fa83810f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -34,8 +34,8 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::collections::hash_map::Entry; @@ -274,7 +274,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } self.check_repr(attrs, span, target, item, hir_id); - self.check_used(attrs, target); + self.check_used(attrs, target, span); } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { @@ -1930,12 +1930,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_used(&self, attrs: &[Attribute], target: Target) { + fn check_used(&self, attrs: &[Attribute], target: Target, target_span: Span) { let mut used_linker_span = None; let mut used_compiler_span = None; for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { if target != Target::Static { - self.dcx().emit_err(errors::UsedStatic { span: attr.span }); + self.dcx().emit_err(errors::UsedStatic { + attr_span: attr.span, + span: target_span, + target: target.name(), + }); } let inner = attr.meta_item_list(); match inner.as_deref() { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bbd586386dd..55514883cb1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -102,7 +102,7 @@ fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { Publicness::new(true, true) } -/// Determine if a work from the worklist is coming from the a `#[allow]` +/// Determine if a work from the worklist is coming from a `#[allow]` /// or a `#[expect]` of `dead_code` #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] enum ComesFromAllowExpect { @@ -277,7 +277,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -297,7 +300,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => { self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); return; @@ -402,31 +408,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return false; } - // don't ignore impls for Enums and pub Structs whose methods don't have self receiver, - // cause external crate may call such methods to construct values of these types - if let Some(local_impl_of) = impl_of.as_local() - && let Some(local_def_id) = def_id.as_local() - && let Some(fn_sig) = - self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) - && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind - && let Res::Def(def_kind, did) = path.res - { - match def_kind { - // for example, #[derive(Default)] pub struct T(i32); - // external crate can call T::default() to construct T, - // so that don't ignore impl Default for pub Enum and Structs - DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => { - return false; - } - // don't ignore impl Default for Enums, - // cause we don't know which variant is constructed - DefKind::Enum => return false, - _ => (), - }; - } - if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { @@ -690,6 +671,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.handle_field_pattern_match(pat, res, fields); } PatKind::Path(ref qpath) => { + if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() { + self.check_def_id(adt.did()); + } let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_res(res); } @@ -845,7 +829,7 @@ fn check_item<'tcx>( // mark the method live if the self_ty is public, // or the method is public and may construct self if tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || may_construct_self) + && (ty_and_all_fields_are_public || (ty_is_public && may_construct_self)) { // if the impl item is public, // and the ty may be constructed or can be constructed in foreign crates, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 7734dba3670..a026ff3b13b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -551,7 +551,10 @@ pub struct ReprConflictingLint; #[diag(passes_used_static)] pub struct UsedStatic { #[primary_span] + pub attr_span: Span, + #[label] pub span: Span, + pub target: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 0ba61f8e8b4..0720efebf97 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -695,7 +695,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) { record_variants!( (self, g, g, Id::None, ast, GenericArgs, GenericArgs), - [AngleBracketed, Parenthesized] + [AngleBracketed, Parenthesized, ParenthesizedElided] ); ast_visit::walk_generic_args(self, g) } diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index c9a47650456..603e98cfa92 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::{infer::TyCtxtInferExt, traits}; use crate::errors::{ diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index da4435ebebe..dee8ba7e87d 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -30,7 +30,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Node; use rustc_middle::bug; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::privacy::{self, Level}; use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc}; use rustc_middle::query::Providers; @@ -178,7 +178,15 @@ impl<'tcx> ReachableContext<'tcx> { if !self.any_library { // If we are building an executable, only explicitly extern // types need to be exported. - if has_custom_linkage(self.tcx, search_item) { + let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() { + self.tcx.codegen_fn_attrs(search_item) + } else { + CodegenFnAttrs::EMPTY + }; + let is_extern = codegen_attrs.contains_extern_indicator(); + let std_internal = + codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + if is_extern || std_internal { self.reachable_symbols.insert(search_item); } } else { @@ -302,7 +310,7 @@ impl<'tcx> ReachableContext<'tcx> { GlobalAlloc::Static(def_id) => { self.propagate_item(Res::Def(self.tcx.def_kind(def_id), def_id)) } - GlobalAlloc::Function(instance) => { + GlobalAlloc::Function { instance, .. } => { // Manually visit to actually see the instance's `DefId`. Type visitors won't see it self.propagate_item(Res::Def( self.tcx.def_kind(instance.def_id()), diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index b6ae54010c2..b71853b871d 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -pulldown-cmark = { version = "0.9.6", default-features = false } +pulldown-cmark = { version = "0.11", features = ["html"], default-features = false } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 4e0f2792d97..92cf73870ff 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -321,8 +321,14 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { // The fields are not expanded yet. return; } - let def_ids = fields.iter().map(|field| self.r.local_def_id(field.id).to_def_id()); - self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids)); + let fields = fields + .iter() + .enumerate() + .map(|(i, field)| { + field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) + }) + .collect(); + self.r.field_names.insert(def_id, fields); } fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 1bca5602a4e..a4fdb4a0baf 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -8,6 +8,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_span::hygiene::LocalExpnId; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; +use std::mem; use tracing::debug; pub(crate) fn collect_definitions( @@ -15,8 +16,9 @@ pub(crate) fn collect_definitions( fragment: &AstFragment, expansion: LocalExpnId, ) { - let (parent_def, impl_trait_context) = resolver.invocation_parents[&expansion]; - fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion, impl_trait_context }); + let (parent_def, impl_trait_context, in_attr) = resolver.invocation_parents[&expansion]; + let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr }; + fragment.visit_with(&mut visitor); } /// Creates `DefId`s for nodes in the AST. @@ -24,6 +26,7 @@ struct DefCollector<'a, 'b, 'tcx> { resolver: &'a mut Resolver<'b, 'tcx>, parent_def: LocalDefId, impl_trait_context: ImplTraitContext, + in_attr: bool, expansion: LocalExpnId, } @@ -53,7 +56,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { } fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) { - let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def); + let orig_parent_def = mem::replace(&mut self.parent_def, parent_def); f(self); self.parent_def = orig_parent_def; } @@ -63,7 +66,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { impl_trait_context: ImplTraitContext, f: F, ) { - let orig_itc = std::mem::replace(&mut self.impl_trait_context, impl_trait_context); + let orig_itc = mem::replace(&mut self.impl_trait_context, impl_trait_context); f(self); self.impl_trait_context = orig_itc; } @@ -105,8 +108,10 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let old_parent = - self.resolver.invocation_parents.insert(id, (self.parent_def, self.impl_trait_context)); + let old_parent = self + .resolver + .invocation_parents + .insert(id, (self.parent_def, self.impl_trait_context, self.in_attr)); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } @@ -334,7 +339,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { None => closure_def, } } - ExprKind::Gen(_, _, _) => { + ExprKind::Gen(_, _, _, _) => { self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) } ExprKind::ConstBlock(ref constant) => { @@ -413,4 +418,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { visit::walk_crate(self, krate) } } + + fn visit_attribute(&mut self, attr: &'a Attribute) -> Self::Result { + let orig_in_attr = mem::replace(&mut self.in_attr, true); + visit::walk_attribute(self, attr); + self.in_attr = orig_in_attr; + } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 263daa11ec3..ffd495aa985 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1726,11 +1726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { )) = binding.kind { let def_id = self.tcx.parent(ctor_def_id); - return self - .field_def_ids(def_id)? - .iter() - .map(|&field_id| self.def_span(field_id)) - .reduce(Span::to); // None for `struct Foo()` + return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()` } None } @@ -1987,10 +1983,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { candidates .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); if let Some(candidate) = candidates.get(0) { + let path = { + // remove the possible common prefix of the path + let start_index = (0..failed_segment_idx) + .find(|&i| path[i].ident != candidate.path.segments[i].ident) + .unwrap_or_default(); + let segments = (start_index..=failed_segment_idx) + .map(|s| candidate.path.segments[s].clone()) + .collect(); + Path { segments, span: Span::default(), tokens: None } + }; ( String::from("unresolved import"), Some(( - vec![(ident.span, pprust::path_to_string(&candidate.path))], + vec![(ident.span, pprust::path_to_string(&path))], String::from("a similar path exists"), Applicability::MaybeIncorrect, )), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 66a1c05289b..1d37264f96a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1221,6 +1221,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, } } } + GenericArgs::ParenthesizedElided(_) => {} } } } @@ -1743,7 +1744,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ) { self.r.dcx().emit_err(errors::LendingIteratorReportError { lifetime: lifetime.ident.span, - ty: ty.span(), + ty: ty.span, }); } else { self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 764cc350182..941fb6436df 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1532,17 +1532,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if !this.has_private_fields(def_id) { // If the fields of the type are private, we shouldn't be suggesting using // the struct literal syntax at all, as that will cause a subsequent error. - let field_ids = this.r.field_def_ids(def_id); - let (fields, applicability) = match field_ids { - Some(field_ids) => { - let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id)); - + let fields = this.r.field_idents(def_id); + let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty()); + let (fields, applicability) = match fields { + Some(fields) => { let fields = if let Some(old_fields) = old_fields { fields + .iter() .enumerate() .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { - let new = new.to_ident_string(); + let new = new.name.to_ident_string(); if let Some(Some(old)) = old && new != *old { @@ -1553,17 +1553,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }) .collect::<Vec<String>>() } else { - fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>() + fields + .iter() + .map(|f| format!("{f}{tail}")) + .collect::<Vec<String>>() }; (fields.join(", "), applicability) } None => ("/* fields */".to_string(), Applicability::HasPlaceholders), }; - let pad = match field_ids { - Some([]) => "", - _ => " ", - }; + let pad = if has_fields { " " } else { "" }; err.span_suggestion( span, format!("use struct {descr} syntax instead"), @@ -1723,12 +1723,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { &args[..], ); // Use spans of the tuple struct definition. - self.r.field_def_ids(def_id).map(|field_ids| { - field_ids - .iter() - .map(|&field_id| self.r.def_span(field_id)) - .collect::<Vec<_>>() - }) + self.r + .field_idents(def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>()) } _ => None, }; @@ -1791,7 +1788,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => { let def_id = self.r.tcx.parent(ctor_def_id); err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here")); - let fields = self.r.field_def_ids(def_id).map_or_else( + let fields = self.r.field_idents(def_id).map_or_else( || "/* fields */".to_string(), |field_ids| vec!["_"; field_ids.len()].join(", "), ); @@ -2017,12 +2014,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = resolution.full_res() { - if let Some(field_ids) = self.r.field_def_ids(did) { - if let Some(field_id) = field_ids - .iter() - .find(|&&field_id| ident.name == self.r.tcx.item_name(field_id)) - { - return Some(AssocSuggestion::Field(self.r.def_span(*field_id))); + if let Some(fields) = self.r.field_idents(did) { + if let Some(field) = fields.iter().find(|id| ident.name == id.name) { + return Some(AssocSuggestion::Field(field.span)); } } } @@ -2418,7 +2412,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { match kind { CtorKind::Const => false, CtorKind::Fn => { - !self.r.field_def_ids(def_id).is_some_and(|field_ids| field_ids.is_empty()) + !self.r.field_idents(def_id).is_some_and(|field_ids| field_ids.is_empty()) } } }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 94cdce1025f..7cb5a3fb2fd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -350,6 +350,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment { (args.span, found_lifetimes) } GenericArgs::Parenthesized(args) => (args.span, true), + GenericArgs::ParenthesizedElided(span) => (*span, true), } } else { (DUMMY_SP, false) @@ -990,7 +991,7 @@ pub struct Resolver<'a, 'tcx> { extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'a>>, /// N.B., this is used only for better diagnostics, not name resolution itself. - field_def_ids: LocalDefIdMap<&'tcx [DefId]>, + field_names: LocalDefIdMap<Vec<Ident>>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. @@ -1138,7 +1139,7 @@ pub struct Resolver<'a, 'tcx> { /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId` /// we know what parent node that fragment should be attached to thanks to this table, /// and how the `impl Trait` fragments were introduced. - invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext)>, + invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext, bool /*in_attr*/)>, /// Some way to know that we are in a *trait* impl in `visit_assoc_item`. /// FIXME: Replace with a more general AST map (together with some other fields). @@ -1370,7 +1371,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id_to_def_id.insert(CRATE_NODE_ID, crate_feed); let mut invocation_parents = FxHashMap::default(); - invocation_parents.insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential)); + invocation_parents + .insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential, false)); let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = tcx .sess @@ -1405,7 +1407,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { prelude: None, extern_prelude, - field_def_ids: Default::default(), + field_names: Default::default(), field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), @@ -2126,10 +2128,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn field_def_ids(&self, def_id: DefId) -> Option<&'tcx [DefId]> { + fn field_idents(&self, def_id: DefId) -> Option<Vec<Ident>> { match def_id.as_local() { - Some(def_id) => self.field_def_ids.get(&def_id).copied(), - None => Some(self.tcx.associated_item_def_ids(def_id)), + Some(def_id) => self.field_names.get(&def_id).cloned(), + None => Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&def_id| { + Ident::new(self.tcx.item_name(def_id), self.tcx.def_span(def_id)) + }) + .collect(), + ), } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 026a2ca1412..cb9bebd33d3 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -297,11 +297,12 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { .invocation_parents .get(&invoc_id) .or_else(|| self.invocation_parents.get(&eager_expansion_root)) - .map(|&(mod_def_id, _)| mod_def_id) - .filter(|&mod_def_id| { - invoc.fragment_kind == AstFragmentKind::Expr + .filter(|&&(mod_def_id, _, in_attr)| { + in_attr + && invoc.fragment_kind == AstFragmentKind::Expr && self.tcx.def_kind(mod_def_id) == DefKind::Mod - }); + }) + .map(|&(mod_def_id, ..)| mod_def_id); let (ext, res) = self.smart_resolve_macro_path( path, kind, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 66b4981eb55..59460815321 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -1,4 +1,6 @@ -use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag}; +use pulldown_cmark::{ + BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag, +}; use rustc_ast as ast; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxHashMap; @@ -427,7 +429,9 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> { while let Some(event) = event_iter.next() { match event { - Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + Event::Start(Tag::Link { link_type, dest_url, title: _, id: _ }) + if may_be_doc_link(link_type) => + { if matches!( link_type, LinkType::Inline @@ -441,7 +445,7 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> { } } - links.push(preprocess_link(&dest)); + links.push(preprocess_link(&dest_url)); } _ => {} } @@ -451,8 +455,8 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> { } /// Collects additional data of link. -fn collect_link_data<'input, 'callback>( - event_iter: &mut Parser<'input, 'callback>, +fn collect_link_data<'input, F: BrokenLinkCallback<'input>>( + event_iter: &mut Parser<'input, F>, ) -> Option<Box<str>> { let mut display_text: Option<String> = None; let mut append_text = |text: CowStr<'_>| { diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 1dcb69920d7..d27dfd88824 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -155,7 +155,7 @@ impl FileEncoder { if std::intrinsics::unlikely(self.buffered > flush_threshold) { self.flush(); } - // SAFETY: We checked above that that N < self.buffer_empty().len(), + // SAFETY: We checked above that N < self.buffer_empty().len(), // and if isn't, flush ensures that our empty buffer is now BUF_SIZE. // We produce a post-mono error if N > BUF_SIZE. let buf = unsafe { self.buffer_empty().first_chunk_mut::<N>().unwrap_unchecked() }; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2d38ad37133..41c99f7edee 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2965,8 +2965,9 @@ pub(crate) mod dep_tracking { CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, - Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, - SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3073,6 +3074,7 @@ pub(crate) mod dep_tracking { OomStrategy, LanguageIdentifier, NextSolverConfig, + PatchableFunctionEntry, Polonius, InliningThreshold, FunctionReturn, @@ -3250,6 +3252,35 @@ impl DumpMonoStatsFormat { } } +/// `-Z patchable-function-entry` representation - how many nops to put before and after function +/// entry. +#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +pub struct PatchableFunctionEntry { + /// Nops before the entry + prefix: u8, + /// Nops after the entry + entry: u8, +} + +impl PatchableFunctionEntry { + pub fn from_total_and_prefix_nops( + total_nops: u8, + prefix_nops: u8, + ) -> Option<PatchableFunctionEntry> { + if total_nops < prefix_nops { + None + } else { + Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) + } + } + pub fn prefix(&self) -> u8 { + self.prefix + } + pub fn entry(&self) -> u8 { + self.entry + } +} + /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, /// or future prototype. #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9a10adeb6d1..7421cae65e4 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -379,6 +379,7 @@ mod desc { pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; + pub const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_oom_strategy: &str = "either `panic` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; @@ -734,6 +735,32 @@ mod parse { true } + pub(crate) fn parse_patchable_function_entry( + slot: &mut PatchableFunctionEntry, + v: Option<&str>, + ) -> bool { + let mut total_nops = 0; + let mut prefix_nops = 0; + + if !parse_number(&mut total_nops, v) { + let parts = v.and_then(|v| v.split_once(',')).unzip(); + if !parse_number(&mut total_nops, parts.0) { + return false; + } + if !parse_number(&mut prefix_nops, parts.1) { + return false; + } + } + + if let Some(pfe) = + PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops) + { + *slot = pfe; + return true; + } + false + } + pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool { match v { Some("panic") => *slot = OomStrategy::Panic, @@ -1603,8 +1630,6 @@ options! { "only allow the listed language features to be enabled in code (comma separated)"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata (default: no)"), - asm_comments: bool = (false, parse_bool, [TRACKED], - "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), @@ -1739,6 +1764,8 @@ options! { "enable LLVM inlining (default: yes)"), inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable MIR inlining (default: no)"), + inline_mir_forwarder_threshold: Option<usize> = (None, parse_opt_number, [TRACKED], + "inlining threshold when the caller is a simple forwarding function (default: 30)"), inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED], "inlining threshold for functions with inline hint (default: 100)"), inline_mir_preserve_debug: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1859,6 +1886,8 @@ options! { "panic strategy for panics in drops"), parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link (default: no)"), + patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED], + "nop padding at function entry"), plt: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries @@ -2076,6 +2105,8 @@ written to standard error output)"), "Generate sync unwind tables instead of async unwind tables (default: no)"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + verbose_asm: bool = (false, parse_bool, [TRACKED], + "add descriptive comments from LLVM to the assembly (may change behavior) (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")] verbose_internals: bool = (false, parse_bool, [TRACKED_NO_CRATE_HASH], "in general, enable more debug printouts (default: no)"), diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 1e0a60bc371..1230667ee91 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index dde5e30c3d0..b0ced8e920f 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -160,7 +160,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; - let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id); + let GenericPredicates { parent, predicates, effects_min_tys: _ } = + tables.tcx.predicates_of(def_id); stable_mir::ty::GenericPredicates { parent: parent.map(|did| tables.trait_def(did)), predicates: predicates @@ -181,7 +182,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ) -> stable_mir::ty::GenericPredicates { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; - let GenericPredicates { parent, predicates } = tables.tcx.explicit_predicates_of(def_id); + let GenericPredicates { parent, predicates, effects_min_tys: _ } = + tables.tcx.explicit_predicates_of(def_id); stable_mir::ty::GenericPredicates { parent: parent.map(|did| tables.trait_def(did)), predicates: predicates @@ -228,6 +230,46 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } + fn get_attrs_by_path( + &self, + def_id: stable_mir::DefId, + attr: &[stable_mir::Symbol], + ) -> Vec<stable_mir::crate_def::Attribute> { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let did = tables[def_id]; + let attr_name: Vec<_> = + attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect(); + tcx.get_attrs_by_path(did, &attr_name) + .map(|attribute| { + let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let span = attribute.span; + stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) + }) + .collect() + } + + fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let did = tables[def_id]; + let filter_fn = move |a: &&rustc_ast::ast::Attribute| { + matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_)) + }; + let attrs_iter = if let Some(did) = did.as_local() { + tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + } else { + tcx.item_attrs(did).iter().filter(filter_fn) + }; + attrs_iter + .map(|attribute| { + let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); + let span = attribute.span; + stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) + }) + .collect() + } + fn span_to_string(&self, span: stable_mir::ty::Span) -> String { let tables = self.0.borrow(); tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span]) @@ -587,7 +629,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); let args_ref = args.internal(&mut *tables, tcx); - match Instance::resolve(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) { + match Instance::try_resolve(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) { Ok(Some(instance)) => Some(instance.stable(&mut *tables)), Ok(None) | Err(_) => None, } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index f15b82d0c03..8bfaa112c2f 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -644,6 +644,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { target: target.map(|t| t.as_usize()), unwind: unwind.stable(tables), }, + mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(), mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => { TerminatorKind::Assert { cond: cond.stable(tables), @@ -709,7 +710,7 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { match self { - mir::interpret::GlobalAlloc::Function(instance) => { + mir::interpret::GlobalAlloc::Function { instance, .. } => { GlobalAlloc::Function(instance.stable(tables)) } mir::interpret::GlobalAlloc::VTable(ty, trait_ref) => { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 74c3fcc9b9e..9382460d6d4 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -600,7 +600,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind { ty::GenericParamDefKind::Type { has_default, synthetic } => { GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic } } - ty::GenericParamDefKind::Const { has_default, is_host_effect: _ } => { + ty::GenericParamDefKind::Const { has_default, is_host_effect: _, synthetic: _ } => { GenericParamDefKind::Const { has_default: *has_default } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6d4a8c29bc9..af56f4e5141 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -194,6 +194,14 @@ symbols! { Display, DoubleEndedIterator, Duration, + EffectsCompat, + EffectsIntersection, + EffectsIntersectionOutput, + EffectsMaybe, + EffectsNoRuntime, + EffectsRuntime, + EffectsTyCompat, + Effects__, Encodable, Encoder, Enumerate, @@ -768,6 +776,7 @@ symbols! { enable, encode, end, + entry_nops, enumerate_method, env, env_CFG_RELEASE: env!("CFG_RELEASE"), @@ -1383,6 +1392,7 @@ symbols! { passes, pat, pat_param, + patchable_function_entry, path, pattern_complexity, pattern_parentheses, @@ -1421,6 +1431,7 @@ symbols! { prefetch_read_instruction, prefetch_write_data, prefetch_write_instruction, + prefix_nops, preg, prelude, prelude_import, @@ -1502,6 +1513,7 @@ symbols! { recursion_limit, reexport_test_harness_main, ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, ref_pat_everywhere, ref_unwind_safe_trait, reference, @@ -1603,6 +1615,7 @@ symbols! { rustc_do_not_const_check, rustc_doc_primitive, rustc_dummy, + rustc_dump_def_parents, rustc_dump_item_bounds, rustc_dump_predicates, rustc_dump_user_args, @@ -1655,6 +1668,7 @@ symbols! { rustc_reallocator, rustc_regions, rustc_reservation_impl, + rustc_runtime, rustc_safe_intrinsic, rustc_serialize, rustc_skip_during_method_dispatch, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 9edd2ff9b1a..5aa46cc0dea 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -85,9 +85,13 @@ pub(super) fn mangle<'tcx>( } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. - ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::CoroutineKindShim { .. } => { - printer.write_str("{{fn-once-shim}}").unwrap(); + ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => { + printer + .write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" }) + .unwrap(); + } + ty::InstanceKind::CoroutineKindShim { .. } => { + printer.write_str("{{by-move-body-shim}}").unwrap(); } _ => {} } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 42c4fa83d1b..5f8029af020 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -49,8 +49,15 @@ pub(super) fn mangle<'tcx>( ty::InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), ty::InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), - ty::InstanceKind::ConstructCoroutineInClosureShim { .. } - | ty::InstanceKind::CoroutineKindShim { .. } => Some("fn_once"), + // FIXME(async_closures): This shouldn't be needed when we fix + // `Instance::ty`/`Instance::def_id`. + ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: true, .. } => { + Some("by_move") + } + ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => { + Some("by_ref") + } + ty::InstanceKind::CoroutineKindShim { .. } => Some("by_move_body"), _ => None, }; diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 5713542c17d..8058130f441 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -339,7 +339,9 @@ impl CastTarget { } } - pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size { + /// When you only access the range containing valid data, you can use this unaligned size; + /// otherwise, use the safer `size` method. + pub fn unaligned_size<C: HasDataLayout>(&self, _cx: &C) -> Size { // Prefix arguments are passed in specific designated registers let prefix_size = self .prefix @@ -353,6 +355,10 @@ impl CastTarget { prefix_size + rest_size } + pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size { + self.unaligned_size(cx).align_to(self.align(cx)) + } + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { self.prefix .iter() diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 1f4fb57d996..f023a0eb53a 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,8 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -bitflags = "2.4.1" -derivative = "2.2.0" itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } @@ -15,7 +13,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } -rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } @@ -28,7 +25,6 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } rustc_type_ir = { path = "../rustc_type_ir" } -rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs new file mode 100644 index 00000000000..f6ac8fc7b61 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs @@ -0,0 +1 @@ +pub mod traits; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs new file mode 100644 index 00000000000..c301deac616 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -0,0 +1,698 @@ +use std::ops::ControlFlow; + +use rustc_errors::{ + struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, E0283, E0284, E0790, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor as _; +use rustc_hir::LangItem; +use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt}; +use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; +use rustc_infer::traits::util::elaborate; +use rustc_infer::traits::{ + Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, +}; +use rustc_macros::extension; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; + +use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _; +use crate::error_reporting::traits::{ + to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _, +}; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::ObligationCtxt; + +#[derive(Debug)] +pub enum CandidateSource { + DefId(DefId), + ParamEnv(Span), +} + +pub fn compute_applicable_impls_for_diagnostics<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &PolyTraitObligation<'tcx>, +) -> Vec<CandidateSource> { + let tcx = infcx.tcx; + let param_env = obligation.param_env; + + let predicate_polarity = obligation.predicate.skip_binder().polarity; + + let impl_may_apply = |impl_def_id| { + let ocx = ObligationCtxt::new(infcx); + infcx.enter_forall(obligation.predicate, |placeholder_obligation| { + let obligation_trait_ref = ocx.normalize( + &ObligationCause::dummy(), + param_env, + placeholder_obligation.trait_ref, + ); + + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = + tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args); + let impl_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); + + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref) + { + return false; + } + + let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); + let impl_polarity = impl_trait_header.polarity; + + match (impl_polarity, predicate_polarity) { + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {} + _ => return false, + } + + let obligations = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .into_iter() + .map(|(predicate, _)| { + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + }) + // Kinda hacky, but let's just throw away obligations that overflow. + // This may reduce the accuracy of this check (if the obligation guides + // inference or it actually resulted in error after others are processed) + // ... but this is diagnostics code. + .filter(|obligation| { + infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok() + }); + ocx.register_obligations(obligations); + + ocx.select_where_possible().is_empty() + }) + }; + + let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { + let ocx = ObligationCtxt::new(infcx); + infcx.enter_forall(obligation.predicate, |placeholder_obligation| { + let obligation_trait_ref = ocx.normalize( + &ObligationCause::dummy(), + param_env, + placeholder_obligation.trait_ref, + ); + + let param_env_predicate = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + poly_trait_predicate, + ); + let param_env_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref); + + if let Err(_) = ocx.eq( + &ObligationCause::dummy(), + param_env, + obligation_trait_ref, + param_env_trait_ref, + ) { + return false; + } + + ocx.select_where_possible().is_empty() + }) + }; + + let mut ambiguities = Vec::new(); + + tcx.for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + if infcx.probe(|_| impl_may_apply(impl_def_id)) { + ambiguities.push(CandidateSource::DefId(impl_def_id)) + } + }, + ); + + let predicates = + tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); + for (pred, span) in elaborate(tcx, predicates.into_iter()) { + let kind = pred.kind(); + if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() + && param_env_candidate_may_apply(kind.rebind(trait_pred)) + { + if kind.rebind(trait_pred.trait_ref) + == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) + { + ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(CandidateSource::ParamEnv(span)) + } + } + } + + ambiguities +} + +#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + #[instrument(skip(self), level = "debug")] + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(obligation.predicate); + let span = obligation.cause.span; + + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); + + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. + + let bound_predicate = predicate.kind(); + let mut err = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + let trait_ref = bound_predicate.rebind(data.trait_ref); + debug!(?trait_ref); + + if let Err(e) = predicate.error_reported() { + return e; + } + + if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) { + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case + // other `Foo` impls are incoherent. + return guar; + } + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like "<generic + // #0> doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { + match self.tainted_by_errors() { + None => { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + trait_ref.self_ty().skip_binder().into(), + TypeAnnotationNeeded::E0282, + false, + ); + return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); + } + Some(e) => return e, + } + } + + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // Pick the first generic parameter that still contains inference variables as the one + // we're going to emit an error for. If there are none (see above), fall back to + // a more general error. + let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + + let mut err = if let Some(arg) = arg { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0283, + true, + ) + } else { + struct_span_code_err!( + self.dcx(), + span, + E0283, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + }; + + let mut ambiguities = compute_applicable_impls_for_diagnostics( + self.infcx, + &obligation.with(self.tcx, trait_ref), + ); + let has_non_region_infer = + trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); + // It doesn't make sense to talk about applicable impls if there are more than a + // handful of them. If there are a lot of them, but only a few of them have no type + // params, we only show those, as they are more likely to be useful/intended. + if ambiguities.len() > 5 { + let infcx = self.infcx; + if !ambiguities.iter().all(|option| match option { + CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0, + CandidateSource::ParamEnv(_) => true, + }) { + // If not all are blanket impls, we filter blanked impls out. + ambiguities.retain(|option| match option { + CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0, + CandidateSource::ParamEnv(_) => true, + }); + } + } + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if let Some(e) = self.tainted_by_errors() + && arg.is_none() + { + // If `arg.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return e; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); + } else { + if let Some(e) = self.tainted_by_errors() { + err.cancel(); + return e; + } + err.note(format!("cannot satisfy `{predicate}`")); + let impl_candidates = + self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); + if impl_candidates.len() < 40 { + self.report_similar_impl_candidates( + impl_candidates.as_slice(), + trait_ref, + obligation.cause.body_id, + &mut err, + false, + obligation.param_env, + ); + } + } + + if let ObligationCauseCode::WhereClause(def_id, _) + | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code() + { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } + + if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) + && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(span, self.tcx); + expr_finder.visit_expr(&body.value); + + if let Some(hir::Expr { + kind: + hir::ExprKind::Call( + hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }, + _, + ) + | hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = expr_finder.result + && let [ + .., + trait_path_segment @ hir::PathSegment { + res: Res::Def(DefKind::Trait, trait_id), + .. + }, + hir::PathSegment { + ident: assoc_item_name, + res: Res::Def(_, item_id), + .. + }, + ] = path.segments + && data.trait_ref.def_id == *trait_id + && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && let None = self.tainted_by_errors() + { + let (verb, noun) = match self.tcx.associated_item(item_id).kind { + ty::AssocKind::Const => ("refer to the", "constant"), + ty::AssocKind::Fn => ("call", "function"), + // This is already covered by E0223, but this following single match + // arm doesn't hurt here. + ty::AssocKind::Type => ("refer to the", "type"), + }; + + // Replace the more general E0283 with a more specific error + err.cancel(); + err = self.dcx().struct_span_err( + span, + format!( + "cannot {verb} associated {noun} on trait without specifying the \ + corresponding `impl` type", + ), + ); + err.code(E0790); + + if let Some(local_def_id) = data.trait_ref.def_id.as_local() + && let hir::Node::Item(hir::Item { + ident: trait_name, + kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), + .. + }) = self.tcx.hir_node_by_def_id(local_def_id) + && let Some(method_ref) = trait_item_refs + .iter() + .find(|item_ref| item_ref.ident == *assoc_item_name) + { + err.span_label( + method_ref.span, + format!("`{trait_name}::{assoc_item_name}` defined here"), + ); + } + + err.span_label(span, format!("cannot {verb} associated {noun} of trait")); + + let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); + + if let Some(impl_def_id) = + trait_impls.non_blanket_impls().values().flatten().next() + { + let non_blanket_impl_count = + trait_impls.non_blanket_impls().values().flatten().count(); + // If there is only one implementation of the trait, suggest using it. + // Otherwise, use a placeholder comment for the implementation. + let (message, self_type) = if non_blanket_impl_count == 1 { + ( + "use the fully-qualified path to the only available \ + implementation", + format!( + "{}", + self.tcx.type_of(impl_def_id).instantiate_identity() + ), + ) + } else { + ( + "use a fully-qualified path to a specific available \ + implementation", + "/* self type */".to_string(), + ) + }; + let mut suggestions = + vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = + trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions + .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + err.multipart_suggestion( + message, + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + }; + + err + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + + if let Err(e) = arg.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0282, + false, + ) + } + + ty::PredicateKind::Subtype(data) => { + if let Err(e) = data.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + let ty::SubtypePredicate { a_is_expected: _, a, b } = data; + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + TypeAnnotationNeeded::E0282, + true, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + if let Err(e) = predicate.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + + if let Err(guar) = + self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) + { + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case + // other `Foo` impls are incoherent. + return guar; + } + let arg = data + .projection_term + .args + .iter() + .chain(Some(data.term.into_arg())) + .find(|g| g.has_non_region_infer()); + if let Some(arg) = arg { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0284, + true, + ) + .with_note(format!("cannot satisfy `{predicate}`")) + } else { + // If we can't find a generic parameter, just print a generic error + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { + if let Err(e) = predicate.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + let arg = data.walk().find(|g| g.is_non_region_infer()); + if let Some(arg) = arg { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0284, + true, + ); + err + } else { + // If we can't find a generic parameter, just print a generic error + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self + .emit_inference_failure_err( + obligation.cause.body_id, + span, + ct.into(), + TypeAnnotationNeeded::E0284, + true, + ), + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) + if term.is_infer() => + { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot normalize `{alias}`", + ) + .with_span_label(span, format!("cannot normalize `{alias}`")) + } + + _ => { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit() + } + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diag<'_>, + ambiguities: &[CandidateSource], + predicate: ty::Predicate<'tcx>, + ) { + let mut spans = vec![]; + let mut crates = vec![]; + let mut post = vec![]; + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + CandidateSource::DefId(impl_def_id) => match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } + }, + CandidateSource::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } + } + } + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.tainted_by_errors().is_some() + && (crate_names.len() == 1 + && spans.len() == 0 + && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) + || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + // NOTE(eddyb) this was `.cancel()`, but `err` + // is borrowed, so we can't fully defuse it. + err.downgrade_to_delayed_bug(); + return; + } + + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); + let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) + } else if post.len() == 1 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(format!("cannot satisfy `{predicate}`")); + } + (0, _, 1) => { + err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } +} + +struct HasNumericInferVisitor; + +impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d3096cf4b52..f7ec5f1ff32 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,34 +1,30 @@ -// ignore-tidy-filelength :( - use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; +use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; use crate::errors::{ AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, }; -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::error_reporting::TyCategory; use crate::infer::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; -use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; -use crate::traits::error_reporting::{ambiguity, ambiguity::CandidateSource::*}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::specialize::to_pretty_impl_header; use crate::traits::NormalizeExt; use crate::traits::{ - elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, - ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, - SelectionError, SignatureMismatch, TraitNotObjectSafe, + elaborate, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, + TraitNotObjectSafe, }; use core::ops::ControlFlow; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; -use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey}; -use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart}; +use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; +use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; use rustc_hir::{self as hir, LangItem}; -use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_macros::extension; @@ -42,323 +38,21 @@ use rustc_middle::ty::print::{ PrintTraitRefExt as _, }; use rustc_middle::ty::{ - self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, - TypeVisitableExt, Upcast, + self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; -use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; +use rustc_span::{BytePos, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; -use std::fmt; -use std::iter; use super::{ - ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason, - HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, + ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, }; pub use rustc_infer::traits::error_reporting::*; -pub enum OverflowCause<'tcx> { - DeeplyNormalize(ty::AliasTerm<'tcx>), - TraitSolver(ty::Predicate<'tcx>), -} - -pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_, G>, -) { - let suggested_limit = match tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - err.help(format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", - suggested_limit, - tcx.crate_name(LOCAL_CRATE), - )); -} - -#[extension(pub trait TypeErrCtxtExt<'tcx>)] -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - fn report_fulfillment_errors( - &self, - mut errors: Vec<FulfillmentError<'tcx>>, - ) -> ErrorGuaranteed { - self.sub_relations - .borrow_mut() - .add_constraints(self, errors.iter().map(|e| e.obligation.predicate)); - - #[derive(Debug)] - struct ErrorDescriptor<'tcx> { - predicate: ty::Predicate<'tcx>, - index: Option<usize>, // None if this is an old error - } - - let mut error_map: FxIndexMap<_, Vec<_>> = self - .reported_trait_errors - .borrow() - .iter() - .map(|(&span, predicates)| { - ( - span, - predicates - .0 - .iter() - .map(|&predicate| ErrorDescriptor { predicate, index: None }) - .collect(), - ) - }) - .collect(); - - // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics - // with more relevant type information and hide redundant E0282 errors. - errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) - if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) => - { - 1 - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, - ty::PredicateKind::Coerce(_) => 2, - _ => 0, - }); - - for (index, error) in errors.iter().enumerate() { - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - - error_map.entry(span).or_default().push(ErrorDescriptor { - predicate: error.obligation.predicate, - index: Some(index), - }); - } - - // We do this in 2 passes because we want to display errors in order, though - // maybe it *is* better to sort errors by span or something. - let mut is_suppressed = vec![false; errors.len()]; - for (_, error_set) in error_map.iter() { - // We want to suppress "duplicate" errors with the same span. - for error in error_set { - if let Some(index) = error.index { - // Suppress errors that are either: - // 1) strictly implied by another error. - // 2) implied by an error with a smaller index. - for error2 in error_set { - if error2.index.is_some_and(|index2| is_suppressed[index2]) { - // Avoid errors being suppressed by already-suppressed - // errors, to prevent all errors from being suppressed - // at once. - continue; - } - - if self.error_implies(error2.predicate, error.predicate) - && !(error2.index >= error.index - && self.error_implies(error.predicate, error2.predicate)) - { - info!("skipping {:?} (implied by {:?})", error, error2); - is_suppressed[index] = true; - break; - } - } - } - } - } - - let mut reported = None; - - for from_expansion in [false, true] { - for (error, suppressed) in iter::zip(&errors, &is_suppressed) { - if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { - let guar = self.report_fulfillment_error(error); - self.infcx.set_tainted_by_errors(guar); - reported = Some(guar); - // We want to ignore desugarings here: spans are equivalent even - // if one is the result of a desugaring and the other is not. - let mut span = error.obligation.cause.span; - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(_) = expn_data.kind { - span = expn_data.call_site; - } - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_insert_with(|| (vec![], guar)) - .0 - .push(error.obligation.predicate); - } - } - } - - // It could be that we don't report an error because we have seen an `ErrorReported` from - // another source. We should probably be able to fix most of these, but some are delayed - // bugs that get a proper error after this function. - reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors")) - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - fn report_overflow_error( - &self, - cause: OverflowCause<'tcx>, - span: Span, - suggest_increasing_limit: bool, - mutate: impl FnOnce(&mut Diag<'_>), - ) -> ! { - let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit); - mutate(&mut err); - err.emit(); - FatalError.raise(); - } - - fn build_overflow_error( - &self, - cause: OverflowCause<'tcx>, - span: Span, - suggest_increasing_limit: bool, - ) -> Diag<'tcx> { - fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String - where - T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, - { - let s = value.to_string(); - if s.len() > 50 { - // We don't need to save the type to a file, we will be talking about this type already - // in a separate note when we explain the obligation, so it will be available that way. - let mut cx: FmtPrinter<'_, '_> = - FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6)); - value.print(&mut cx).unwrap(); - cx.into_buffer() - } else { - s - } - } - - let mut err = match cause { - OverflowCause::DeeplyNormalize(alias_term) => { - let alias_term = self.resolve_vars_if_possible(alias_term); - let kind = alias_term.kind(self.tcx).descr(); - let alias_str = with_short_path(self.tcx, alias_term); - struct_span_code_err!( - self.dcx(), - span, - E0275, - "overflow normalizing the {kind} `{alias_str}`", - ) - } - OverflowCause::TraitSolver(predicate) => { - let predicate = self.resolve_vars_if_possible(predicate); - match predicate.kind().skip_binder() { - ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) - | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { - struct_span_code_err!( - self.dcx(), - span, - E0275, - "overflow assigning `{a}` to `{b}`", - ) - } - _ => { - let pred_str = with_short_path(self.tcx, predicate); - struct_span_code_err!( - self.dcx(), - span, - E0275, - "overflow evaluating the requirement `{pred_str}`", - ) - } - } - } - }; - - if suggest_increasing_limit { - suggest_new_overflow_limit(self.tcx, &mut err); - } - - err - } - - /// Reports that an overflow has occurred and halts compilation. We - /// halt compilation unconditionally because it is important that - /// overflows never be masked -- they basically represent computations - /// whose result could not be truly determined and thus we can't say - /// if the program type checks or not -- and they are unusual - /// occurrences in any case. - fn report_overflow_obligation<T>( - &self, - obligation: &Obligation<'tcx, T>, - suggest_increasing_limit: bool, - ) -> ! - where - T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone, - { - let predicate = obligation.predicate.clone().upcast(self.tcx); - let predicate = self.resolve_vars_if_possible(predicate); - self.report_overflow_error( - OverflowCause::TraitSolver(predicate), - obligation.cause.span, - suggest_increasing_limit, - |err| { - self.note_obligation_cause_code( - obligation.cause.body_id, - err, - predicate, - obligation.param_env, - obligation.cause.code(), - &mut vec![], - &mut Default::default(), - ); - }, - ); - } - - /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_obligation` except - /// that we can give a more helpful error message (and, in particular, - /// we do not suggest increasing the overflow limit, which is not - /// going to help). - fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { - let cycle = self.resolve_vars_if_possible(cycle.to_owned()); - assert!(!cycle.is_empty()); - - debug!(?cycle, "report_overflow_error_cycle"); - - // The 'deepest' obligation is most likely to have a useful - // cause 'backtrace' - self.report_overflow_obligation( - cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), - false, - ); - } - - fn report_overflow_no_abort( - &self, - obligation: PredicateObligation<'tcx>, - suggest_increasing_limit: bool, - ) -> ErrorGuaranteed { - let obligation = self.resolve_vars_if_possible(obligation); - let mut err = self.build_overflow_error( - OverflowCause::TraitSolver(obligation.predicate), - obligation.cause.span, - suggest_increasing_limit, - ); - self.note_obligation_cause(&mut err, &obligation); - self.point_at_returns_when_relevant(&mut err, &obligation); - err.emit() - } - +#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. @@ -986,7 +680,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.point_at_returns_when_relevant(&mut err, &obligation); err.emit() } +} +#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool { let mut base_cause = obligation.cause.code().clone(); let mut applied_do_not_recommend = false; @@ -1073,7 +770,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // mismatched, then we have a totally different error to report. if self.enter_forall(found_args, |found_args| { self.enter_forall(expected_args, |expected_args| { - !self.can_sub(obligation.param_env, expected_args, found_args) + !self.can_eq(obligation.param_env, expected_args, found_args) }) }) { return None; @@ -1101,7 +798,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let ty::FnPtr(sig) = by_ref_captures.kind() && !sig.skip_binder().output().is_unit() { - let mut err = self.tcx.dcx().create_err(AsyncClosureNotFn { + let mut err = self.dcx().create_err(AsyncClosureNotFn { span: self.tcx.def_span(closure_def_id), kind: expected_kind.as_str(), }); @@ -1134,7 +831,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`, - /// identify thoe method chain sub-expressions that could or could not have been annotated + /// identify those method chain sub-expressions that could or could not have been annotated /// with `?`. fn try_conversion_context( &self, @@ -1296,7 +993,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expr = binding_expr; } if let hir::Node::Param(_param) = parent { - // ...and it is a an fn argument. + // ...and it is an fn argument. break; } } @@ -1351,7 +1048,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, ty: Ty<'tcx>, obligation: &PredicateObligation<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let span = obligation.cause.span; let mut diag = match ty.kind() { @@ -1445,8 +1142,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } -#[extension(pub(super) trait InferCtxtPrivExt<'tcx>)] -impl<'tcx> TypeErrCtxt<'_, 'tcx> { +#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn can_match_trait( &self, goal: ty::TraitPredicate<'tcx>, @@ -1507,72 +1204,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - #[instrument(skip(self), level = "debug")] - fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed { - let mut error = FulfillmentError { - obligation: error.obligation.clone(), - code: error.code.clone(), - root_obligation: error.root_obligation.clone(), - }; - if matches!( - error.code, - FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented) - | FulfillmentErrorCode::Project(_) - ) && self.apply_do_not_recommend(&mut error.obligation) - { - error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented); - } - - match error.code { - FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error( - error.obligation.clone(), - &error.root_obligation, - selection_error, - ), - FulfillmentErrorCode::Project(ref e) => { - self.report_projection_error(&error.obligation, e) - } - FulfillmentErrorCode::Ambiguity { overflow: None } => { - self.maybe_report_ambiguity(&error.obligation) - } - FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { - self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit) - } - FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self - .report_mismatched_types( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - *err, - ) - .emit(), - FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => { - let mut diag = self.report_mismatched_consts( - &error.obligation.cause, - expected_found.expected, - expected_found.found, - *err, - ); - let code = error.obligation.cause.code().peel_derives().peel_match_impls(); - if let ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) = code - { - self.note_obligation_cause_code( - error.obligation.cause.body_id, - &mut diag, - error.obligation.predicate, - error.obligation.param_env, - code, - &mut vec![], - &mut Default::default(), - ); - } - diag.emit() - } - FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle), - } - } - #[instrument(level = "debug", skip_all)] fn report_projection_error( &self, @@ -1586,60 +1217,113 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } self.probe(|_| { - let ocx = ObligationCtxt::new(self); - // try to find the mismatched types to report the error with. // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. let bound_predicate = predicate.kind(); - let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) = - bound_predicate.skip_binder() - { - let data = self.instantiate_binder_with_fresh_vars( - obligation.cause.span, - infer::BoundRegionConversionTime::HigherRankedType, - bound_predicate.rebind(data), - ); - let unnormalized_term = data.projection_term.to_term(self.tcx); - // FIXME(-Znext-solver): For diagnostic purposes, it would be nice - // to deeply normalize this type. - let normalized_term = - ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - - debug!(?obligation.cause, ?obligation.param_env); - - debug!(?normalized_term, data.ty = ?data.term); + let (values, err) = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + let ocx = ObligationCtxt::new(self); + + let data = self.instantiate_binder_with_fresh_vars( + obligation.cause.span, + infer::BoundRegionConversionTime::HigherRankedType, + bound_predicate.rebind(data), + ); + let unnormalized_term = data.projection_term.to_term(self.tcx); + // FIXME(-Znext-solver): For diagnostic purposes, it would be nice + // to deeply normalize this type. + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); + + let is_normalized_term_expected = !matches!( + obligation.cause.code().peel_derives(), + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + | ObligationCauseCode::Coercion { .. } + ); - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - |ObligationCauseCode::WhereClause(..)| ObligationCauseCode::WhereClauseInExpr( - .. - ) | ObligationCauseCode::Coercion { .. } - ); + let (expected, actual) = if is_normalized_term_expected { + (normalized_term, data.term) + } else { + (data.term, normalized_term) + }; - let (expected, actual) = if is_normalized_term_expected { - (normalized_term, data.term) - } else { - (data.term, normalized_term) - }; + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + // + // we intentionally drop errors from normalization here, + // since the normalization is just done to improve the error message. + let _ = ocx.select_where_possible(); - // constrain inference variables a bit more to nested obligations from normalize so - // we can have more helpful errors. - // - // we intentionally drop errors from normalization here, - // since the normalization is just done to improve the error message. - let _ = ocx.select_where_possible(); + if let Err(new_err) = + ocx.eq(&obligation.cause, obligation.param_env, expected, actual) + { + ( + Some(( + data.projection_term, + is_normalized_term_expected, + self.resolve_vars_if_possible(normalized_term), + data.term, + )), + new_err, + ) + } else { + (None, error.err) + } + } + ty::PredicateKind::AliasRelate(lhs, rhs, _) => { + let derive_better_type_error = + |alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| { + let ocx = ObligationCtxt::new(self); + let normalized_term = match expected_term.unpack() { + ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(), + ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(), + }; + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + obligation.param_env, + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { + alias: alias_term, + term: normalized_term, + }), + )); + let _ = ocx.select_where_possible(); + if let Err(terr) = ocx.eq( + &ObligationCause::dummy(), + obligation.param_env, + expected_term, + normalized_term, + ) { + Some((terr, self.resolve_vars_if_possible(normalized_term))) + } else { + None + } + }; - if let Err(new_err) = - ocx.eq(&obligation.cause, obligation.param_env, expected, actual) - { - (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) - } else { - (None, error.err) + if let Some(lhs) = lhs.to_alias_term() + && let Some((better_type_err, expected_term)) = + derive_better_type_error(lhs, rhs) + { + ( + Some((lhs, true, self.resolve_vars_if_possible(expected_term), rhs)), + better_type_err, + ) + } else if let Some(rhs) = rhs.to_alias_term() + && let Some((better_type_err, expected_term)) = + derive_better_type_error(rhs, lhs) + { + ( + Some((rhs, true, self.resolve_vars_if_possible(expected_term), lhs)), + better_type_err, + ) + } else { + (None, error.err) + } } - } else { - (None, error.err) + _ => (None, error.err), }; let msg = values @@ -1737,15 +1421,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_detailed_projection_msg( &self, - pred: ty::ProjectionPredicate<'tcx>, + projection_term: ty::AliasTerm<'tcx>, normalized_ty: ty::Term<'tcx>, expected_ty: ty::Term<'tcx>, ) -> Option<String> { - let trait_def_id = pred.projection_term.trait_def_id(self.tcx); - let self_ty = pred.projection_term.self_ty(); + let trait_def_id = projection_term.trait_def_id(self.tcx); + let self_ty = projection_term.self_ty(); with_forced_trimmed_paths! { - if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) { + if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { let fn_kind = self_ty.prefix_string(self.tcx); let item = match self_ty.kind() { ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), @@ -2297,536 +1981,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) } - #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(obligation.predicate); - let span = obligation.cause.span; - - debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); - - // Ambiguity errors are often caused as fallout from earlier errors. - // We ignore them if this `infcx` is tainted in some cases below. - - let bound_predicate = predicate.kind(); - let mut err = match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - let trait_ref = bound_predicate.rebind(data.trait_ref); - debug!(?trait_ref); - - if let Err(e) = predicate.error_reported() { - return e; - } - - if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) { - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. - return guar; - } - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like "<generic - // #0> doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and don't add the obligations to - // begin with in those cases. - if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { - match self.tainted_by_errors() { - None => { - let err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - trait_ref.self_ty().skip_binder().into(), - ErrorCode::E0282, - false, - ); - return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); - } - Some(e) => return e, - } - } - - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // Pick the first generic parameter that still contains inference variables as the one - // we're going to emit an error for. If there are none (see above), fall back to - // a more general error. - let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); - - let mut err = if let Some(arg) = arg { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0283, - true, - ) - } else { - struct_span_code_err!( - self.dcx(), - span, - E0283, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - }; - - let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics( - self.infcx, - &obligation.with(self.tcx, trait_ref), - ); - let has_non_region_infer = - trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); - // It doesn't make sense to talk about applicable impls if there are more than a - // handful of them. If there are a lot of them, but only a few of them have no type - // params, we only show those, as they are more likely to be useful/intended. - if ambiguities.len() > 5 { - let infcx = self.infcx; - if !ambiguities.iter().all(|option| match option { - DefId(did) => infcx.tcx.generics_of(*did).count() == 0, - ParamEnv(_) => true, - }) { - // If not all are blanket impls, we filter blanked impls out. - ambiguities.retain(|option| match option { - DefId(did) => infcx.tcx.generics_of(*did).count() == 0, - ParamEnv(_) => true, - }); - } - } - if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { - if let Some(e) = self.tainted_by_errors() - && arg.is_none() - { - // If `arg.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return e; - } - self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); - } else { - if let Some(e) = self.tainted_by_errors() { - err.cancel(); - return e; - } - err.note(format!("cannot satisfy `{predicate}`")); - let impl_candidates = - self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); - if impl_candidates.len() < 40 { - self.report_similar_impl_candidates( - impl_candidates.as_slice(), - trait_ref, - obligation.cause.body_id, - &mut err, - false, - obligation.param_env, - ); - } - } - - if let ObligationCauseCode::WhereClause(def_id, _) - | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code() - { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } - - if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) - && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) - { - let mut expr_finder = FindExprBySpan::new(span, self.tcx); - expr_finder.visit_expr(&body.value); - - if let Some(hir::Expr { - kind: - hir::ExprKind::Call( - hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), - .. - }, - _, - ) - | hir::ExprKind::Path(hir::QPath::Resolved(None, path)), - .. - }) = expr_finder.result - && let [ - .., - trait_path_segment @ hir::PathSegment { - res: Res::Def(DefKind::Trait, trait_id), - .. - }, - hir::PathSegment { - ident: assoc_item_name, - res: Res::Def(_, item_id), - .. - }, - ] = path.segments - && data.trait_ref.def_id == *trait_id - && self.tcx.trait_of_item(*item_id) == Some(*trait_id) - && let None = self.tainted_by_errors() - { - let (verb, noun) = match self.tcx.associated_item(item_id).kind { - ty::AssocKind::Const => ("refer to the", "constant"), - ty::AssocKind::Fn => ("call", "function"), - // This is already covered by E0223, but this following single match - // arm doesn't hurt here. - ty::AssocKind::Type => ("refer to the", "type"), - }; - - // Replace the more general E0283 with a more specific error - err.cancel(); - err = self.dcx().struct_span_err( - span, - format!( - "cannot {verb} associated {noun} on trait without specifying the \ - corresponding `impl` type", - ), - ); - err.code(E0790); - - if let Some(local_def_id) = data.trait_ref.def_id.as_local() - && let hir::Node::Item(hir::Item { - ident: trait_name, - kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), - .. - }) = self.tcx.hir_node_by_def_id(local_def_id) - && let Some(method_ref) = trait_item_refs - .iter() - .find(|item_ref| item_ref.ident == *assoc_item_name) - { - err.span_label( - method_ref.span, - format!("`{trait_name}::{assoc_item_name}` defined here"), - ); - } - - err.span_label(span, format!("cannot {verb} associated {noun} of trait")); - - let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); - - if let Some(impl_def_id) = - trait_impls.non_blanket_impls().values().flatten().next() - { - let non_blanket_impl_count = - trait_impls.non_blanket_impls().values().flatten().count(); - // If there is only one implementation of the trait, suggest using it. - // Otherwise, use a placeholder comment for the implementation. - let (message, self_type) = if non_blanket_impl_count == 1 { - ( - "use the fully-qualified path to the only available \ - implementation", - format!( - "{}", - self.tcx.type_of(impl_def_id).instantiate_identity() - ), - ) - } else { - ( - "use a fully-qualified path to a specific available \ - implementation", - "/* self type */".to_string(), - ) - }; - let mut suggestions = - vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = - trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and <type> - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string())); - suggestions - .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push(( - trait_path_segment.ident.span.shrink_to_hi(), - ">".to_string(), - )); - } - err.multipart_suggestion( - message, - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - }; - - err - } - - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - - if let Err(e) = arg.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0282, - false, - ) - } - - ty::PredicateKind::Subtype(data) => { - if let Err(e) = data.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - let SubtypePredicate { a_is_expected: _, a, b } = data; - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - a.into(), - ErrorCode::E0282, - true, - ) - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { - if let Err(e) = predicate.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - - if let Err(guar) = - self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) - { - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. - return guar; - } - let arg = data - .projection_term - .args - .iter() - .chain(Some(data.term.into_arg())) - .find(|g| g.has_non_region_infer()); - if let Some(arg) = arg { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0284, - true, - ) - .with_note(format!("cannot satisfy `{predicate}`")) - } else { - // If we can't find a generic parameter, just print a generic error - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { - if let Err(e) = predicate.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - let arg = data.walk().find(|g| g.is_non_region_infer()); - if let Some(arg) = arg { - let err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0284, - true, - ); - err - } else { - // If we can't find a generic parameter, just print a generic error - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self - .emit_inference_failure_err( - obligation.cause.body_id, - span, - ct.into(), - ErrorCode::E0284, - true, - ), - ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) - if term.is_infer() => - { - if let Some(e) = self.tainted_by_errors() { - return e; - } - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot normalize `{alias}`", - ) - .with_span_label(span, format!("cannot normalize `{alias}`")) - } - - _ => { - if let Some(e) = self.tainted_by_errors() { - return e; - } - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit() - } - - fn annotate_source_of_ambiguity( - &self, - err: &mut Diag<'_>, - ambiguities: &[ambiguity::CandidateSource], - predicate: ty::Predicate<'tcx>, - ) { - let mut spans = vec![]; - let mut crates = vec![]; - let mut post = vec![]; - let mut has_param_env = false; - for ambiguity in ambiguities { - match ambiguity { - ambiguity::CandidateSource::DefId(impl_def_id) => { - match self.tcx.span_of_impl(*impl_def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { - post.push(header); - } - } - } - } - ambiguity::CandidateSource::ParamEnv(span) => { - has_param_env = true; - spans.push(*span); - } - } - } - let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); - crate_names.sort(); - crate_names.dedup(); - post.sort(); - post.dedup(); - - if self.tainted_by_errors().is_some() - && (crate_names.len() == 1 - && spans.len() == 0 - && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) - || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) - { - // Avoid complaining about other inference issues for expressions like - // `42 >> 1`, where the types are still `{integer}`, but we want to - // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? - // NOTE(eddyb) this was `.cancel()`, but `err` - // is borrowed, so we can't fully defuse it. - err.downgrade_to_delayed_bug(); - return; - } - - let msg = format!( - "multiple `impl`s{} satisfying `{}` found", - if has_param_env { " or `where` clauses" } else { "" }, - predicate - ); - let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) - } else if post.len() == 1 { - format!(": `{}`", post[0]) - } else { - String::new() - }; - - match (spans.len(), crates.len(), crate_names.len()) { - (0, 0, 0) => { - err.note(format!("cannot satisfy `{predicate}`")); - } - (0, _, 1) => { - err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); - } - (0, _, _) => { - err.note(format!( - "{} in the following crates: {}{}", - msg, - crate_names.join(", "), - post, - )); - } - (_, 0, 0) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - } - (_, 1, 1) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); - } - _ => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!( - "and more `impl`s found in the following crates: {}{}", - crate_names.join(", "), - post, - )); - } - } - } - /// Returns `true` if the trait predicate may apply for *some* assignment /// to the type parameters. fn predicate_can_apply( @@ -2884,7 +2038,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = - self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) + self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) && let Ok(ref mut s1) = err.suggestions && let Ok(ref mut s2) = diag.suggestions { @@ -2894,136 +2048,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - #[instrument(level = "debug", skip_all)] - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut Diag<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = - obligation.predicate.kind().skip_binder() - else { - return; - }; - let (ObligationCauseCode::WhereClause(item_def_id, span) - | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) = - *obligation.cause.code().peel_derives() - else { - return; - }; - if span.is_dummy() { - return; - } - debug!(?pred, ?item_def_id, ?span); - - let (Some(node), true) = ( - self.tcx.hir().get_if_local(item_def_id), - self.tcx.is_lang_item(pred.def_id(), LangItem::Sized), - ) else { - return; - }; - self.maybe_suggest_unsized_generics(err, span, node); - } - - #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics(&self, err: &mut Diag<'_>, span: Span, node: Node<'tcx>) { - let Some(generics) = node.generics() else { - return; - }; - let sized_trait = self.tcx.lang_items().sized_trait(); - debug!(?generics.params); - debug!(?generics.predicates); - let Some(param) = generics.params.iter().find(|param| param.span == span) else { - return; - }; - // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit - // `Sized` bound is there intentionally and we don't need to suggest relaxing it. - let explicitly_sized = generics - .bounds_for_param(param.def_id) - .flat_map(|bp| bp.bounds) - .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); - if explicitly_sized { - return; - } - debug!(?param); - match node { - hir::Node::Item( - item @ hir::Item { - // Only suggest indirection for uses of type parameters in ADTs. - kind: - hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), - .. - }, - ) => { - if self.maybe_indirection_for_unsized(err, item, param) { - return; - } - } - _ => {} - }; - - // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. - let (span, separator, open_paren_sp) = - if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { - (s, " +", open_paren_sp) - } else { - (param.name.ident().span.shrink_to_hi(), ":", None) - }; - - let mut suggs = vec![]; - let suggestion = format!("{separator} ?Sized"); - - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((span, format!("){suggestion}"))); - } else { - suggs.push((span, suggestion)); - } - - err.multipart_suggestion_verbose( - "consider relaxing the implicit `Sized` restriction", - suggs, - Applicability::MachineApplicable, - ); - } - - fn maybe_indirection_for_unsized( - &self, - err: &mut Diag<'_>, - item: &Item<'tcx>, - param: &GenericParam<'tcx>, - ) -> bool { - // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a - // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` - // is not. Look for invalid "bare" parameter uses, and suggest using indirection. - let mut visitor = - FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; - visitor.visit_item(item); - if visitor.invalid_spans.is_empty() { - return false; - } - let mut multispan: MultiSpan = param.span.into(); - multispan.push_span_label( - param.span, - format!("this could be changed to `{}: ?Sized`...", param.name.ident()), - ); - for sp in visitor.invalid_spans { - multispan.push_span_label( - sp, - format!("...if indirection were used here: `Box<{}>`", param.name.ident()), - ); - } - err.span_help( - multispan, - format!( - "you could relax the implicit `Sized` bound on `{T}` if it were \ - used through indirection like `&{T}` or `Box<{T}>`", - T = param.name.ident(), - ), - ); - true - } - fn is_recursive_obligation( &self, obligated_types: &mut Vec<Ty<'tcx>>, @@ -3379,7 +2403,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { found_kind: ty::ClosureKind, kind: ty::ClosureKind, trait_prefix: &'static str, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let closure_span = self.tcx.def_span(closure_def_id); let mut err = ClosureKindMismatch { @@ -3422,7 +2446,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { found_trait_ref: ty::TraitRef<'tcx>, expected_trait_ref: ty::TraitRef<'tcx>, terr: TypeError<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { let self_ty = found_trait_ref.self_ty(); let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { ( @@ -3473,7 +2497,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span: Span, found_trait_ref: ty::TraitRef<'tcx>, expected_trait_ref: ty::TraitRef<'tcx>, - ) -> Result<Diag<'tcx>, ErrorGuaranteed> { + ) -> Result<Diag<'a>, ErrorGuaranteed> { let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); @@ -3553,7 +2577,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) .unwrap_or((found_span, None, found)); - self.report_arg_count_mismatch( + self.infcx.report_arg_count_mismatch( span, closure_span, expected, @@ -3569,7 +2593,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, span: Span, - ) -> Result<Diag<'tcx>, ErrorGuaranteed> { + ) -> Result<Diag<'a>, ErrorGuaranteed> { if !self.tcx.features().generic_const_exprs { let guar = self .dcx() diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs index 4b5b1d77b30..e8d7e80ac56 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs @@ -1,3 +1,5 @@ +// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`. + use crate::infer::InferCtxt; use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag}; @@ -9,8 +11,6 @@ use rustc_span::{Span, DUMMY_SP}; use super::ArgKind; -pub use rustc_infer::traits::error_reporting::*; - #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { /// Given some node representing a fn-like thing in the HIR map, @@ -88,7 +88,7 @@ impl<'tcx> InferCtxt<'tcx> { found_args: Vec<ArgKind>, is_closure: bool, closure_arg_span: Option<Span>, - ) -> Diag<'tcx> { + ) -> Diag<'_> { let kind = if is_closure { "closure" } else { "function" }; let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs new file mode 100644 index 00000000000..2131e236401 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -0,0 +1,385 @@ +pub mod ambiguity; +mod fulfillment_errors; +mod infer_ctxt_ext; +pub mod on_unimplemented; +mod overflow; +pub mod suggestions; + +use std::iter; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, LangItem}; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::traits::{ + Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, +}; +use rustc_macros::extension; +use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; + +use ambiguity::TypeErrCtxtAmbiguityExt as _; +use fulfillment_errors::TypeErrCtxtExt as _; +use suggestions::TypeErrCtxtExt as _; + +use crate::traits::{FulfillmentError, FulfillmentErrorCode}; + +pub use self::fulfillment_errors::*; +pub use self::infer_ctxt_ext::*; +pub use self::overflow::*; + +// When outputting impl candidates, prefer showing those that are more similar. +// +// We also compare candidates after skipping lifetimes, which has a lower +// priority than exact matches. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum CandidateSimilarity { + Exact { ignoring_lifetimes: bool }, + Fuzzy { ignoring_lifetimes: bool }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ImplCandidate<'tcx> { + pub trait_ref: ty::TraitRef<'tcx>, + pub similarity: CandidateSimilarity, + impl_def_id: DefId, +} + +enum GetSafeTransmuteErrorAndReason { + Silent, + Error { err_msg: String, safe_transmute_explanation: Option<String> }, +} + +struct UnsatisfiedConst(pub bool); + +/// Crude way of getting back an `Expr` from a `Span`. +pub struct FindExprBySpan<'hir> { + pub span: Span, + pub result: Option<&'hir hir::Expr<'hir>>, + pub ty_result: Option<&'hir hir::Ty<'hir>>, + pub include_closures: bool, + pub tcx: TyCtxt<'hir>, +} + +impl<'hir> FindExprBySpan<'hir> { + pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self { + Self { span, result: None, ty_result: None, tcx, include_closures: false } + } +} + +impl<'v> Visitor<'v> for FindExprBySpan<'v> { + type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.span == ex.span { + self.result = Some(ex); + } else { + if let hir::ExprKind::Closure(..) = ex.kind + && self.include_closures + && let closure_header_sp = self.span.with_hi(ex.span.hi()) + && closure_header_sp == ex.span + { + self.result = Some(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if self.span == ty.span { + self.ty_result = Some(ty); + } else { + hir::intravisit::walk_ty(self, ty); + } + } +} + +/// Summarizes information +#[derive(Clone)] +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) + Arg(String, String), + + /// An argument of tuple type. For a "found" argument, the span is + /// the location in the source of the pattern. For an "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option<Span>, Vec<(String, String)>), +} + +impl ArgKind { + fn empty() -> ArgKind { + ArgKind::Arg("_".to_owned(), "_".to_owned()) + } + + /// Creates an `ArgKind` from the expected type of an + /// argument. It has no name (`_`) and an optional source span. + pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind { + match t.kind() { + ty::Tuple(tys) => ArgKind::Tuple( + span, + tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(), + ), + _ => ArgKind::Arg("_".to_owned(), t.to_string()), + } + } +} + +#[derive(Copy, Clone)] +pub enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} + +#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + fn report_fulfillment_errors( + &self, + mut errors: Vec<FulfillmentError<'tcx>>, + ) -> ErrorGuaranteed { + self.sub_relations + .borrow_mut() + .add_constraints(self, errors.iter().map(|e| e.obligation.predicate)); + + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option<usize>, // None if this is an old error + } + + let mut error_map: FxIndexMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .0 + .iter() + .map(|&predicate| ErrorDescriptor { predicate, index: None }) + .collect(), + ) + }) + .collect(); + + // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics + // with more relevant type information and hide redundant E0282 errors. + errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) + if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) => + { + 1 + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, + ty::PredicateKind::Coerce(_) => 2, + _ => 0, + }); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate, + index: Some(index), + }); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.is_some_and(|index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(error2.predicate, error.predicate) + && !(error2.index >= error.index + && self.error_implies(error.predicate, error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + let mut reported = None; + + for from_expansion in [false, true] { + for (error, suppressed) in iter::zip(&errors, &is_suppressed) { + if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { + let guar = self.report_fulfillment_error(error); + self.infcx.set_tainted_by_errors(guar); + reported = Some(guar); + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_insert_with(|| (vec![], guar)) + .0 + .push(error.obligation.predicate); + } + } + } + + // It could be that we don't report an error because we have seen an `ErrorReported` from + // another source. We should probably be able to fix most of these, but some are delayed + // bugs that get a proper error after this function. + reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors")) + } + + #[instrument(skip(self), level = "debug")] + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed { + let mut error = FulfillmentError { + obligation: error.obligation.clone(), + code: error.code.clone(), + root_obligation: error.root_obligation.clone(), + }; + if matches!( + error.code, + FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented) + | FulfillmentErrorCode::Project(_) + ) && self.apply_do_not_recommend(&mut error.obligation) + { + error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented); + } + + match error.code { + FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error( + error.obligation.clone(), + &error.root_obligation, + selection_error, + ), + FulfillmentErrorCode::Project(ref e) => { + self.report_projection_error(&error.obligation, e) + } + FulfillmentErrorCode::Ambiguity { overflow: None } => { + self.maybe_report_ambiguity(&error.obligation) + } + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { + self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit) + } + FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self + .report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ) + .emit(), + FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => { + let mut diag = self.report_mismatched_consts( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ); + let code = error.obligation.cause.code().peel_derives().peel_match_impls(); + if let ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) = code + { + self.note_obligation_cause_code( + error.obligation.cause.body_id, + &mut diag, + error.obligation.predicate, + error.obligation.param_env, + code, + &mut vec![], + &mut Default::default(), + ); + } + diag.emit() + } + FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle), + } + } +} + +/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a +/// string. +pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { + use std::fmt::Write; + + let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity(); + let mut w = "impl".to_owned(); + + let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id); + + // FIXME: Currently only handles ?Sized. + // Needs to support ?Move and ?DynSized when they are implemented. + let mut types_without_default_bounds = FxIndexSet::default(); + let sized_trait = tcx.lang_items().sized_trait(); + + let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>(); + if !arg_names.is_empty() { + types_without_default_bounds.extend(args.types()); + w.push('<'); + w.push_str(&arg_names.join(", ")); + w.push('>'); + } + + write!( + w, + " {} for {}", + trait_ref.print_only_trait_path(), + tcx.type_of(impl_def_id).instantiate_identity() + ) + .unwrap(); + + // The predicates will contain default bounds like `T: Sized`. We need to + // remove these bounds, and add `T: ?Sized` to any untouched type parameters. + let predicates = tcx.predicates_of(impl_def_id).predicates; + let mut pretty_predicates = + Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); + + for (p, _) in predicates { + if let Some(poly_trait_ref) = p.as_trait_clause() { + if Some(poly_trait_ref.def_id()) == sized_trait { + // FIXME(#120456) - is `swap_remove` correct? + types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder()); + continue; + } + } + pretty_predicates.push(p.to_string()); + } + + pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized"))); + + if !pretty_predicates.is_empty() { + write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); + } + + w.push(';'); + Some(w) +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 32c8a454b40..e90fe8fb94d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,5 +1,10 @@ use super::{ObligationCauseCode, PredicateObligation}; +use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; use crate::infer::error_reporting::TypeErrCtxt; +use crate::infer::InferCtxtExt; use rustc_ast::AttrArgs; use rustc_ast::AttrArgsEq; use rustc_ast::AttrKind; @@ -21,12 +26,6 @@ use rustc_span::Span; use std::iter; use std::path::PathBuf; -use crate::errors::{ - EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, -}; - -use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; - /// The symbols which are always allowed in a format string static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ kw::SelfUpper, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs new file mode 100644 index 00000000000..061a5a4be20 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -0,0 +1,197 @@ +use std::fmt; + +use rustc_errors::{ + struct_span_code_err, Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, E0275, +}; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::traits::{Obligation, PredicateObligation}; +use rustc_macros::extension; +use rustc_middle::ty::print::{FmtPrinter, Print}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::Limit; +use rustc_span::Span; +use rustc_type_ir::Upcast; + +use super::InferCtxtPrivExt; +use crate::error_reporting::traits::suggestions::TypeErrCtxtExt; + +pub enum OverflowCause<'tcx> { + DeeplyNormalize(ty::AliasTerm<'tcx>), + TraitSolver(ty::Predicate<'tcx>), +} + +pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_, G>, +) { + let suggested_limit = match tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + tcx.crate_name(LOCAL_CRATE), + )); +} + +#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_error( + &self, + cause: OverflowCause<'tcx>, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diag<'_>), + ) -> ! { + let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit); + mutate(&mut err); + err.emit(); + FatalError.raise(); + } + + fn build_overflow_error( + &self, + cause: OverflowCause<'tcx>, + span: Span, + suggest_increasing_limit: bool, + ) -> Diag<'a> { + fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String + where + T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, + { + let s = value.to_string(); + if s.len() > 50 { + // We don't need to save the type to a file, we will be talking about this type already + // in a separate note when we explain the obligation, so it will be available that way. + let mut cx: FmtPrinter<'_, '_> = + FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6)); + value.print(&mut cx).unwrap(); + cx.into_buffer() + } else { + s + } + } + + let mut err = match cause { + OverflowCause::DeeplyNormalize(alias_term) => { + let alias_term = self.resolve_vars_if_possible(alias_term); + let kind = alias_term.kind(self.tcx).descr(); + let alias_str = with_short_path(self.tcx, alias_term); + struct_span_code_err!( + self.dcx(), + span, + E0275, + "overflow normalizing the {kind} `{alias_str}`", + ) + } + OverflowCause::TraitSolver(predicate) => { + let predicate = self.resolve_vars_if_possible(predicate); + match predicate.kind().skip_binder() { + ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) + | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { + struct_span_code_err!( + self.dcx(), + span, + E0275, + "overflow assigning `{a}` to `{b}`", + ) + } + _ => { + let pred_str = with_short_path(self.tcx, predicate); + struct_span_code_err!( + self.dcx(), + span, + E0275, + "overflow evaluating the requirement `{pred_str}`", + ) + } + } + } + }; + + if suggest_increasing_limit { + suggest_new_overflow_limit(self.tcx, &mut err); + } + + err + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone, + { + let predicate = obligation.predicate.clone().upcast(self.tcx); + let predicate = self.resolve_vars_if_possible(predicate); + self.report_overflow_error( + OverflowCause::TraitSolver(predicate), + obligation.cause.span, + suggest_increasing_limit, + |err| { + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + }, + ); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_obligation` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(cycle.to_owned()); + assert!(!cycle.is_empty()); + + debug!(?cycle, "report_overflow_error_cycle"); + + // The 'deepest' obligation is most likely to have a useful + // cause 'backtrace' + self.report_overflow_obligation( + cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), + false, + ); + } + + fn report_overflow_no_abort( + &self, + obligation: PredicateObligation<'tcx>, + suggest_increasing_limit: bool, + ) -> ErrorGuaranteed { + let obligation = self.resolve_vars_if_possible(obligation); + let mut err = self.build_overflow_error( + OverflowCause::TraitSolver(obligation.predicate), + obligation.cause.span, + suggest_increasing_limit, + ); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit() + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index f4a026c0367..2cf808f962f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -44,8 +44,8 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; +use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; use crate::infer::InferCtxtExt as _; -use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{ with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _, @@ -241,8 +241,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } } -#[extension(pub trait TypeErrCtxtExt<'tcx>)] -impl<'tcx> TypeErrCtxt<'_, 'tcx> { +#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn suggest_restricting_param_bound( &self, err: &mut Diag<'_>, @@ -1845,7 +1845,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn point_at_returns_when_relevant( &self, - err: &mut Diag<'tcx>, + err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, ) { match obligation.cause.code().peel_derives() { @@ -1884,7 +1884,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause: &ObligationCauseCode<'tcx>, found_node: Option<Node<'_>>, param_env: ty::ParamEnv<'tcx>, - ) -> Diag<'tcx> { + ) -> Diag<'a> { pub(crate) fn build_fn_sig_ty<'tcx>( infcx: &InferCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, @@ -2104,7 +2104,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn note_conflicting_closure_bounds( &self, cause: &ObligationCauseCode<'tcx>, - err: &mut Diag<'tcx>, + err: &mut Diag<'_>, ) { // First, look for an `WhereClauseInExpr`, which means we can get // the uninstantiated predicate list of the called function. And check @@ -3472,6 +3472,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) }); } + // Suppress `compare_type_predicate_entailment` errors for RPITITs, since they + // should be implied by the parent method. + ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } + if tcx.is_impl_trait_in_trait(trait_item_def_id) => {} ObligationCauseCode::CompareImplItem { trait_item_def_id, kind, .. } => { let item_name = tcx.item_name(trait_item_def_id); let msg = format!( @@ -4114,7 +4118,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { expr = binding_expr; } if let hir::Node::Param(param) = parent { - // ...and it is a an fn argument. + // ...and it is an fn argument. let prev_ty = self.resolve_vars_if_possible( typeck_results .node_type_opt(param.hir_id) @@ -4619,6 +4623,132 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } } + + #[instrument(level = "debug", skip_all)] + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + ) { + let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + obligation.predicate.kind().skip_binder() + else { + return; + }; + let (ObligationCauseCode::WhereClause(item_def_id, span) + | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) = + *obligation.cause.code().peel_derives() + else { + return; + }; + if span.is_dummy() { + return; + } + debug!(?pred, ?item_def_id, ?span); + + let (Some(node), true) = ( + self.tcx.hir().get_if_local(item_def_id), + self.tcx.is_lang_item(pred.def_id(), LangItem::Sized), + ) else { + return; + }; + + let Some(generics) = node.generics() else { + return; + }; + let sized_trait = self.tcx.lang_items().sized_trait(); + debug!(?generics.params); + debug!(?generics.predicates); + let Some(param) = generics.params.iter().find(|param| param.span == span) else { + return; + }; + // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit + // `Sized` bound is there intentionally and we don't need to suggest relaxing it. + let explicitly_sized = generics + .bounds_for_param(param.def_id) + .flat_map(|bp| bp.bounds) + .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); + if explicitly_sized { + return; + } + debug!(?param); + match node { + hir::Node::Item( + item @ hir::Item { + // Only suggest indirection for uses of type parameters in ADTs. + kind: + hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), + .. + }, + ) => { + if self.suggest_indirection_for_unsized(err, item, param) { + return; + } + } + _ => {} + }; + + // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. + let (span, separator, open_paren_sp) = + if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { + (s, " +", open_paren_sp) + } else { + (param.name.ident().span.shrink_to_hi(), ":", None) + }; + + let mut suggs = vec![]; + let suggestion = format!("{separator} ?Sized"); + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((span, format!("){suggestion}"))); + } else { + suggs.push((span, suggestion)); + } + + err.multipart_suggestion_verbose( + "consider relaxing the implicit `Sized` restriction", + suggs, + Applicability::MachineApplicable, + ); + } + + fn suggest_indirection_for_unsized( + &self, + err: &mut Diag<'_>, + item: &hir::Item<'tcx>, + param: &hir::GenericParam<'tcx>, + ) -> bool { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` + // is not. Look for invalid "bare" parameter uses, and suggest using indirection. + let mut visitor = + FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + visitor.visit_item(item); + if visitor.invalid_spans.is_empty() { + return false; + } + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!("...if indirection were used here: `Box<{}>`", param.name.ident()), + ); + } + err.span_help( + multispan, + format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), + ); + true + } } /// Add a hint to add a missing borrow or remove an unnecessary one. @@ -4995,6 +5125,12 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( let ty::ClauseKind::Projection(proj) = clause else { return; }; + // avoid ICEing since effects desugared associated types don't have names. + // this path should only be hit for `~const` on invalid places, so they + // will have an informative error already. + if tcx.is_effects_desugared_assoc_ty(proj.projection_term.def_id) { + return; + } let name = tcx.item_name(proj.projection_term.def_id); let mut predicates = generics.predicates.iter().peekable(); let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; @@ -5116,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { (ty, refs) } + +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec<Span>, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { + // Skip where-clauses, to avoid suggesting indirection for type parameters found there. + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + debug!(?ty, "FindTypeParam::visit_ty"); + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index c95649e2ffb..ad087620ae0 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,3 +1,4 @@ +use crate::infer::at::ToTrace; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; @@ -17,6 +18,16 @@ pub use rustc_infer::infer::*; #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { + fn can_eq<T: ToTrace<'tcx>>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool { + self.probe(|_| { + let ocx = ObligationCtxt::new(self); + let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else { + return false; + }; + ocx.select_where_possible().is_empty() + }) + } + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let ty = self.resolve_vars_if_possible(ty); diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 50c618bb3bd..d0a12d73941 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -26,12 +26,14 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(type_alias_impl_trait)] +#![feature(unwrap_infallible)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end #[macro_use] extern crate tracing; +pub mod error_reporting; pub mod errors; pub mod infer; pub mod regions; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 643d5f80480..f67518a577e 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,24 +1,19 @@ use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalVarInfo, CanonicalVarValues, }; -use rustc_infer::infer::{ - BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt, -}; +use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; -use rustc_infer::traits::util::supertraits; use rustc_infer::traits::{ObligationCause, Reveal}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; -use rustc_type_ir::relate::Relate; use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode}; -use crate::traits::coherence::trait_ref_is_knowable; use crate::traits::specialization_graph; #[repr(transparent)] @@ -48,13 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< type Span = Span; - fn solver_mode(&self) -> ty::solve::SolverMode { - match self.intercrate { - true => SolverMode::Coherence, - false => SolverMode::Normal, - } - } - fn build_with_canonical<V>( interner: TyCtxt<'tcx>, solver_mode: SolverMode, @@ -74,104 +62,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< (SolverDelegate(infcx), value, vars) } - fn universe(&self) -> ty::UniverseIndex { - self.0.universe() - } - - fn create_next_universe(&self) -> ty::UniverseIndex { - self.0.create_next_universe() - } - - fn universe_of_ty(&self, vid: ty::TyVid) -> Option<ty::UniverseIndex> { - // FIXME(BoxyUwU): this is kind of jank and means that printing unresolved - // ty infers will give you the universe of the var it resolved to not the universe - // it actually had. It also means that if you have a `?0.1` and infer it to `u8` then - // try to print out `?0.1` it will just print `?0`. - match self.0.probe_ty_var(vid) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> { - match self.0.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex> { - // Same issue as with `universe_of_ty` - match self.0.probe_const_var(ct) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { - self.0.root_var(var) - } - - fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { - self.0.root_const_var(var) - } - - fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { - match self.0.probe_ty_var(vid) { - Ok(ty) => ty, - Err(_) => Ty::new_var(self.0.tcx, self.0.root_var(vid)), - } - } - - fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { - self.0.opportunistic_resolve_int_var(vid) - } - - fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { - self.0.opportunistic_resolve_float_var(vid) - } - - fn opportunistic_resolve_ct_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> { - match self.0.probe_const_var(vid) { - Ok(ct) => ct, - Err(_) => ty::Const::new_var(self.0.tcx, self.0.root_const_var(vid)), - } - } - - fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> { - match self.0.probe_effect_var(vid) { - Some(ct) => ct, - None => ty::Const::new_infer( - self.0.tcx, - ty::InferConst::EffectVar(self.0.root_effect_var(vid)), - ), - } - } - - fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { - self.0 - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.0.tcx, vid) - } - - fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> { - self.0.defining_opaque_types() - } - - fn next_ty_infer(&self) -> Ty<'tcx> { - self.0.next_ty_var(DUMMY_SP) - } - - fn next_const_infer(&self) -> ty::Const<'tcx> { - self.0.next_const_var(DUMMY_SP) - } - - fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { - self.0.fresh_args_for_item(DUMMY_SP, def_id) - } - fn fresh_var_for_kind_with_span( &self, arg: ty::GenericArg<'tcx>, @@ -186,68 +76,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< } } - fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>( - &self, - value: ty::Binder<'tcx, T>, - ) -> T { - self.0.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - value, - ) - } - - fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>( - &self, - value: ty::Binder<'tcx, T>, - f: impl FnOnce(T) -> U, - ) -> U { - self.0.enter_forall(value, f) - } - - fn relate<T: Relate<TyCtxt<'tcx>>>( - &self, - param_env: ty::ParamEnv<'tcx>, - lhs: T, - variance: ty::Variance, - rhs: T, - ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { - self.0.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs) - } - - fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>( - &self, - param_env: ty::ParamEnv<'tcx>, - lhs: T, - rhs: T, - ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { - self.0 - .at(&ObligationCause::dummy(), param_env) - .eq_structurally_relating_aliases_no_trace(lhs, rhs) - } - - fn resolve_vars_if_possible<T>(&self, value: T) -> T - where - T: TypeFoldable<TyCtxt<'tcx>>, - { - self.0.resolve_vars_if_possible(value) - } - - fn probe<T>(&self, probe: impl FnOnce() -> T) -> T { - self.0.probe(|_| probe()) - } - fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> { self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution) } - fn elaborate_supertraits( - interner: TyCtxt<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ) -> impl Iterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>> { - supertraits(interner, trait_ref) - } - fn try_const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -265,14 +97,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< } } - fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) { - self.0.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup) - } - - fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) { - self.0.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy()); - } - fn well_formed_goals( &self, param_env: ty::ParamEnv<'tcx>, @@ -367,15 +191,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< let _ = self.take_opaque_types(); } - fn trait_ref_is_knowable<E: std::fmt::Debug>( - &self, - trait_ref: ty::TraitRef<'tcx>, - lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, - ) -> Result<bool, E> { - trait_ref_is_knowable(&self.0, trait_ref, lazily_normalize_ty) - .map(|is_knowable| is_knowable.is_ok()) - } - fn fetch_eligible_assoc_item( &self, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 2679da942b7..ca313590265 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use std::marker::PhantomData; -use crate::traits::error_reporting::{OverflowCause, TypeErrCtxtExt}; +use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; use rustc_data_structures::stack::ensure_sufficient_stack; diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index a4177d8a93f..7e996c5c5ef 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -25,41 +25,13 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; +pub use rustc_next_trait_solver::coherence::*; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; use std::fmt::Debug; -use std::ops::ControlFlow; -use super::error_reporting::suggest_new_overflow_limit; use super::ObligationCtxt; - -/// Whether we do the orphan check relative to this crate or to some remote crate. -#[derive(Copy, Clone, Debug)] -pub enum InCrate { - Local { mode: OrphanCheckMode }, - Remote, -} - -#[derive(Copy, Clone, Debug)] -pub enum OrphanCheckMode { - /// Proper orphan check. - Proper, - /// Improper orphan check for backward compatibility. - /// - /// In this mode, type params inside projections are considered to be covered - /// even if the projection may normalize to a type that doesn't actually cover - /// them. This is unsound. See also [#124559] and [#99554]. - /// - /// [#124559]: https://github.com/rust-lang/rust/issues/124559 - /// [#99554]: https://github.com/rust-lang/rust/issues/99554 - Compat, -} - -#[derive(Debug, Copy, Clone)] -pub enum Conflict { - Upstream, - Downstream, -} +use crate::error_reporting::traits::suggest_new_overflow_limit; pub struct OverlapResult<'tcx> { pub impl_header: ty::ImplHeader<'tcx>, @@ -121,7 +93,7 @@ pub fn overlapping_impls( // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; + let drcx = DeepRejectCtxt::new(tcx, TreatParams::AsCandidateKey); let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); let may_overlap = match (impl1_ref, impl2_ref) { @@ -612,426 +584,6 @@ fn try_prove_negated_where_clause<'tcx>( true } -/// Returns whether all impls which would apply to the `trait_ref` -/// e.g. `Ty: Trait<Arg>` are already known in the local crate. -/// -/// This both checks whether any downstream or sibling crates could -/// implement it and whether an upstream crate can add this impl -/// without breaking backwards compatibility. -#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] -pub fn trait_ref_is_knowable<'tcx, E: Debug>( - infcx: &InferCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -) -> Result<Result<(), Conflict>, E> { - if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() - { - // A downstream or cousin crate is allowed to implement some - // generic parameters of this trait-ref. - return Ok(Err(Conflict::Downstream)); - } - - if trait_ref_is_local_or_fundamental(infcx.tcx, trait_ref) { - // This is a local or fundamental trait, so future-compatibility - // is no concern. We know that downstream/cousin crates are not - // allowed to implement a generic parameter of this trait ref, - // which means impls could only come from dependencies of this - // crate, which we already know about. - return Ok(Ok(())); - } - - // This is a remote non-fundamental trait, so if another crate - // can be the "final owner" of the generic parameters of this trait-ref, - // they are allowed to implement it future-compatibly. - // - // However, if we are a final owner, then nobody else can be, - // and if we are an intermediate owner, then we don't care - // about future-compatibility, which means that we're OK if - // we are an owner. - if orphan_check_trait_ref( - infcx, - trait_ref, - InCrate::Local { mode: OrphanCheckMode::Proper }, - &mut lazily_normalize_ty, - )? - .is_ok() - { - Ok(Ok(())) - } else { - Ok(Err(Conflict::Upstream)) - } -} - -pub fn trait_ref_is_local_or_fundamental<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, -) -> bool { - trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental) -} - -#[derive(Debug, Copy, Clone)] -pub enum IsFirstInputType { - No, - Yes, -} - -impl From<bool> for IsFirstInputType { - fn from(b: bool) -> IsFirstInputType { - match b { - false => IsFirstInputType::No, - true => IsFirstInputType::Yes, - } - } -} - -#[derive(Debug)] -pub enum OrphanCheckErr<'tcx, T> { - NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>), - UncoveredTyParams(UncoveredTyParams<'tcx, T>), -} - -#[derive(Debug)] -pub struct UncoveredTyParams<'tcx, T> { - pub uncovered: T, - pub local_ty: Option<Ty<'tcx>>, -} - -/// Checks whether a trait-ref is potentially implementable by a crate. -/// -/// The current rule is that a trait-ref orphan checks in a crate C: -/// -/// 1. Order the parameters in the trait-ref in generic parameters order -/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). -/// 2. Of these type parameters, there is at least one type parameter -/// in which, walking the type as a tree, you can reach a type local -/// to C where all types in-between are fundamental types. Call the -/// first such parameter the "local key parameter". -/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType -/// going through `Box`, which is fundamental. -/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for -/// the same reason. -/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's -/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between -/// the local type and the type parameter. -/// 3. Before this local type, no generic type parameter of the impl must -/// be reachable through fundamental types. -/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. -/// - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is -/// reachable through the fundamental type `Box`. -/// 4. Every type in the local key parameter not known in C, going -/// through the parameter's type tree, must appear only as a subtree of -/// a type local to C, with only fundamental types between the type -/// local to C and the local key parameter. -/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) -/// is bad, because the only local type with `T` as a subtree is -/// `LocalType<T>`, and `Vec<->` is between it and the type parameter. -/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because -/// the second occurrence of `T` is not a subtree of *any* local type. -/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of -/// `LocalType<Vec<T>>`, which is local and has no types between it and -/// the type parameter. -/// -/// The orphan rules actually serve several different purposes: -/// -/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where -/// every type local to one crate is unknown in the other) can't implement -/// the same trait-ref. This follows because it can be seen that no such -/// type can orphan-check in 2 such crates. -/// -/// To check that a local impl follows the orphan rules, we check it in -/// InCrate::Local mode, using type parameters for the "generic" types. -/// -/// In InCrate::Local mode the orphan check succeeds if the current crate -/// is definitely allowed to implement the given trait (no false positives). -/// -/// 2. They ground negative reasoning for coherence. If a user wants to -/// write both a conditional blanket impl and a specific impl, we need to -/// make sure they do not overlap. For example, if we write -/// ```ignore (illustrative) -/// impl<T> IntoIterator for Vec<T> -/// impl<T: Iterator> IntoIterator for T -/// ``` -/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. -/// We can observe that this holds in the current crate, but we need to make -/// sure this will also hold in all unknown crates (both "independent" crates, -/// which we need for link-safety, and also child crates, because we don't want -/// child crates to get error for impl conflicts in a *dependency*). -/// -/// For that, we only allow negative reasoning if, for every assignment to the -/// inference variables, every unknown crate would get an orphan error if they -/// try to implement this trait-ref. To check for this, we use InCrate::Remote -/// mode. That is sound because we already know all the impls from known crates. -/// -/// In InCrate::Remote mode the orphan check succeeds if a foreign crate -/// *could* implement the given trait (no false negatives). -/// -/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can -/// add "non-blanket" impls without breaking negative reasoning in dependent -/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. -/// -/// For that, we only allow a crate to perform negative reasoning on -/// non-local-non-`#[fundamental]` if there's a local key parameter as per (2). -/// -/// Because we never perform negative reasoning generically (coherence does -/// not involve type parameters), this can be interpreted as doing the full -/// orphan check (using InCrate::Local mode), instantiating non-local known -/// types for all inference variables. -/// -/// This allows for crates to future-compatibly add impls as long as they -/// can't apply to types with a key parameter in a child crate - applying -/// the rules, this basically means that every type parameter in the impl -/// must appear behind a non-fundamental type (because this is not a -/// type-system requirement, crate owners might also go for "semantic -/// future-compatibility" involving things such as sealed traits, but -/// the above requirement is sufficient, and is necessary in "open world" -/// cases). -/// -/// Note that this function is never called for types that have both type -/// parameters and inference variables. -#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] -pub fn orphan_check_trait_ref<'tcx, E: Debug>( - infcx: &InferCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - in_crate: InCrate, - lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -) -> Result<Result<(), OrphanCheckErr<'tcx, Ty<'tcx>>>, E> { - if trait_ref.has_param() { - bug!("orphan check only expects inference variables: {trait_ref:?}"); - } - - let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); - Ok(match trait_ref.visit_with(&mut checker) { - ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), - ControlFlow::Break(residual) => match residual { - OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), - OrphanCheckEarlyExit::UncoveredTyParam(ty) => { - // Does there exist some local type after the `ParamTy`. - checker.search_first_local_ty = true; - let local_ty = match trait_ref.visit_with(&mut checker).break_value() { - Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), - _ => None, - }; - Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { - uncovered: ty, - local_ty, - })) - } - OrphanCheckEarlyExit::LocalTy(_) => Ok(()), - }, - }) -} - -struct OrphanChecker<'a, 'tcx, F> { - infcx: &'a InferCtxt<'tcx>, - in_crate: InCrate, - in_self_ty: bool, - lazily_normalize_ty: F, - /// Ignore orphan check failures and exclusively search for the first local type. - search_first_local_ty: bool, - non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>, -} - -impl<'a, 'tcx, F, E> OrphanChecker<'a, 'tcx, F> -where - F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -{ - fn new(infcx: &'a InferCtxt<'tcx>, in_crate: InCrate, lazily_normalize_ty: F) -> Self { - OrphanChecker { - infcx, - in_crate, - in_self_ty: true, - lazily_normalize_ty, - search_first_local_ty: false, - non_local_tys: Vec::new(), - } - } - - fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { - self.non_local_tys.push((t, self.in_self_ty.into())); - ControlFlow::Continue(()) - } - - fn found_uncovered_ty_param( - &mut self, - ty: Ty<'tcx>, - ) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { - if self.search_first_local_ty { - return ControlFlow::Continue(()); - } - - ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) - } - - fn def_id_is_local(&mut self, def_id: DefId) -> bool { - match self.in_crate { - InCrate::Local { .. } => def_id.is_local(), - InCrate::Remote => false, - } - } -} - -enum OrphanCheckEarlyExit<'tcx, E> { - NormalizationFailure(E), - UncoveredTyParam(Ty<'tcx>), - LocalTy(Ty<'tcx>), -} - -impl<'a, 'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'a, 'tcx, F> -where - F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -{ - type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>; - - fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result { - ControlFlow::Continue(()) - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - let ty = self.infcx.shallow_resolve(ty); - let ty = match (self.lazily_normalize_ty)(ty) { - Ok(norm_ty) if norm_ty.is_ty_var() => ty, - Ok(norm_ty) => norm_ty, - Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), - }; - - let result = match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::FnDef(..) - | ty::Pat(..) - | ty::FnPtr(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Never - | ty::Tuple(..) => self.found_non_local_ty(ty), - - ty::Param(..) => bug!("unexpected ty param"), - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { - match self.in_crate { - InCrate::Local { .. } => self.found_uncovered_ty_param(ty), - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - } - } - - // A rigid alias may normalize to anything. - // * If it references an infer var, placeholder or bound ty, it may - // normalize to that, so we have to treat it as an uncovered ty param. - // * Otherwise it may normalize to any non-type-generic type - // be it local or non-local. - ty::Alias(kind, _) => { - if ty.has_type_flags( - ty::TypeFlags::HAS_TY_PLACEHOLDER - | ty::TypeFlags::HAS_TY_BOUND - | ty::TypeFlags::HAS_TY_INFER, - ) { - match self.in_crate { - InCrate::Local { mode } => match kind { - ty::Projection if let OrphanCheckMode::Compat = mode => { - ControlFlow::Continue(()) - } - _ => self.found_uncovered_ty_param(ty), - }, - InCrate::Remote => { - // The inference variable might be unified with a local - // type in that remote crate. - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } - } - } else { - // Regarding *opaque types* specifically, we choose to treat them as non-local, - // even those that appear within the same crate. This seems somewhat surprising - // at first, but makes sense when you consider that opaque types are supposed - // to hide the underlying type *within the same crate*. When an opaque type is - // used from outside the module where it is declared, it should be impossible to - // observe anything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type to determine whether - // or not the opaque type itself should be considered local. - // - // However, this could make it a breaking change to switch the underlying hidden - // type from a local type to a remote type. This would violate the rule that - // opaque types should be completely opaque apart from the traits that they - // implement, so we don't use this behavior. - // Addendum: Moreover, revealing the underlying type is likely to cause cycle - // errors as we rely on coherence / the specialization graph during typeck. - - self.found_non_local_ty(ty) - } - } - - // For fundamental types, we just look inside of them. - ty::Ref(_, ty, _) => ty.visit_with(self), - ty::Adt(def, args) => { - if self.def_id_is_local(def.did()) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else if def.is_fundamental() { - args.visit_with(self) - } else { - self.found_non_local_ty(ty) - } - } - ty::Foreign(def_id) => { - if self.def_id_is_local(def_id) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - ty::Dynamic(tt, ..) => { - let principal = tt.principal().map(|p| p.def_id()); - if principal.is_some_and(|p| self.def_id_is_local(p)) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => { - if self.def_id_is_local(did) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - // This should only be created when checking whether we have to check whether some - // auto trait impl applies. There will never be multiple impls, so we can just - // act as if it were a local type here. - ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - }; - // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so - // the first type we visit is always the self type. - self.in_self_ty = false; - result - } - - /// All possible values for a constant parameter already exist - /// in the crate defining the trait, so they are always non-local[^1]. - /// - /// Because there's no way to have an impl where the first local - /// generic argument is a constant, we also don't have to fail - /// the orphan check when encountering a parameter or a generic constant. - /// - /// This means that we can completely ignore constants during the orphan check. - /// - /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. - /// - /// [^1]: This might not hold for function pointers or trait objects in the future. - /// As these should be quite rare as const arguments and especially rare as impl - /// parameters, allowing uncovered const parameters in impls seems more useful - /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. - fn visit_const(&mut self, _c: ty::Const<'tcx>) -> Self::Result { - ControlFlow::Continue(()) - } -} - /// Compute the `intercrate_ambiguity_causes` for the new solver using /// "proof trees". /// diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 811f61d2bf3..bdc27e734f9 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -3,10 +3,10 @@ use std::fmt::Debug; use super::{FromSolverError, TraitEngine}; use super::{FulfillmentContext, ScrubbedTraitError}; +use crate::error_reporting::traits::TypeErrCtxtExt; use crate::regions::InferCtxtRegionExt; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; use crate::solve::NextSolverError; -use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::fulfill::OldSolverError; use crate::traits::NormalizeExt; use crate::traits::StructurallyNormalizeExt; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs deleted file mode 100644 index e6cb28df593..00000000000 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ /dev/null @@ -1,136 +0,0 @@ -use rustc_hir::def_id::DefId; -use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; -use rustc_infer::traits::util::elaborate; -use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; -use rustc_middle::ty; -use rustc_span::{Span, DUMMY_SP}; - -use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::ObligationCtxt; - -#[derive(Debug)] -pub enum CandidateSource { - DefId(DefId), - ParamEnv(Span), -} - -pub fn compute_applicable_impls_for_diagnostics<'tcx>( - infcx: &InferCtxt<'tcx>, - obligation: &PolyTraitObligation<'tcx>, -) -> Vec<CandidateSource> { - let tcx = infcx.tcx; - let param_env = obligation.param_env; - - let predicate_polarity = obligation.predicate.skip_binder().polarity; - - let impl_may_apply = |impl_def_id| { - let ocx = ObligationCtxt::new(infcx); - infcx.enter_forall(obligation.predicate, |placeholder_obligation| { - let obligation_trait_ref = ocx.normalize( - &ObligationCause::dummy(), - param_env, - placeholder_obligation.trait_ref, - ); - - let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = - tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args); - let impl_trait_ref = - ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); - - if let Err(_) = - ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref) - { - return false; - } - - let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); - let impl_polarity = impl_trait_header.polarity; - - match (impl_polarity, predicate_polarity) { - (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) - | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {} - _ => return false, - } - - let obligations = tcx - .predicates_of(impl_def_id) - .instantiate(tcx, impl_args) - .into_iter() - .map(|(predicate, _)| { - Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) - }) - // Kinda hacky, but let's just throw away obligations that overflow. - // This may reduce the accuracy of this check (if the obligation guides - // inference or it actually resulted in error after others are processed) - // ... but this is diagnostics code. - .filter(|obligation| { - infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok() - }); - ocx.register_obligations(obligations); - - ocx.select_where_possible().is_empty() - }) - }; - - let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { - let ocx = ObligationCtxt::new(infcx); - infcx.enter_forall(obligation.predicate, |placeholder_obligation| { - let obligation_trait_ref = ocx.normalize( - &ObligationCause::dummy(), - param_env, - placeholder_obligation.trait_ref, - ); - - let param_env_predicate = infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - poly_trait_predicate, - ); - let param_env_trait_ref = - ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref); - - if let Err(_) = ocx.eq( - &ObligationCause::dummy(), - param_env, - obligation_trait_ref, - param_env_trait_ref, - ) { - return false; - } - - ocx.select_where_possible().is_empty() - }) - }; - - let mut ambiguities = Vec::new(); - - tcx.for_each_relevant_impl( - obligation.predicate.def_id(), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - if infcx.probe(|_| impl_may_apply(impl_def_id)) { - ambiguities.push(CandidateSource::DefId(impl_def_id)) - } - }, - ); - - let predicates = - tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); - for (pred, span) in elaborate(tcx, predicates.into_iter()) { - let kind = pred.kind(); - if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() - && param_env_candidate_may_apply(kind.rebind(trait_pred)) - { - if kind.rebind(trait_pred.trait_ref) - == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) - { - ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) - } else { - ambiguities.push(CandidateSource::ParamEnv(span)) - } - } - } - - ambiguities -} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs deleted file mode 100644 index 633d488f7be..00000000000 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ /dev/null @@ -1,182 +0,0 @@ -// ignore-tidy-filelength :( - -pub mod ambiguity; -mod infer_ctxt_ext; -pub mod on_unimplemented; -pub mod suggestions; -mod type_err_ctxt_ext; - -use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::Span; -use std::ops::ControlFlow; - -pub use self::infer_ctxt_ext::*; -pub use self::type_err_ctxt_ext::*; - -// When outputting impl candidates, prefer showing those that are more similar. -// -// We also compare candidates after skipping lifetimes, which has a lower -// priority than exact matches. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum CandidateSimilarity { - Exact { ignoring_lifetimes: bool }, - Fuzzy { ignoring_lifetimes: bool }, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ImplCandidate<'tcx> { - pub trait_ref: ty::TraitRef<'tcx>, - pub similarity: CandidateSimilarity, - impl_def_id: DefId, -} - -enum GetSafeTransmuteErrorAndReason { - Silent, - Error { err_msg: String, safe_transmute_explanation: Option<String> }, -} - -struct UnsatisfiedConst(pub bool); - -/// Crude way of getting back an `Expr` from a `Span`. -pub struct FindExprBySpan<'hir> { - pub span: Span, - pub result: Option<&'hir hir::Expr<'hir>>, - pub ty_result: Option<&'hir hir::Ty<'hir>>, - pub include_closures: bool, - pub tcx: TyCtxt<'hir>, -} - -impl<'hir> FindExprBySpan<'hir> { - pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self { - Self { span, result: None, ty_result: None, tcx, include_closures: false } - } -} - -impl<'v> Visitor<'v> for FindExprBySpan<'v> { - type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - if self.span == ex.span { - self.result = Some(ex); - } else { - if let hir::ExprKind::Closure(..) = ex.kind - && self.include_closures - && let closure_header_sp = self.span.with_hi(ex.span.hi()) - && closure_header_sp == ex.span - { - self.result = Some(ex); - } - hir::intravisit::walk_expr(self, ex); - } - } - - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - if self.span == ty.span { - self.ty_result = Some(ty); - } else { - hir::intravisit::walk_ty(self, ty); - } - } -} - -/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting -/// `param: ?Sized` would be a valid constraint. -struct FindTypeParam { - param: rustc_span::Symbol, - invalid_spans: Vec<Span>, - nested: bool, -} - -impl<'v> Visitor<'v> for FindTypeParam { - fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { - // Skip where-clauses, to avoid suggesting indirection for type parameters found there. - } - - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // We collect the spans of all uses of the "bare" type param, like in `field: T` or - // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be - // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` - // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases - // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors - // in that case should make what happened clear enough. - match ty.kind { - hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} - hir::TyKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].ident.name == self.param => - { - if !self.nested { - debug!(?ty, "FindTypeParam::visit_ty"); - self.invalid_spans.push(ty.span); - } - } - hir::TyKind::Path(_) => { - let prev = self.nested; - self.nested = true; - hir::intravisit::walk_ty(self, ty); - self.nested = prev; - } - _ => { - hir::intravisit::walk_ty(self, ty); - } - } - } -} - -/// Summarizes information -#[derive(Clone)] -pub enum ArgKind { - /// An argument of non-tuple type. Parameters are (name, ty) - Arg(String, String), - - /// An argument of tuple type. For a "found" argument, the span is - /// the location in the source of the pattern. For an "expected" - /// argument, it will be None. The vector is a list of (name, ty) - /// strings for the components of the tuple. - Tuple(Option<Span>, Vec<(String, String)>), -} - -impl ArgKind { - fn empty() -> ArgKind { - ArgKind::Arg("_".to_owned(), "_".to_owned()) - } - - /// Creates an `ArgKind` from the expected type of an - /// argument. It has no name (`_`) and an optional source span. - pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind { - match t.kind() { - ty::Tuple(tys) => ArgKind::Tuple( - span, - tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(), - ), - _ => ArgKind::Arg("_".to_owned(), t.to_string()), - } - } -} - -struct HasNumericInferVisitor; - -impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } -} - -#[derive(Copy, Clone)] -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index bce5c7101cc..5597c8be592 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,5 +1,5 @@ +use crate::error_reporting::traits::TypeErrCtxtOverflowExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; -use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::normalize_with_depth_to; use rustc_data_structures::captures::Captures; use rustc_data_structures::obligation_forest::ProcessResult; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 662d95db8ba..703ff2f7f16 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -6,7 +6,6 @@ pub mod auto_trait; pub(crate) mod coherence; pub mod const_evaluatable; mod engine; -pub mod error_reporting; mod fulfill; pub mod misc; pub mod normalize; @@ -24,10 +23,10 @@ mod util; pub mod vtable; pub mod wf; +use crate::error_reporting::traits::TypeErrCtxtExt as _; use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::regions::InferCtxtRegionExt; -use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_middle::query::Providers; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index a9ac0f7eb25..dda3aaaf71e 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -1,9 +1,9 @@ //! Deeply normalize types using the old trait solver. -use super::error_reporting::OverflowCause; -use super::error_reporting::TypeErrCtxtExt; use super::SelectionContext; use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::traits::TypeErrCtxtOverflowExt; use crate::solve::NextSolverError; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 4d10d33fa6e..1c6993bdd37 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -187,7 +187,7 @@ fn predicates_reference_self( ) -> SmallVec<[Span; 1]> { let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); let predicates = if supertraits_only { - tcx.super_predicates_of(trait_def_id) + tcx.explicit_super_predicates_of(trait_def_id) } else { tcx.predicates_of(trait_def_id) }; @@ -203,7 +203,7 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span tcx.associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) - .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied()) + .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) .filter_map(|c| predicate_references_self(tcx, c)) .collect() } @@ -256,7 +256,7 @@ fn super_predicates_have_non_lifetime_binders( if !tcx.features().non_lifetime_binders { return SmallVec::new(); } - tcx.super_predicates_of(trait_def_id) + tcx.explicit_super_predicates_of(trait_def_id) .predicates .iter() .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span)) @@ -805,10 +805,11 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( .unwrap() .contains(&data.trait_ref(self.tcx).def_id); + // only walk contained types if it's not a super trait if is_supertrait_of_current_trait { - ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200 + ControlFlow::Continue(()) } else { - t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error + t.super_visit_with(self) // POSSIBLY reporting an error } } _ => t.super_visit_with(self), // walk contained types, if any diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index bed76b84ee0..c11e86abef8 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -2,11 +2,11 @@ //! which folds deeply, invoking the underlying //! `normalize_canonicalized_projection_ty` query when it encounters projections. +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::traits::TypeErrCtxtOverflowExt; use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::error_reporting::OverflowCause; -use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::needs_normalization; use crate::traits::Normalized; use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index b38841db923..8525215a3bc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -3,7 +3,6 @@ use crate::traits::wf; use crate::traits::ObligationCtxt; use rustc_infer::infer::canonical::Canonical; -use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; @@ -12,6 +11,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::DUMMY_SP; +use rustc_type_ir::outlives::{push_outlives_components, Component}; use smallvec::{smallvec, SmallVec}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] @@ -284,7 +284,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( /// those relationships. fn implied_bounds_from_components<'tcx>( sub_region: ty::Region<'tcx>, - sup_components: SmallVec<[Component<'tcx>; 4]>, + sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>, ) -> Vec<OutlivesBound<'tcx>> { sup_components .into_iter() diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e36a9ca8bd1..4c3d833b0f9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -571,7 +571,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + let drcx = DeepRejectCtxt::new(self.tcx(), TreatParams::ForLookup); let obligation_args = obligation.predicate.skip_binder().trait_ref.args; self.tcx().for_each_relevant_impl( obligation.predicate.def_id(), diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 8c5dc88184c..9508a3e8e15 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -574,7 +574,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Check supertraits hold. This is so that their associated type bounds // will be checked in the code below. for super_trait in tcx - .super_predicates_of(trait_predicate.def_id()) + .explicit_super_predicates_of(trait_predicate.def_id()) .instantiate(tcx, trait_predicate.trait_ref.args) .predicates .into_iter() diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 68cc04bc8e6..2c4ffdabf14 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -18,9 +18,9 @@ use super::{ TraitQueryMode, }; -use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::error_reporting::traits::TypeErrCtxtOverflowExt; +use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; -use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::normalize_with_depth; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; @@ -1523,7 +1523,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // bound regions. let trait_ref = predicate.skip_binder().trait_ref; - coherence::trait_ref_is_knowable::<!>(self.infcx, trait_ref, |ty| Ok(ty)).unwrap() + coherence::trait_ref_is_knowable(self.infcx, trait_ref, |ty| Ok::<_, !>(ty)).into_ok() } /// Returns `true` if the global caches can be used. diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index c9bb0d330e1..6a904ef487e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -14,6 +14,7 @@ use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::print::PrintTraitRefExt as _; use specialization_graph::GraphExt; +use crate::error_reporting::traits::to_pretty_impl_header; use crate::errors::NegativePositiveConflict; use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; @@ -25,8 +26,8 @@ use rustc_errors::{codes::*, Diag, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; use rustc_middle::query::LocalCrate; +use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP}; @@ -485,61 +486,3 @@ fn report_conflicting_impls<'tcx>( } } } - -/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a -/// string. -pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { - use std::fmt::Write; - - let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity(); - let mut w = "impl".to_owned(); - - let args = GenericArgs::identity_for_item(tcx, impl_def_id); - - // FIXME: Currently only handles ?Sized. - // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxIndexSet::default(); - let sized_trait = tcx.lang_items().sized_trait(); - - let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>(); - if !arg_names.is_empty() { - types_without_default_bounds.extend(args.types()); - w.push('<'); - w.push_str(&arg_names.join(", ")); - w.push('>'); - } - - write!( - w, - " {} for {}", - trait_ref.print_only_trait_path(), - tcx.type_of(impl_def_id).instantiate_identity() - ) - .unwrap(); - - // The predicates will contain default bounds like `T: Sized`. We need to - // remove these bounds, and add `T: ?Sized` to any untouched type parameters. - let predicates = tcx.predicates_of(impl_def_id).predicates; - let mut pretty_predicates = - Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); - - for (p, _) in predicates { - if let Some(poly_trait_ref) = p.as_trait_clause() { - if Some(poly_trait_ref.def_id()) == sized_trait { - // FIXME(#120456) - is `swap_remove` correct? - types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder()); - continue; - } - } - pretty_predicates.push(p.to_string()); - } - - pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized"))); - - if !pretty_predicates.is_empty() { - write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); - } - - w.push(';'); - Some(w) -} diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index f132e36468a..951af4b0920 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -128,7 +128,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { } // Get components of trait alias. - let predicates = tcx.super_predicates_of(trait_ref.def_id()); + let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 017b0a45d1f..8f56f9c0f3e 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -6,7 +6,7 @@ use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry}; use rustc_middle::ty::{GenericArgs, TypeVisitableExt}; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::fmt::Debug; @@ -117,7 +117,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); let mut direct_super_traits_iter = tcx - .super_predicates_of(inner_most_trait_ref.def_id()) + .explicit_super_predicates_of(inner_most_trait_ref.def_id()) .predicates .into_iter() .filter_map(move |(pred, _)| { @@ -285,13 +285,14 @@ fn vtable_entries<'tcx>( return VtblEntry::Vacant; } - let instance = ty::Instance::resolve_for_vtable( + let instance = ty::Instance::expect_resolve_for_vtable( tcx, ty::ParamEnv::reveal_all(), def_id, args, - ) - .expect("resolution failed during building vtable representation"); + DUMMY_SP, + ); + VtblEntry::Method(instance) }); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 066755f7b3e..f071dc6c784 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -437,12 +437,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes the obligations required for an alias (except inherent) to be WF /// into `self.out`. - fn compute_alias_ty(&mut self, data: ty::AliasTy<'tcx>) { - self.compute_alias_term(data.into()); - } - - /// Pushes the obligations required for an alias (except inherent) to be WF - /// into `self.out`. fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { // A projection is well-formed if // @@ -498,7 +492,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.extend(obligations); } - self.compute_projection_args(data.args); + data.args.visit_with(self); } fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) { @@ -646,8 +640,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { - type Result = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); @@ -702,8 +694,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { } ty::Alias(ty::Projection | ty::Opaque | ty::Weak, data) => { - self.compute_alias_ty(data); - return; // Subtree handled by compute_projection. + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); } ty::Alias(ty::Inherent, data) => { self.compute_inherent_projection(data); diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index c73ececd1d1..3ee5fd876ff 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -7,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt; use rustc_trait_selection::traits::{ ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext, Unimplemented, diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 0430e0bb70e..2d70fdc3935 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -2,8 +2,8 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::{ normalize::NormalizationResult, CanonicalAliasGoal, NoSolution, }; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f1dd94839fe..f078cfe1b25 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -127,9 +127,9 @@ fn fn_sig_for_fn_abi<'tcx>( coroutine_kind = ty::ClosureKind::FnOnce; // Implementations of `FnMut` and `Fn` for coroutine-closures - // still take their receiver by (mut) ref. + // still take their receiver by ref. if receiver_by_ref { - Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty) + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty) } else { coroutine_ty } diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 4395eb57cd6..6726db8bb54 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -4,8 +4,9 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; +use rustc_span::sym; use rustc_span::symbol::kw; pub(crate) fn provide(providers: &mut Providers) { @@ -14,6 +15,7 @@ pub(crate) fn provide(providers: &mut Providers) { associated_item_def_ids, associated_items, associated_types_for_impl_traits_in_associated_fn, + associated_type_for_effects, associated_type_for_impl_trait_in_trait, impl_item_implementor_ids, ..*providers @@ -44,7 +46,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { ) }) .copied(), - ), + ) + .chain(tcx.associated_type_for_effects(def_id)), ) } hir::ItemKind::Impl(impl_) => { @@ -70,7 +73,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { ) }) .copied() - })), + })) + .chain(tcx.associated_type_for_effects(def_id)), ) } _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), @@ -143,6 +147,7 @@ fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty container: ty::TraitContainer, fn_has_self_parameter: has_self, opt_rpitit_info: None, + is_effects_desugaring: false, } } @@ -162,9 +167,138 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A container: ty::ImplContainer, fn_has_self_parameter: has_self, opt_rpitit_info: None, + is_effects_desugaring: false, } } +/// Given an `def_id` of a trait or a trait impl: +/// +/// If `def_id` is a trait that has `#[const_trait]`, then it synthesizes +/// a new def id corresponding to a new associated type for the effects. +/// +/// If `def_id` is an impl, then synthesize the associated type according +/// to the constness of the impl. +fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { + // don't synthesize the associated type even if the user has written `const_trait` + // if the effects feature is disabled. + if !tcx.features().effects { + return None; + } + let (feed, parent_did) = match tcx.def_kind(def_id) { + DefKind::Trait => { + let trait_def_id = def_id; + let attr = tcx.get_attr(def_id, sym::const_trait)?; + + let span = attr.span; + let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy); + + let local_def_id = trait_assoc_ty.def_id(); + let def_id = local_def_id.to_def_id(); + + // Copy span of the attribute. + trait_assoc_ty.def_ident_span(Some(span)); + + trait_assoc_ty.associated_item(ty::AssocItem { + name: kw::Empty, + kind: ty::AssocKind::Type, + def_id, + trait_item_def_id: None, + container: ty::TraitContainer, + fn_has_self_parameter: false, + opt_rpitit_info: None, + is_effects_desugaring: true, + }); + + // No default type + trait_assoc_ty.defaultness(hir::Defaultness::Default { has_value: false }); + + trait_assoc_ty.is_type_alias_impl_trait(false); + + (trait_assoc_ty, trait_def_id) + } + DefKind::Impl { .. } => { + let impl_def_id = def_id; + let trait_id = tcx.trait_id_of_impl(def_id.to_def_id())?; + + // first get the DefId of the assoc type on the trait, if there is not, + // then we don't need to generate it on the impl. + let trait_assoc_id = tcx.associated_type_for_effects(trait_id)?; + + // FIXME(effects): span + let span = tcx.def_ident_span(def_id).unwrap(); + + let impl_assoc_ty = tcx.at(span).create_def(def_id, kw::Empty, DefKind::AssocTy); + + let local_def_id = impl_assoc_ty.def_id(); + let def_id = local_def_id.to_def_id(); + + impl_assoc_ty.def_ident_span(Some(span)); + + impl_assoc_ty.associated_item(ty::AssocItem { + name: kw::Empty, + kind: ty::AssocKind::Type, + def_id, + trait_item_def_id: Some(trait_assoc_id), + container: ty::ImplContainer, + fn_has_self_parameter: false, + opt_rpitit_info: None, + is_effects_desugaring: true, + }); + + // no default value. + impl_assoc_ty.defaultness(hir::Defaultness::Final); + + // set the type of the associated type! If this is a const impl, + // we set to Maybe, otherwise we set to `Runtime`. + let type_def_id = if tcx.is_const_trait_impl_raw(impl_def_id.to_def_id()) { + tcx.require_lang_item(hir::LangItem::EffectsMaybe, Some(span)) + } else { + tcx.require_lang_item(hir::LangItem::EffectsRuntime, Some(span)) + }; + // FIXME(effects): make impls use `Min` for their effect types + impl_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_adt( + tcx, + tcx.adt_def(type_def_id), + ty::GenericArgs::empty(), + ))); + + (impl_assoc_ty, impl_def_id) + } + def_kind => bug!( + "associated_type_for_effects: {:?} should be Trait or Impl but is {:?}", + def_id, + def_kind + ), + }; + + feed.feed_hir(); + + // visibility is public. + feed.visibility(ty::Visibility::Public); + + // Copy generics_of of the trait/impl, making the trait/impl as parent. + feed.generics_of({ + let parent_generics = tcx.generics_of(parent_did); + let parent_count = parent_generics.parent_count + parent_generics.own_params.len(); + + ty::Generics { + parent: Some(parent_did.to_def_id()), + parent_count, + own_params: vec![], + param_def_id_to_index: parent_generics.param_def_id_to_index.clone(), + has_self: false, + has_late_bound_regions: None, + host_effect_index: parent_generics.host_effect_index, + } + }); + feed.explicit_item_super_predicates(ty::EarlyBinder::bind(&[])); + + // There are no inferred outlives for the synthesized associated type. + feed.inferred_outlives_of(&[]); + + Some(feed.def_id().to_def_id()) +} + /// Given an `fn_def_id` of a trait or a trait implementation: /// /// if `fn_def_id` is a function defined inside a trait, then it synthesizes @@ -275,6 +409,7 @@ fn associated_type_for_impl_trait_in_trait( fn_def_id: fn_def_id.to_def_id(), opaque_def_id: opaque_ty_def_id.to_def_id(), }), + is_effects_desugaring: false, }); // Copy visility of the containing function. @@ -326,6 +461,7 @@ fn associated_type_for_impl_trait_in_impl( container: ty::ImplContainer, fn_has_self_parameter: false, opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }), + is_effects_desugaring: false, }); // Copy visility of the containing function. diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index c50a490a9dc..7b6d86d22a5 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -16,7 +16,7 @@ use traits::{translate_args, Reveal}; use crate::errors::UnexpectedFnPtrAssociatedItem; -fn resolve_instance<'tcx>( +fn resolve_instance_raw<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, (DefId, GenericArgsRef<'tcx>)>, ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> { @@ -364,5 +364,5 @@ fn resolve_associated_item<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { resolve_instance, ..*providers }; + *providers = Providers { resolve_instance_raw, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 686f2f04ad9..5e91320f897 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -169,10 +169,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { // Collect opaque types nested within the associated type bounds of this opaque type. // We use identity args here, because we already know that the opaque type uses // only generic parameters, and thus instantiating would not give us more information. - for (pred, span) in self - .tcx - .explicit_item_bounds(alias_ty.def_id) - .instantiate_identity_iter_copied() + for (pred, span) in + self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied() { trace!(?pred); self.visit_spanned(span, pred); diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 97a1b94263e..eb6cb369974 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -62,7 +62,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( } } DefKind::OpaqueTy => { - for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() { + for (pred, span) in tcx.explicit_item_bounds(item).iter_identity_copied() { try_visit!(visitor.visit(span, pred)); } } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 491ef34430c..2531219baec 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -448,7 +448,7 @@ where /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), /// but on an iterator of `TypeFoldable` values. - pub fn instantiate_identity_iter(self) -> Iter::IntoIter { + pub fn iter_identity(self) -> Iter::IntoIter { self.value.into_iter() } } @@ -515,9 +515,7 @@ where /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), /// but on an iterator of values that deref to a `TypeFoldable`. - pub fn instantiate_identity_iter_copied( - self, - ) -> impl Iterator<Item = <Iter::Item as Deref>::Target> { + pub fn iter_identity_copied(self) -> impl Iterator<Item = <Iter::Item as Deref>::Target> { self.value.into_iter().map(|v| *v) } } diff --git a/compiler/rustc_type_ir/src/effects.rs b/compiler/rustc_type_ir/src/effects.rs new file mode 100644 index 00000000000..f7942f2f982 --- /dev/null +++ b/compiler/rustc_type_ir/src/effects.rs @@ -0,0 +1,59 @@ +use crate::inherent::{AdtDef, IntoKind, Ty}; +use crate::lang_items::TraitSolverLangItem::{EffectsMaybe, EffectsNoRuntime, EffectsRuntime}; +use crate::Interner; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum EffectKind { + Maybe, + Runtime, + NoRuntime, +} + +impl EffectKind { + pub fn try_from_def_id<I: Interner>(tcx: I, def_id: I::DefId) -> Option<EffectKind> { + if tcx.is_lang_item(def_id, EffectsMaybe) { + Some(EffectKind::Maybe) + } else if tcx.is_lang_item(def_id, EffectsRuntime) { + Some(EffectKind::Runtime) + } else if tcx.is_lang_item(def_id, EffectsNoRuntime) { + Some(EffectKind::NoRuntime) + } else { + None + } + } + + pub fn to_def_id<I: Interner>(self, tcx: I) -> I::DefId { + let lang_item = match self { + EffectKind::Maybe => EffectsMaybe, + EffectKind::NoRuntime => EffectsNoRuntime, + EffectKind::Runtime => EffectsRuntime, + }; + + tcx.require_lang_item(lang_item) + } + + pub fn try_from_ty<I: Interner>(tcx: I, ty: I::Ty) -> Option<EffectKind> { + if let crate::Adt(def, _) = ty.kind() { + Self::try_from_def_id(tcx, def.def_id()) + } else { + None + } + } + + pub fn to_ty<I: Interner>(self, tcx: I) -> I::Ty { + I::Ty::new_adt(tcx, tcx.adt_def(self.to_def_id(tcx)), Default::default()) + } + + /// Returns an intersection between two effect kinds. If one effect kind + /// is more permissive than the other (e.g. `Maybe` vs `Runtime`), this + /// returns the less permissive effect kind (`Runtime`). + pub fn intersection(a: Self, b: Self) -> Option<Self> { + use EffectKind::*; + match (a, b) { + (Maybe, x) | (x, Maybe) => Some(x), + (Runtime, Runtime) => Some(Runtime), + (NoRuntime, NoRuntime) => Some(NoRuntime), + (Runtime, NoRuntime) | (NoRuntime, Runtime) => None, + } + } +} diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs new file mode 100644 index 00000000000..0def7d12f74 --- /dev/null +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -0,0 +1,305 @@ +use std::marker::PhantomData; + +use smallvec::smallvec; + +use crate::data_structures::HashSet; +use crate::outlives::{push_outlives_components, Component}; +use crate::{self as ty, Interner}; +use crate::{inherent::*, Upcast as _}; + +/// "Elaboration" is the process of identifying all the predicates that +/// are implied by a source predicate. Currently, this basically means +/// walking the "supertraits" and other similar assumptions. For example, +/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` +/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that +/// `T: Foo`, then we know that `T: 'static`. +pub struct Elaborator<I: Interner, O> { + cx: I, + stack: Vec<O>, + visited: HashSet<ty::Binder<I, ty::PredicateKind<I>>>, + mode: Filter, +} + +enum Filter { + All, + OnlySelf, +} + +/// Describes how to elaborate an obligation into a sub-obligation. +pub trait Elaboratable<I: Interner> { + fn predicate(&self) -> I::Predicate; + + // Makes a new `Self` but with a different clause that comes from elaboration. + fn child(&self, clause: I::Clause) -> Self; + + // Makes a new `Self` but with a different clause and a different cause + // code (if `Self` has one, such as [`PredicateObligation`]). + fn child_with_derived_cause( + &self, + clause: I::Clause, + span: I::Span, + parent_trait_pred: ty::Binder<I, ty::TraitPredicate<I>>, + index: usize, + ) -> Self; +} + +pub fn elaborate<I: Interner, O: Elaboratable<I>>( + cx: I, + obligations: impl IntoIterator<Item = O>, +) -> Elaborator<I, O> { + let mut elaborator = + Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All }; + elaborator.extend_deduped(obligations); + elaborator +} + +impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { + fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) { + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + self.stack.extend( + obligations.into_iter().filter(|o| { + self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind())) + }), + ); + } + + /// Filter to only the supertraits of trait predicates, i.e. only the predicates + /// that have `Self` as their self type, instead of all implied predicates. + pub fn filter_only_self(mut self) -> Self { + self.mode = Filter::OnlySelf; + self + } + + fn elaborate(&mut self, elaboratable: &O) { + let cx = self.cx; + + // We only elaborate clauses. + let Some(clause) = elaboratable.predicate().as_clause() else { + return; + }; + + let bound_clause = clause.kind(); + match bound_clause.skip_binder() { + ty::ClauseKind::Trait(data) => { + // Negative trait bounds do not imply any supertrait bounds + if data.polarity != ty::PredicatePolarity::Positive { + return; + } + + let map_to_child_clause = + |(index, (clause, span)): (usize, (I::Clause, I::Span))| { + elaboratable.child_with_derived_cause( + clause.instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), + span, + bound_clause.rebind(data), + index, + ) + }; + + // Get predicates implied by the trait, or only super predicates if we only care about self predicates. + match self.mode { + Filter::All => self.extend_deduped( + cx.explicit_implied_predicates_of(data.def_id()) + .iter_identity() + .enumerate() + .map(map_to_child_clause), + ), + Filter::OnlySelf => self.extend_deduped( + cx.explicit_super_predicates_of(data.def_id()) + .iter_identity() + .enumerate() + .map(map_to_child_clause), + ), + }; + } + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. + // + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + if r_min.is_bound() { + return; + } + + let mut components = smallvec![]; + push_outlives_components(cx, ty_max, &mut components); + self.extend_deduped( + components + .into_iter() + .filter_map(|component| elaborate_component_to_clause(cx, component, r_min)) + .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(cx))), + ); + } + ty::ClauseKind::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + ty::ClauseKind::WellFormed(..) => { + // Currently, we do not elaborate WF predicates, + // although we easily could. + } + ty::ClauseKind::Projection(..) => { + // Nothing to elaborate in a projection predicate. + } + ty::ClauseKind::ConstEvaluatable(..) => { + // Currently, we do not elaborate const-evaluatable + // predicates. + } + ty::ClauseKind::ConstArgHasType(..) => { + // Nothing to elaborate + } + } + } +} + +fn elaborate_component_to_clause<I: Interner>( + cx: I, + component: Component<I>, + outlives_region: I::Region, +) -> Option<ty::ClauseKind<I>> { + match component { + Component::Region(r) => { + if r.is_bound() { + None + } else { + Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r, outlives_region))) + } + } + + Component::Param(p) => { + let ty = Ty::new_param(cx, p); + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + } + + Component::Placeholder(p) => { + let ty = Ty::new_placeholder(cx, p); + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + } + + Component::UnresolvedInferenceVariable(_) => None, + + Component::Alias(alias_ty) => { + // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. + // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + alias_ty.to_ty(cx), + outlives_region, + ))) + } + + Component::EscapingAlias(_) => { + // We might be able to do more here, but we don't + // want to deal with escaping vars right now. + None + } + } +} + +impl<I: Interner, O: Elaboratable<I>> Iterator for Elaborator<I, O> { + type Item = O; + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option<Self::Item> { + // Extract next item from top-most stack frame, if any. + if let Some(obligation) = self.stack.pop() { + self.elaborate(&obligation); + Some(obligation) + } else { + None + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +/// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally) +/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used +/// to identify which traits may define a given associated type to help avoid cycle errors, +/// and to make size estimates for vtable layout computation. +pub fn supertrait_def_ids<I: Interner>( + cx: I, + trait_def_id: I::DefId, +) -> impl Iterator<Item = I::DefId> { + let mut set = HashSet::default(); + let mut stack = vec![trait_def_id]; + + set.insert(trait_def_id); + + std::iter::from_fn(move || { + let trait_def_id = stack.pop()?; + + for (predicate, _) in cx.explicit_super_predicates_of(trait_def_id).iter_identity() { + if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() { + if set.insert(data.def_id()) { + stack.push(data.def_id()); + } + } + } + + Some(trait_def_id) + }) +} + +pub fn supertraits<I: Interner>( + tcx: I, + trait_ref: ty::Binder<I, ty::TraitRef<I>>, +) -> FilterToTraits<I, Elaborator<I, I::Clause>> { + elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits() +} + +pub fn transitive_bounds<I: Interner>( + tcx: I, + trait_refs: impl Iterator<Item = ty::Binder<I, ty::TraitRef<I>>>, +) -> FilterToTraits<I, Elaborator<I, I::Clause>> { + elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx))) + .filter_only_self() + .filter_to_traits() +} + +impl<I: Interner> Elaborator<I, I::Clause> { + fn filter_to_traits(self) -> FilterToTraits<I, Self> { + FilterToTraits { _cx: PhantomData, base_iterator: self } + } +} + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits<I: Interner, It: Iterator<Item = I::Clause>> { + _cx: PhantomData<I>, + base_iterator: It, +} + +impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I, It> { + type Item = ty::Binder<I, ty::TraitRef<I>>; + + fn next(&mut self) -> Option<ty::Binder<I, ty::TraitRef<I>>> { + while let Some(pred) = self.base_iterator.next() { + if let Some(data) = pred.as_trait_clause() { + return Some(data.map_bound(|t| t.trait_ref)); + } + } + None + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs new file mode 100644 index 00000000000..0810fa5c558 --- /dev/null +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -0,0 +1,397 @@ +use std::fmt::Debug; +use std::hash::Hash; +use std::iter; +use std::marker::PhantomData; + +use rustc_ast_ir::Mutability; +#[cfg(feature = "nightly")] +use rustc_data_structures::fingerprint::Fingerprint; +#[cfg(feature = "nightly")] +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +#[cfg(feature = "nightly")] +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; + +use crate::inherent::*; +use crate::visit::TypeVisitableExt as _; +use crate::{self as ty, Interner}; + +/// See `simplify_type`. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub enum SimplifiedType<DefId> { + Bool, + Char, + Int(ty::IntTy), + Uint(ty::UintTy), + Float(ty::FloatTy), + Adt(DefId), + Foreign(DefId), + Str, + Array, + Slice, + Ref(Mutability), + Ptr(Mutability), + Never, + Tuple(usize), + /// A trait object, all of whose components are markers + /// (e.g., `dyn Send + Sync`). + MarkerTraitObject, + Trait(DefId), + Closure(DefId), + Coroutine(DefId), + CoroutineWitness(DefId), + Function(usize), + Placeholder, + Error, +} + +#[cfg(feature = "nightly")] +impl<HCX: Clone, DefId: HashStable<HCX>> ToStableHashKey<HCX> for SimplifiedType<DefId> { + type KeyType = Fingerprint; + + #[inline] + fn to_stable_hash_key(&self, hcx: &HCX) -> Fingerprint { + let mut hasher = StableHasher::new(); + let mut hcx: HCX = hcx.clone(); + self.hash_stable(&mut hcx, &mut hasher); + hasher.finish() + } +} + +/// Generic parameters are pretty much just bound variables, e.g. +/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as +/// `for<'a, T> fn(&'a T) -> u32`. +/// +/// Typecheck of `foo` has to succeed for all possible generic arguments, so +/// during typeck, we have to treat its generic parameters as if they +/// were placeholders. +/// +/// But when calling `foo` we only have to provide a specific generic argument. +/// In that case the generic parameters are instantiated with inference variables. +/// As we use `simplify_type` before that instantiation happens, we just treat +/// generic parameters as if they were inference variables in that case. +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum TreatParams { + /// Treat parameters as infer vars. This is the correct mode for caching + /// an impl's type for lookup. + AsCandidateKey, + /// Treat parameters as placeholders in the given environment. This is the + /// correct mode for *lookup*, as during candidate selection. + /// + /// This also treats projections with inference variables as infer vars + /// since they could be further normalized. + ForLookup, +} + +/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. +/// +/// **This function should only be used if you need to store or retrieve the type from some +/// hashmap. If you want to quickly decide whether two types may unify, use the [DeepRejectCtxt] +/// instead.** +/// +/// The idea is to get something simple that we can use to quickly decide if two types could unify, +/// for example during method lookup. If this function returns `Some(x)` it can only unify with +/// types for which this method returns either `Some(x)` as well or `None`. +/// +/// A special case here are parameters and projections, which are only injective +/// if they are treated as placeholders. +/// +/// For example when storing impls based on their simplified self type, we treat +/// generic parameters as if they were inference variables. We must not simplify them here, +/// as they can unify with any other type. +/// +/// With projections we have to be even more careful, as treating them as placeholders +/// is only correct if they are fully normalized. +/// +/// ¹ meaning that if the outermost layers are different, then the whole types are also different. +pub fn simplify_type<I: Interner>( + tcx: I, + ty: I::Ty, + treat_params: TreatParams, +) -> Option<SimplifiedType<I::DefId>> { + match ty.kind() { + ty::Bool => Some(SimplifiedType::Bool), + ty::Char => Some(SimplifiedType::Char), + ty::Int(int_type) => Some(SimplifiedType::Int(int_type)), + ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)), + ty::Float(float_type) => Some(SimplifiedType::Float(float_type)), + ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id())), + ty::Str => Some(SimplifiedType::Str), + ty::Array(..) => Some(SimplifiedType::Array), + ty::Slice(..) => Some(SimplifiedType::Slice), + ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params), + ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), + ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { + Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { + Some(SimplifiedType::Trait(principal_def_id)) + } + _ => Some(SimplifiedType::MarkerTraitObject), + }, + ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)), + ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => { + Some(SimplifiedType::Closure(def_id)) + } + ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)), + ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)), + ty::Never => Some(SimplifiedType::Never), + ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), + ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), + ty::Placeholder(..) => Some(SimplifiedType::Placeholder), + ty::Param(_) => match treat_params { + TreatParams::ForLookup => Some(SimplifiedType::Placeholder), + TreatParams::AsCandidateKey => None, + }, + ty::Alias(..) => match treat_params { + // When treating `ty::Param` as a placeholder, projections also + // don't unify with anything else as long as they are fully normalized. + // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder` + // when the new solver is enabled by default. + TreatParams::ForLookup if !ty.has_non_region_infer() => { + Some(SimplifiedType::Placeholder) + } + TreatParams::ForLookup | TreatParams::AsCandidateKey => None, + }, + ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), + ty::Error(_) => Some(SimplifiedType::Error), + ty::Bound(..) | ty::Infer(_) => None, + } +} + +impl<DefId> SimplifiedType<DefId> { + pub fn def(self) -> Option<DefId> { + match self { + SimplifiedType::Adt(d) + | SimplifiedType::Foreign(d) + | SimplifiedType::Trait(d) + | SimplifiedType::Closure(d) + | SimplifiedType::Coroutine(d) + | SimplifiedType::CoroutineWitness(d) => Some(d), + _ => None, + } + } +} + +/// Given generic arguments from an obligation and an impl, +/// could these two be unified after replacing parameters in the +/// the impl with inference variables. +/// +/// For obligations, parameters won't be replaced by inference +/// variables and only unify with themselves. We treat them +/// the same way we treat placeholders. +/// +/// We also use this function during coherence. For coherence the +/// impls only have to overlap for some value, so we treat parameters +/// on both sides like inference variables. This behavior is toggled +/// using the `treat_obligation_params` field. +#[derive(Debug, Clone, Copy)] +pub struct DeepRejectCtxt<I: Interner> { + treat_obligation_params: TreatParams, + _interner: PhantomData<I>, +} + +impl<I: Interner> DeepRejectCtxt<I> { + pub fn new(_interner: I, treat_obligation_params: TreatParams) -> Self { + DeepRejectCtxt { treat_obligation_params, _interner: PhantomData } + } + + pub fn args_may_unify( + self, + obligation_args: I::GenericArgs, + impl_args: I::GenericArgs, + ) -> bool { + iter::zip(obligation_args.iter(), impl_args.iter()).all(|(obl, imp)| { + match (obl.kind(), imp.kind()) { + // We don't fast reject based on regions. + (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) => true, + (ty::GenericArgKind::Type(obl), ty::GenericArgKind::Type(imp)) => { + self.types_may_unify(obl, imp) + } + (ty::GenericArgKind::Const(obl), ty::GenericArgKind::Const(imp)) => { + self.consts_may_unify(obl, imp) + } + _ => panic!("kind mismatch: {obl:?} {imp:?}"), + } + }) + } + + pub fn types_may_unify(self, obligation_ty: I::Ty, impl_ty: I::Ty) -> bool { + match impl_ty.kind() { + // Start by checking whether the type in the impl may unify with + // pretty much everything. Just return `true` in that case. + ty::Param(_) | ty::Error(_) | ty::Alias(..) => return true, + // These types only unify with inference variables or their own + // variant. + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Str + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Dynamic(..) + | ty::Pat(..) + | ty::Ref(..) + | ty::Never + | ty::Tuple(..) + | ty::FnPtr(..) + | ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()), + ty::FnDef(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(_) => panic!("unexpected impl_ty: {impl_ty:?}"), + } + + let k = impl_ty.kind(); + match obligation_ty.kind() { + // Purely rigid types, use structural equivalence. + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never + | ty::Foreign(_) => obligation_ty == impl_ty, + ty::Ref(_, obl_ty, obl_mutbl) => match k { + ty::Ref(_, impl_ty, impl_mutbl) => { + obl_mutbl == impl_mutbl && self.types_may_unify(obl_ty, impl_ty) + } + _ => false, + }, + ty::Adt(obl_def, obl_args) => match k { + ty::Adt(impl_def, impl_args) => { + obl_def == impl_def && self.args_may_unify(obl_args, impl_args) + } + _ => false, + }, + ty::Pat(obl_ty, _) => { + // FIXME(pattern_types): take pattern into account + matches!(k, ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty)) + } + ty::Slice(obl_ty) => { + matches!(k, ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty)) + } + ty::Array(obl_ty, obl_len) => match k { + ty::Array(impl_ty, impl_len) => { + self.types_may_unify(obl_ty, impl_ty) + && self.consts_may_unify(obl_len, impl_len) + } + _ => false, + }, + ty::Tuple(obl) => match k { + ty::Tuple(imp) => { + obl.len() == imp.len() + && iter::zip(obl.iter(), imp.iter()) + .all(|(obl, imp)| self.types_may_unify(obl, imp)) + } + _ => false, + }, + ty::RawPtr(obl_ty, obl_mutbl) => match k { + ty::RawPtr(imp_ty, imp_mutbl) => { + obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty) + } + _ => false, + }, + ty::Dynamic(obl_preds, ..) => { + // Ideally we would walk the existential predicates here or at least + // compare their length. But considering that the relevant `Relate` impl + // actually sorts and deduplicates these, that doesn't work. + matches!(k, ty::Dynamic(impl_preds, ..) if + obl_preds.principal_def_id() == impl_preds.principal_def_id() + ) + } + ty::FnPtr(obl_sig) => match k { + ty::FnPtr(impl_sig) => { + let ty::FnSig { inputs_and_output, c_variadic, safety, abi } = + obl_sig.skip_binder(); + let impl_sig = impl_sig.skip_binder(); + + abi == impl_sig.abi + && c_variadic == impl_sig.c_variadic + && safety == impl_sig.safety + && inputs_and_output.len() == impl_sig.inputs_and_output.len() + && iter::zip(inputs_and_output.iter(), impl_sig.inputs_and_output.iter()) + .all(|(obl, imp)| self.types_may_unify(obl, imp)) + } + _ => false, + }, + + // Impls cannot contain these types as these cannot be named directly. + ty::FnDef(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => false, + + // Placeholder types don't unify with anything on their own + ty::Placeholder(..) | ty::Bound(..) => false, + + // Depending on the value of `treat_obligation_params`, we either + // treat generic parameters like placeholders or like inference variables. + ty::Param(_) => match self.treat_obligation_params { + TreatParams::ForLookup => false, + TreatParams::AsCandidateKey => true, + }, + + ty::Infer(ty::IntVar(_)) => impl_ty.is_integral(), + + ty::Infer(ty::FloatVar(_)) => impl_ty.is_floating_point(), + + ty::Infer(_) => true, + + // As we're walking the whole type, it may encounter projections + // inside of binders and what not, so we're just going to assume that + // projections can unify with other stuff. + // + // Looking forward to lazy normalization this is the safer strategy anyways. + ty::Alias(..) => true, + + ty::Error(_) => true, + + ty::CoroutineWitness(..) => { + panic!("unexpected obligation type: {:?}", obligation_ty) + } + } + } + + pub fn consts_may_unify(self, obligation_ct: I::Const, impl_ct: I::Const) -> bool { + let impl_val = match impl_ct.kind() { + ty::ConstKind::Expr(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Error(_) => { + return true; + } + ty::ConstKind::Value(_, impl_val) => impl_val, + ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { + panic!("unexpected impl arg: {:?}", impl_ct) + } + }; + + match obligation_ct.kind() { + ty::ConstKind::Param(_) => match self.treat_obligation_params { + TreatParams::ForLookup => false, + TreatParams::AsCandidateKey => true, + }, + + // Placeholder consts don't unify with anything on their own + ty::ConstKind::Placeholder(_) => false, + + // As we don't necessarily eagerly evaluate constants, + // they might unify with any value. + ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { + true + } + ty::ConstKind::Value(_, obl_val) => obl_val == impl_val, + + ty::ConstKind::Infer(_) => true, + + ty::ConstKind::Bound(..) => { + panic!("unexpected obl const: {:?}", obligation_ct) + } + } + } +} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs new file mode 100644 index 00000000000..1d0b2345b80 --- /dev/null +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -0,0 +1,98 @@ +use crate::fold::TypeFoldable; +use crate::relate::Relate; +use crate::solve::{Goal, NoSolution, SolverMode}; +use crate::{self as ty, Interner}; + +pub trait InferCtxtLike: Sized { + type Interner: Interner; + fn cx(&self) -> Self::Interner; + + fn solver_mode(&self) -> SolverMode; + + fn universe(&self) -> ty::UniverseIndex; + fn create_next_universe(&self) -> ty::UniverseIndex; + + fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>; + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>; + fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>; + + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; + + fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> <Self::Interner as Interner>::Ty; + fn opportunistic_resolve_ct_var( + &self, + vid: ty::ConstVid, + ) -> <Self::Interner as Interner>::Const; + fn opportunistic_resolve_effect_var( + &self, + vid: ty::EffectVid, + ) -> <Self::Interner as Interner>::Const; + fn opportunistic_resolve_lt_var( + &self, + vid: ty::RegionVid, + ) -> <Self::Interner as Interner>::Region; + + fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes; + + fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty; + fn next_const_infer(&self) -> <Self::Interner as Interner>::Const; + fn fresh_args_for_item( + &self, + def_id: <Self::Interner as Interner>::DefId, + ) -> <Self::Interner as Interner>::GenericArgs; + + fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>( + &self, + value: ty::Binder<Self::Interner, T>, + ) -> T; + + fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>( + &self, + value: ty::Binder<Self::Interner, T>, + f: impl FnOnce(T) -> U, + ) -> U; + + fn relate<T: Relate<Self::Interner>>( + &self, + param_env: <Self::Interner as Interner>::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; + + fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>( + &self, + param_env: <Self::Interner as Interner>::ParamEnv, + lhs: T, + rhs: T, + ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; + + fn shallow_resolve( + &self, + ty: <Self::Interner as Interner>::Ty, + ) -> <Self::Interner as Interner>::Ty; + + fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<Self::Interner>; + + fn probe<T>(&self, probe: impl FnOnce() -> T) -> T; + + fn sub_regions( + &self, + sub: <Self::Interner as Interner>::Region, + sup: <Self::Interner as Interner>::Region, + ); + + fn register_ty_outlives( + &self, + ty: <Self::Interner as Interner>::Ty, + r: <Self::Interner as Interner>::Region, + ); +} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index a4e1a97d505..de86a8536f7 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -9,6 +9,7 @@ use std::hash::Hash; use rustc_ast_ir::Mutability; use crate::data_structures::HashSet; +use crate::elaborate::Elaboratable; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal}; @@ -40,6 +41,10 @@ pub trait Ty<I: Interner<Ty = Self>>: fn new_var(interner: I, var: ty::TyVid) -> Self; + fn new_param(interner: I, param: I::ParamTy) -> Self; + + fn new_placeholder(interner: I, param: I::PlaceholderTy) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self; fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; @@ -120,6 +125,14 @@ pub trait Ty<I: Interner<Ty = Self>>: matches!(self.kind(), ty::Infer(ty::TyVar(_))) } + fn is_floating_point(self) -> bool { + matches!(self.kind(), ty::Float(_) | ty::Infer(ty::FloatVar(_))) + } + + fn is_integral(self) -> bool { + matches!(self.kind(), ty::Infer(ty::IntVar(_)) | ty::Int(_) | ty::Uint(_)) + } + fn is_fn_ptr(self) -> bool { matches!(self.kind(), ty::FnPtr(_)) } @@ -224,6 +237,10 @@ pub trait Region<I: Interner<Region = Self>>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; fn new_static(interner: I) -> Self; + + fn is_bound(self) -> bool { + matches!(self.kind(), ty::ReBound(..)) + } } pub trait Const<I: Interner<Const = Self>>: @@ -264,6 +281,10 @@ pub trait Const<I: Interner<Const = Self>>: } } +pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> { + fn args(self) -> I::GenericArgs; +} + pub trait GenericsOf<I: Interner<GenericsOf = Self>> { fn count(&self) -> usize; } @@ -413,6 +434,8 @@ pub trait Predicate<I: Interner<Predicate = Self>>: + UpcastFrom<I, ty::OutlivesPredicate<I, I::Region>> + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>> { + fn as_clause(self) -> Option<I::Clause>; + fn is_coinductive(self, interner: I) -> bool; // FIXME: Eventually uplift the impl out of rustc and make this defaulted. @@ -425,35 +448,35 @@ pub trait Clause<I: Interner<Clause = Self>>: + Hash + Eq + TypeFoldable<I> - // FIXME: Remove these, uplift the `Upcast` impls. + + UpcastFrom<I, ty::Binder<I, ty::ClauseKind<I>>> + UpcastFrom<I, ty::TraitRef<I>> + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>> + UpcastFrom<I, ty::ProjectionPredicate<I>> + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>> + IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>> + + Elaboratable<I> { fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> { self.kind() - .map_bound(|clause| { - if let ty::ClauseKind::Trait(t) = clause { - Some(t) - } else { - None - } - }) + .map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None }) .transpose() } + fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> { self.kind() - .map_bound(|clause| { - if let ty::ClauseKind::Projection(p) = clause { - Some(p) - } else { - None - } - }) + .map_bound( + |clause| { + if let ty::ClauseKind::Projection(p) = clause { Some(p) } else { None } + }, + ) .transpose() } + + /// Performs a instantiation suitable for going from a + /// poly-trait-ref to supertraits that must hold if that + /// poly-trait-ref holds. This is slightly different from a normal + /// instantiation in terms of what happens with bound regions. + fn instantiate_supertrait(self, tcx: I, trait_ref: ty::Binder<I, ty::TraitRef<I>>) -> Self; } /// Common capabilities of placeholder kinds @@ -498,6 +521,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq { fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>; fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>; + + fn is_fundamental(self) -> bool; } pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { @@ -542,6 +567,8 @@ pub trait EvaluationCache<I: Interner> { } pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { + fn is_local(self) -> bool; + fn as_local(self) -> Option<I::LocalDefId>; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index b89ea30fc34..fdd1553d389 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -32,6 +32,7 @@ pub trait Interner: { type DefId: DefId<Self>; type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>; + type Span: Copy + Debug + Hash + Eq + TypeFoldable<Self>; type GenericArgs: GenericArgs<Self>; type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>; @@ -109,7 +110,7 @@ pub trait Interner: type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>; type ValueConst: Copy + Debug + Hash + Eq; - type ExprConst: Copy + Debug + Hash + Eq + Relate<Self>; + type ExprConst: ExprConst<Self>; // Kinds of regions type Region: Region<Self>; @@ -209,11 +210,15 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>; - // FIXME: Rename this so it's obvious it's only *immediate* super predicates. - fn super_predicates_of( + fn explicit_super_predicates_of( self, def_id: Self::DefId, - ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>; + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; + + fn explicit_implied_predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; fn has_target_features(self, def_id: Self::DefId) -> bool; @@ -221,14 +226,9 @@ pub trait Interner: fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool; - fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>; + fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>; - // FIXME: move `fast_reject` into `rustc_type_ir`. - fn args_may_unify_deep( - self, - obligation_args: Self::GenericArgs, - impl_args: Self::GenericArgs, - ) -> bool; + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>; fn for_each_relevant_impl( self, @@ -251,14 +251,9 @@ pub trait Interner: fn trait_is_object_safe(self, trait_def_id: Self::DefId) -> bool; - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; - - fn fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>; - - fn async_fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>; + fn trait_is_fundamental(self, def_id: Self::DefId) -> bool; - fn supertrait_def_ids(self, trait_def_id: Self::DefId) - -> impl IntoIterator<Item = Self::DefId>; + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; @@ -277,6 +272,11 @@ pub trait Interner: param_env: Self::ParamEnv, placeholder: Self::PlaceholderConst, ) -> Self::Ty; + + fn anonymize_bound_vars<T: TypeFoldable<Self>>( + self, + binder: ty::Binder<Self, T>, + ) -> ty::Binder<Self, T>; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index cf5ec1ab3fe..265a4118827 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -3,8 +3,11 @@ pub enum TraitSolverLangItem { // tidy-alphabetical-start AsyncDestruct, + AsyncFn, AsyncFnKindHelper, AsyncFnKindUpvars, + AsyncFnMut, + AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, CallOnceFuture, @@ -17,6 +20,14 @@ pub enum TraitSolverLangItem { Destruct, DiscriminantKind, DynMetadata, + EffectsIntersection, + EffectsIntersectionOutput, + EffectsMaybe, + EffectsNoRuntime, + EffectsRuntime, + Fn, + FnMut, + FnOnce, FnPtrTrait, FusedIterator, Future, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 9b8ca5efdda..b14a65fc779 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -2,7 +2,7 @@ #![allow(rustc::usage_of_ty_tykind)] #![cfg_attr( feature = "nightly", - feature(associated_type_defaults, min_specialization, never_type, rustc_attrs, negative_impls) + feature(associated_type_defaults, never_type, rustc_attrs, negative_impls) )] #![cfg_attr(feature = "nightly", allow(internal_features))] // tidy-alphabetical-end @@ -20,12 +20,15 @@ pub mod visit; #[cfg(feature = "nightly")] pub mod codec; pub mod data_structures; +pub mod elaborate; pub mod error; +pub mod fast_reject; pub mod fold; pub mod inherent; pub mod ir_print; pub mod lang_items; pub mod lift; +pub mod outlives; pub mod relate; pub mod solve; @@ -35,8 +38,10 @@ mod macros; mod binder; mod canonical; mod const_kind; +mod effects; mod flags; mod generic_arg; +mod infer_ctxt; mod interner; mod opaque_ty; mod predicate; @@ -51,8 +56,10 @@ pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; pub use const_kind::*; +pub use effects::*; pub use flags::*; pub use generic_arg::*; +pub use infer_ctxt::*; pub use interner::*; pub use opaque_ty::*; pub use predicate::*; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs new file mode 100644 index 00000000000..10b6f3355d9 --- /dev/null +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -0,0 +1,228 @@ +//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently +//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that +//! RFC for reference. + +use smallvec::{smallvec, SmallVec}; + +use crate::data_structures::SsoHashSet; +use crate::inherent::*; +use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor}; +use crate::{self as ty, Interner}; + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = ""))] +pub enum Component<I: Interner> { + Region(I::Region), + Param(I::ParamTy), + Placeholder(I::PlaceholderTy), + UnresolvedInferenceVariable(ty::InferTy), + + // Projections like `T::Foo` are tricky because a constraint like + // `T::Foo: 'a` can be satisfied in so many ways. There may be a + // where-clause that says `T::Foo: 'a`, or the defining trait may + // include a bound like `type Foo: 'static`, or -- in the most + // conservative way -- we can prove that `T: 'a` (more generally, + // that all components in the projection outlive `'a`). This code + // is not in a position to judge which is the best technique, so + // we just product the projection as a component and leave it to + // the consumer to decide (but see `EscapingProjection` below). + Alias(ty::AliasTy<I>), + + // In the case where a projection has escaping regions -- meaning + // regions bound within the type itself -- we always use + // the most conservative rule, which requires that all components + // outlive the bound. So for example if we had a type like this: + // + // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo > + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // then the inner projection (underlined) has an escaping region + // `'a`. We consider that outer trait `'c` to meet a bound if `'b` + // outlives `'b: 'c`, and we don't consider whether the trait + // declares that `Foo: 'static` etc. Therefore, we just return the + // free components of such a projection (in this case, `'b`). + // + // However, in the future, we may want to get smarter, and + // actually return a "higher-ranked projection" here. Therefore, + // we mark that these components are part of an escaping + // projection, so that implied bounds code can avoid relying on + // them. This gives us room to improve the regionck reasoning in + // the future without breaking backwards compat. + EscapingAlias(Vec<Component<I>>), +} + +/// Push onto `out` all the things that must outlive `'a` for the condition +/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. +pub fn push_outlives_components<I: Interner>( + tcx: I, + ty: I::Ty, + out: &mut SmallVec<[Component<I>; 4]>, +) { + ty.visit_with(&mut OutlivesCollector { tcx, out, visited: Default::default() }); +} + +struct OutlivesCollector<'a, I: Interner> { + tcx: I, + out: &'a mut SmallVec<[Component<I>; 4]>, + visited: SsoHashSet<I::Ty>, +} + +impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> { + fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + if !self.visited.insert(ty) { + return; + } + // Descend through the types, looking for the various "base" + // components and collecting them into `out`. This is not written + // with `collect()` because of the need to sometimes skip subtrees + // in the `subtys` iterator (e.g., when encountering a + // projection). + match ty.kind() { + ty::FnDef(_, args) => { + // HACK(eddyb) ignore lifetimes found shallowly in `args`. + // This is inconsistent with `ty::Adt` (including all args) + // and with `ty::Closure` (ignoring all args other than + // upvars, of which a `ty::FnDef` doesn't have any), but + // consistent with previous (accidental) behavior. + // See https://github.com/rust-lang/rust/issues/70917 + // for further background and discussion. + for child in args.iter() { + match child.kind() { + ty::GenericArgKind::Lifetime(_) => {} + ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_) => { + child.visit_with(self); + } + } + } + } + + ty::Closure(_, args) => { + args.as_closure().tupled_upvars_ty().visit_with(self); + } + + ty::CoroutineClosure(_, args) => { + args.as_coroutine_closure().tupled_upvars_ty().visit_with(self); + } + + ty::Coroutine(_, args) => { + args.as_coroutine().tupled_upvars_ty().visit_with(self); + + // We ignore regions in the coroutine interior as we don't + // want these to affect region inference + } + + // All regions are bound inside a witness, and we don't emit + // higher-ranked outlives components currently. + ty::CoroutineWitness(..) => {} + + // OutlivesTypeParameterEnv -- the actual checking that `X:'a` + // is implied by the environment is done in regionck. + ty::Param(p) => { + self.out.push(Component::Param(p)); + } + + ty::Placeholder(p) => { + self.out.push(Component::Placeholder(p)); + } + + // For projections, we prefer to generate an obligation like + // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the + // regionck more ways to prove that it holds. However, + // regionck is not (at least currently) prepared to deal with + // higher-ranked regions that may appear in the + // trait-ref. Therefore, if we see any higher-ranked regions, + // we simply fallback to the most restrictive rule, which + // requires that `Pi: 'a` for all `i`. + ty::Alias(_, alias_ty) => { + if !alias_ty.has_escaping_bound_vars() { + // best case: no escaping regions, so push the + // projection and skip the subtree (thus generating no + // constraints for Pi). This defers the choice between + // the rules OutlivesProjectionEnv, + // OutlivesProjectionTraitDef, and + // OutlivesProjectionComponents to regionck. + self.out.push(Component::Alias(alias_ty)); + } else { + // fallback case: hard code + // OutlivesProjectionComponents. Continue walking + // through and constrain Pi. + let mut subcomponents = smallvec![]; + compute_alias_components_recursive(self.tcx, ty, &mut subcomponents); + self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect())); + } + } + + // We assume that inference variables are fully resolved. + // So, if we encounter an inference variable, just record + // the unresolved variable as a component. + ty::Infer(infer_ty) => { + self.out.push(Component::UnresolvedInferenceVariable(infer_ty)); + } + + // Most types do not introduce any region binders, nor + // involve any other subtle cases, and so the WF relation + // simply constraints any regions referenced directly by + // the type and then visits the types that are lexically + // contained within. + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never + | ty::Error(_) => { + // Trivial + } + + ty::Bound(_, _) => { + // FIXME: Bound vars matter here! + } + + ty::Adt(_, _) + | ty::Foreign(_) + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Tuple(_) => { + ty.super_visit_with(self); + } + } + } + + fn visit_region(&mut self, lt: I::Region) -> Self::Result { + if !lt.is_bound() { + self.out.push(Component::Region(lt)); + } + } +} + +/// Collect [Component]s for *all* the args of `parent`. +/// +/// This should not be used to get the components of `parent` itself. +/// Use [push_outlives_components] instead. +pub fn compute_alias_components_recursive<I: Interner>( + tcx: I, + alias_ty: I::Ty, + out: &mut SmallVec<[Component<I>; 4]>, +) { + let ty::Alias(kind, alias_ty) = alias_ty.kind() else { + unreachable!("can only call `compute_alias_components_recursive` on an alias type") + }; + + let opt_variances = + if kind == ty::Opaque { Some(tcx.variances_of(alias_ty.def_id)) } else { None }; + + let mut visitor = OutlivesCollector { tcx, out, visited: Default::default() }; + + for (index, child) in alias_ty.args.iter().enumerate() { + if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) { + continue; + } + child.visit_with(&mut visitor); + } +} diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 44dbf549c1a..5f2d9b96c73 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -6,6 +6,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; +use crate::crate_def::Attribute; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::{BinOp, Body, Place, UnOp}; @@ -55,6 +56,15 @@ pub trait Context { /// Returns the name of given `DefId` fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol; + /// Return attributes with the given attribute name. + /// + /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>; + + /// Get all attributes of a definition. + fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>; + /// Returns printable, human readable form of `Span` fn span_to_string(&self, span: Span) -> String; diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 67752a5e629..d9b987c28a2 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -50,6 +50,21 @@ pub trait CrateDef { let def_id = self.def_id(); with(|cx| cx.span_of_an_item(def_id)) } + + /// Return attributes with the given attribute name. + /// + /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`. + /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`. + fn attrs_by_path(&self, attr: &[Symbol]) -> Vec<Attribute> { + let def_id = self.def_id(); + with(|cx| cx.get_attrs_by_path(def_id, attr)) + } + + /// Return all attributes of this definition. + fn all_attrs(&self) -> Vec<Attribute> { + let def_id = self.def_id(); + with(|cx| cx.get_all_attrs(def_id)) + } } /// A trait that can be used to retrieve a definition's type. @@ -69,6 +84,28 @@ pub trait CrateDefType: CrateDef { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Attribute { + value: String, + span: Span, +} + +impl Attribute { + pub fn new(value: String, span: Span) -> Attribute { + Attribute { value, span } + } + + /// Get the span of this attribute. + pub fn span(&self) -> Span { + self.span + } + + /// Get the string representation of this attribute. + pub fn as_str(&self) -> &str { + &self.value + } +} + macro_rules! crate_def { ( $(#[$attr:meta])* $vis:vis $name:ident $(;)? |
