diff options
| author | bors <bors@rust-lang.org> | 2024-01-21 13:13:03 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-01-21 13:13:03 +0000 |
| commit | 70e720bc685e21bbae276445fedf7d89613ecb0d (patch) | |
| tree | bea46b5cbfb4f8baa9e4b0e34be77eb9600be0da /compiler | |
| parent | 0c8e1e6f444d28742aff75f8789df69007ca589a (diff) | |
| parent | 7092c660077d185df375eaf07d649be2b49018d0 (diff) | |
| download | rust-70e720bc685e21bbae276445fedf7d89613ecb0d.tar.gz rust-70e720bc685e21bbae276445fedf7d89613ecb0d.zip | |
Auto merge of #3270 - rust-lang:rustup-2024-01-21, r=RalfJung
Automatic Rustup
Diffstat (limited to 'compiler')
328 files changed, 4216 insertions, 2960 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index ea194e10def..c45a4a410f9 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -49,7 +49,14 @@ bitflags! { | ReprFlags::IS_LINEAR.bits(); } } -rustc_data_structures::external_bitflags_debug! { ReprFlags } + +// This is the same as `rustc_data_structures::external_bitflags_debug` but without the +// `rustc_data_structures` to make it build on stable. +impl std::fmt::Debug for ReprFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bitflags::parser::to_writer(self, f) + } +} #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 21077c312bd..7f5213431f7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -625,7 +625,8 @@ impl Pat { | PatKind::Range(..) | PatKind::Ident(..) | PatKind::Path(..) - | PatKind::MacCall(_) => {} + | PatKind::MacCall(_) + | PatKind::Err(_) => {} } } @@ -809,6 +810,9 @@ pub enum PatKind { /// A macro pattern; pre-expansion. MacCall(P<MacCall>), + + /// Placeholder for a pattern that wasn't syntactically well formed in some way. + Err(ErrorGuaranteed), } /// Whether the `..` is present in a struct fields pattern. @@ -3300,9 +3304,13 @@ mod size_asserts { static_assert_size!(Impl, 136); static_assert_size!(Item, 136); static_assert_size!(ItemKind, 64); - static_assert_size!(LitKind, 24); + // This can be removed after i128:128 is in the bootstrap compiler's target. + #[cfg(not(bootstrap))] + static_assert_size!(LitKind, 32); static_assert_size!(Local, 72); - static_assert_size!(MetaItemLit, 40); + // This can be removed after i128:128 is in the bootstrap compiler's target. + #[cfg(not(bootstrap))] + static_assert_size!(MetaItemLit, 48); static_assert_size!(Param, 40); static_assert_size!(Pat, 72); static_assert_size!(Path, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 450555d0cb5..90677151d25 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1267,7 +1267,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) { let Pat { id, kind, span, tokens } = pat.deref_mut(); vis.visit_id(id); match kind { - PatKind::Wild | PatKind::Rest | PatKind::Never => {} + PatKind::Wild | PatKind::Rest | PatKind::Never | PatKind::Err(_) => {} PatKind::Ident(_binding_mode, ident, sub) => { vis.visit_ident(ident); visit_opt(sub, |sub| vis.visit_pat(sub)); diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 65036bcdc36..098b8f2d6d0 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -2,7 +2,7 @@ // Predicates on exprs and stmts that the pretty-printer and parser use -use crate::ast; +use crate::{ast, token::Delimiter}; /// Does this expression require a semicolon to be treated /// as a statement? The negation of this: 'can this expression @@ -59,8 +59,12 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | While(..) | ConstBlock(_) => break Some(expr), - // FIXME: These can end in `}`, but changing these would break stable code. - InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => { + MacCall(mac) => { + break (mac.args.delim == Delimiter::Brace).then_some(expr); + } + + InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => { + // These should have been denied pre-expansion. break None; } diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 92b9adf1db7..fbae4964588 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -8,7 +8,6 @@ use rustc_lexer::unescape::{ }; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::ops::Range; use std::{ascii, fmt, str}; // Escapes a string, represented as a symbol. Reuses the original symbol, @@ -39,7 +38,6 @@ pub enum LitError { InvalidFloatSuffix, NonDecimalFloat(u32), IntTooLarge(u32), - NulInCStr(Range<usize>), } impl LitKind { @@ -156,10 +154,7 @@ impl LitKind { let s = symbol.as_str(); let mut buf = Vec::with_capacity(s.len()); let mut error = Ok(()); - unescape_c_string(s, Mode::CStr, &mut |span, c| match c { - Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => { - error = Err(LitError::NulInCStr(span)); - } + unescape_c_string(s, Mode::CStr, &mut |_span, c| match c { Ok(CStrUnit::Byte(b)) => buf.push(b), Ok(CStrUnit::Char(c)) => { buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()) @@ -179,10 +174,7 @@ impl LitKind { // can convert the symbol directly to a `Lrc<u8>` on success. let s = symbol.as_str(); let mut error = Ok(()); - unescape_c_string(s, Mode::RawCStr, &mut |span, c| match c { - Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) => { - error = Err(LitError::NulInCStr(span)); - } + unescape_c_string(s, Mode::RawCStr, &mut |_, c| match c { Ok(_) => {} Err(err) => { if err.is_fatal() { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 3617df931e2..89f50d3a0a7 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -568,7 +568,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { walk_list!(visitor, visit_expr, lower_bound); walk_list!(visitor, visit_expr, upper_bound); } - PatKind::Wild | PatKind::Rest | PatKind::Never => {} + PatKind::Wild | PatKind::Rest | PatKind::Never | PatKind::Err(_) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { walk_list!(visitor, visit_pat, elems); } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index e7177402db1..8615016cda5 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -14,10 +14,6 @@ ast_lowering_assoc_ty_parentheses = ast_lowering_async_coroutines_not_supported = `async` coroutines are not yet supported -ast_lowering_async_non_move_closure_not_supported = - `async` non-`move` closures with parameters are not currently supported - .help = consider using `let` statements to manually capture variables by reference before entering an `async move` closure - ast_lowering_att_syntax_only_x86 = the `att_syntax` option is only supported on x86 diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 2811fe104cd..4843d36372d 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -146,14 +146,6 @@ pub struct ClosureCannotBeStatic { } #[derive(Diagnostic, Clone, Copy)] -#[help] -#[diag(ast_lowering_async_non_move_closure_not_supported, code = "E0708")] -pub struct AsyncNonMoveClosureNotSupported { - #[primary_span] - pub fn_decl_span: Span, -} - -#[derive(Diagnostic, Clone, Copy)] #[diag(ast_lowering_functional_record_update_destructuring_assignment)] pub struct FunctionalRecordUpdateDestructuringAssignment { #[primary_span] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e0b1a10c82e..0920de48eb8 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,6 +1,6 @@ use super::errors::{ - AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks, - BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters, + AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, + ClosureCannotBeStatic, CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign, @@ -13,7 +13,6 @@ use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_middle::span_bug; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -1028,28 +1027,16 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: Span, fn_arg_span: Span, ) -> hir::ExprKind<'hir> { - let CoroutineKind::Async { closure_id: inner_closure_id, .. } = coroutine_kind else { - span_bug!(fn_decl_span, "`async gen` and `gen` closures are not supported, yet"); - }; - if let &ClosureBinder::For { span, .. } = binder { self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span }); } let (binder_clause, generic_params) = self.lower_closure_binder(binder); - let outer_decl = - FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; - let body = self.with_new_scopes(fn_decl_span, |this| { - // FIXME(cramertj): allow `async` non-`move` closures with arguments. - if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() { - this.dcx().emit_err(AsyncNonMoveClosureNotSupported { fn_decl_span }); - } - // Transform `async |x: u8| -> X { ... }` into // `|x: u8| || -> X { ... }`. - let body_id = this.lower_fn_body(&outer_decl, |this| { + let body_id = this.lower_body(|this| { let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output { let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock); Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx))) @@ -1057,22 +1044,26 @@ impl<'hir> LoweringContext<'_, 'hir> { None }; - let async_body = this.make_desugared_coroutine_expr( - capture_clause, - inner_closure_id, - async_ret_ty, + let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + decl, + |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), body.span, - hir::CoroutineDesugaring::Async, + coroutine_kind, hir::CoroutineSource::Closure, - |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)), + async_ret_ty, ); - let hir_id = this.lower_node_id(inner_closure_id); + + let hir_id = this.lower_node_id(coroutine_kind.closure_id()); this.maybe_forward_track_caller(body.span, closure_hir_id, hir_id); - hir::Expr { hir_id, kind: async_body, span: this.lower_span(body.span) } + + (parameters, expr) }); body_id }); + let outer_decl = + FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; + let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params); // We need to lower the declaration outside the new scope, because we // have to conserve the state of being inside a loop condition for the diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a3ff02f5f69..dd3f7289a60 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1082,194 +1082,224 @@ impl<'hir> LoweringContext<'_, 'hir> { let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else { return self.lower_fn_body_block(span, decl, body); }; - let closure_id = coroutine_kind.closure_id(); - self.lower_body(|this| { - let mut parameters: Vec<hir::Param<'_>> = Vec::new(); - let mut statements: Vec<hir::Stmt<'_>> = Vec::new(); - - // Async function parameters are lowered into the closure body so that they are - // captured and so that the drop order matches the equivalent non-async functions. - // - // from: - // - // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) { - // <body> - // } - // - // into: - // - // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) { - // async move { - // let __arg2 = __arg2; - // let <pattern> = __arg2; - // let __arg1 = __arg1; - // let <pattern> = __arg1; - // let __arg0 = __arg0; - // let <pattern> = __arg0; - // drop-temps { <body> } // see comments later in fn for details - // } - // } - // - // If `<pattern>` is a simple ident, then it is lowered to a single - // `let <pattern> = <pattern>;` statement as an optimization. - // - // Note that the body is embedded in `drop-temps`; an - // equivalent desugaring would be `return { <body> - // };`. The key point is that we wish to drop all the - // let-bound variables and temporaries created in the body - // (and its tail expression!) before we drop the - // parameters (c.f. rust-lang/rust#64512). - for (index, parameter) in decl.inputs.iter().enumerate() { - let parameter = this.lower_param(parameter); - let span = parameter.pat.span; - - // Check if this is a binding pattern, if so, we can optimize and avoid adding a - // `let <pat> = __argN;` statement. In this case, we do not rename the parameter. - let (ident, is_simple_parameter) = match parameter.pat.kind { - hir::PatKind::Binding(hir::BindingAnnotation(ByRef::No, _), _, ident, _) => { - (ident, true) - } - // For `ref mut` or wildcard arguments, we can't reuse the binding, but - // we can keep the same name for the parameter. - // This lets rustdoc render it correctly in documentation. - hir::PatKind::Binding(_, _, ident, _) => (ident, false), - hir::PatKind::Wild => { - (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false) - } - _ => { - // Replace the ident for bindings that aren't simple. - let name = format!("__arg{index}"); - let ident = Ident::from_str(&name); - - (ident, false) - } - }; - - let desugared_span = this.mark_span_with_reason(DesugaringKind::Async, span, None); - - // Construct a parameter representing `__argN: <ty>` to replace the parameter of the - // async function. - // - // If this is the simple case, this parameter will end up being the same as the - // original parameter, but with a different pattern id. - let stmt_attrs = this.attrs.get(¶meter.hir_id.local_id).copied(); - let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident); - let new_parameter = hir::Param { - hir_id: parameter.hir_id, - pat: new_parameter_pat, - ty_span: this.lower_span(parameter.ty_span), - span: this.lower_span(parameter.span), - }; + let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( + decl, + |this| this.lower_block_expr(body), + body.span, + coroutine_kind, + hir::CoroutineSource::Fn, + None, + ); - if is_simple_parameter { - // If this is the simple case, then we only insert one statement that is - // `let <pat> = <pat>;`. We re-use the original argument's pattern so that - // `HirId`s are densely assigned. - let expr = this.expr_ident(desugared_span, ident, new_parameter_id); - let stmt = this.stmt_let_pat( - stmt_attrs, - desugared_span, - Some(expr), - parameter.pat, - hir::LocalSource::AsyncFn, - ); - statements.push(stmt); - } else { - // If this is not the simple case, then we construct two statements: - // - // ``` - // let __argN = __argN; - // let <pat> = __argN; - // ``` - // - // The first statement moves the parameter into the closure and thus ensures - // that the drop order is correct. - // - // The second statement creates the bindings that the user wrote. - - // Construct the `let mut __argN = __argN;` statement. It must be a mut binding - // because the user may have specified a `ref mut` binding in the next - // statement. - let (move_pat, move_id) = this.pat_ident_binding_mode( - desugared_span, - ident, - hir::BindingAnnotation::MUT, - ); - let move_expr = this.expr_ident(desugared_span, ident, new_parameter_id); - let move_stmt = this.stmt_let_pat( - None, - desugared_span, - Some(move_expr), - move_pat, - hir::LocalSource::AsyncFn, - ); + // FIXME(async_fn_track_caller): Can this be moved above? + let hir_id = this.lower_node_id(coroutine_kind.closure_id()); + this.maybe_forward_track_caller(body.span, fn_id, hir_id); - // Construct the `let <pat> = __argN;` statement. We re-use the original - // parameter's pattern so that `HirId`s are densely assigned. - let pattern_expr = this.expr_ident(desugared_span, ident, move_id); - let pattern_stmt = this.stmt_let_pat( - stmt_attrs, - desugared_span, - Some(pattern_expr), - parameter.pat, - hir::LocalSource::AsyncFn, - ); + (parameters, expr) + }) + } - statements.push(move_stmt); - statements.push(pattern_stmt); - }; + /// Lowers a desugared coroutine body after moving all of the arguments + /// into the body. This is to make sure that the future actually owns the + /// arguments that are passed to the function, and to ensure things like + /// drop order are stable. + pub fn lower_coroutine_body_with_moved_arguments( + &mut self, + decl: &FnDecl, + lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::Expr<'hir>, + body_span: Span, + coroutine_kind: CoroutineKind, + coroutine_source: hir::CoroutineSource, + return_type_hint: Option<hir::FnRetTy<'hir>>, + ) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) { + let mut parameters: Vec<hir::Param<'_>> = Vec::new(); + let mut statements: Vec<hir::Stmt<'_>> = Vec::new(); + + // Async function parameters are lowered into the closure body so that they are + // captured and so that the drop order matches the equivalent non-async functions. + // + // from: + // + // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) { + // <body> + // } + // + // into: + // + // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) { + // async move { + // let __arg2 = __arg2; + // let <pattern> = __arg2; + // let __arg1 = __arg1; + // let <pattern> = __arg1; + // let __arg0 = __arg0; + // let <pattern> = __arg0; + // drop-temps { <body> } // see comments later in fn for details + // } + // } + // + // If `<pattern>` is a simple ident, then it is lowered to a single + // `let <pattern> = <pattern>;` statement as an optimization. + // + // Note that the body is embedded in `drop-temps`; an + // equivalent desugaring would be `return { <body> + // };`. The key point is that we wish to drop all the + // let-bound variables and temporaries created in the body + // (and its tail expression!) before we drop the + // parameters (c.f. rust-lang/rust#64512). + for (index, parameter) in decl.inputs.iter().enumerate() { + let parameter = self.lower_param(parameter); + let span = parameter.pat.span; + + // Check if this is a binding pattern, if so, we can optimize and avoid adding a + // `let <pat> = __argN;` statement. In this case, we do not rename the parameter. + let (ident, is_simple_parameter) = match parameter.pat.kind { + hir::PatKind::Binding(hir::BindingAnnotation(ByRef::No, _), _, ident, _) => { + (ident, true) + } + // For `ref mut` or wildcard arguments, we can't reuse the binding, but + // we can keep the same name for the parameter. + // This lets rustdoc render it correctly in documentation. + hir::PatKind::Binding(_, _, ident, _) => (ident, false), + hir::PatKind::Wild => { + (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false) + } + _ => { + // Replace the ident for bindings that aren't simple. + let name = format!("__arg{index}"); + let ident = Ident::from_str(&name); - parameters.push(new_parameter); - } + (ident, false) + } + }; - let mkbody = |this: &mut LoweringContext<'_, 'hir>| { - // Create a block from the user's function body: - let user_body = this.lower_block_expr(body); + let desugared_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); - // Transform into `drop-temps { <user-body> }`, an expression: - let desugared_span = - this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); - let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); + // Construct a parameter representing `__argN: <ty>` to replace the parameter of the + // async function. + // + // If this is the simple case, this parameter will end up being the same as the + // original parameter, but with a different pattern id. + let stmt_attrs = self.attrs.get(¶meter.hir_id.local_id).copied(); + let (new_parameter_pat, new_parameter_id) = self.pat_ident(desugared_span, ident); + let new_parameter = hir::Param { + hir_id: parameter.hir_id, + pat: new_parameter_pat, + ty_span: self.lower_span(parameter.ty_span), + span: self.lower_span(parameter.span), + }; - // As noted above, create the final block like + if is_simple_parameter { + // If this is the simple case, then we only insert one statement that is + // `let <pat> = <pat>;`. We re-use the original argument's pattern so that + // `HirId`s are densely assigned. + let expr = self.expr_ident(desugared_span, ident, new_parameter_id); + let stmt = self.stmt_let_pat( + stmt_attrs, + desugared_span, + Some(expr), + parameter.pat, + hir::LocalSource::AsyncFn, + ); + statements.push(stmt); + } else { + // If this is not the simple case, then we construct two statements: // // ``` - // { - // let $param_pattern = $raw_param; - // ... - // drop-temps { <user-body> } - // } + // let __argN = __argN; + // let <pat> = __argN; // ``` - let body = this.block_all( + // + // The first statement moves the parameter into the closure and thus ensures + // that the drop order is correct. + // + // The second statement creates the bindings that the user wrote. + + // Construct the `let mut __argN = __argN;` statement. It must be a mut binding + // because the user may have specified a `ref mut` binding in the next + // statement. + let (move_pat, move_id) = + self.pat_ident_binding_mode(desugared_span, ident, hir::BindingAnnotation::MUT); + let move_expr = self.expr_ident(desugared_span, ident, new_parameter_id); + let move_stmt = self.stmt_let_pat( + None, desugared_span, - this.arena.alloc_from_iter(statements), - Some(user_body), + Some(move_expr), + move_pat, + hir::LocalSource::AsyncFn, ); - this.expr_block(body) - }; - let desugaring_kind = match coroutine_kind { - CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async, - CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen, - CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen, + // Construct the `let <pat> = __argN;` statement. We re-use the original + // parameter's pattern so that `HirId`s are densely assigned. + let pattern_expr = self.expr_ident(desugared_span, ident, move_id); + let pattern_stmt = self.stmt_let_pat( + stmt_attrs, + desugared_span, + Some(pattern_expr), + parameter.pat, + hir::LocalSource::AsyncFn, + ); + + statements.push(move_stmt); + statements.push(pattern_stmt); }; - let coroutine_expr = this.make_desugared_coroutine_expr( - CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, - closure_id, - None, - body.span, - desugaring_kind, - hir::CoroutineSource::Fn, - mkbody, + + parameters.push(new_parameter); + } + + let mkbody = |this: &mut LoweringContext<'_, 'hir>| { + // Create a block from the user's function body: + let user_body = lower_body(this); + + // Transform into `drop-temps { <user-body> }`, an expression: + let desugared_span = + this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None); + let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body)); + + // As noted above, create the final block like + // + // ``` + // { + // let $param_pattern = $raw_param; + // ... + // drop-temps { <user-body> } + // } + // ``` + let body = this.block_all( + desugared_span, + this.arena.alloc_from_iter(statements), + Some(user_body), ); - let hir_id = this.lower_node_id(closure_id); - this.maybe_forward_track_caller(body.span, fn_id, hir_id); - let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) }; + this.expr_block(body) + }; + let desugaring_kind = match coroutine_kind { + CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async, + CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen, + CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen, + }; + let closure_id = coroutine_kind.closure_id(); + let coroutine_expr = self.make_desugared_coroutine_expr( + // FIXME(async_closures): This should only move locals, + // and not upvars. Capturing closure upvars by ref doesn't + // work right now anyways, so whatever. + CaptureBy::Value { move_kw: rustc_span::DUMMY_SP }, + closure_id, + return_type_hint, + body_span, + desugaring_kind, + coroutine_source, + mkbody, + ); - (this.arena.alloc_from_iter(parameters), expr) - }) + let expr = hir::Expr { + hir_id: self.lower_node_id(closure_id), + kind: coroutine_expr, + span: self.lower_span(body_span), + }; + + (self.arena.alloc_from_iter(parameters), expr) } fn lower_method_sig( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5387880b6e6..057fe65d0af 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -662,9 +662,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (opt_hash_including_bodies, attrs_hash) = if self.tcx.needs_crate_hash() { self.tcx.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); - hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| { - node.hash_stable(hcx, &mut stable_hasher) - }); + node.hash_stable(&mut hcx, &mut stable_hasher); + // Bodies are stored out of line, so we need to pull them explicitly in the hash. + bodies.hash_stable(&mut hcx, &mut stable_hasher); let h1 = stable_hasher.finish(); let mut stable_hasher = StableHasher::new(); diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 1c405fac7e4..0af141ff99a 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -109,6 +109,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // return inner to be processed in next loop PatKind::Paren(inner) => pattern = inner, PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span), + PatKind::Err(guar) => break hir::PatKind::Err(*guar), } }; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f4b424259de..c2fd4558fd7 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1519,6 +1519,11 @@ impl<'a> State<'a> { self.pclose(); } PatKind::MacCall(m) => self.print_mac(m), + PatKind::Err(_) => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose(); + } } self.ann.post(self, AnnNode::Pat(pat)) } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b4a73574aa2..da67862a48d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -26,6 +26,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::FindExprBySpan; use rustc_trait_selection::traits::ObligationCtxt; use std::iter; @@ -1257,7 +1258,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return None; }; debug!("checking call args for uses of inner_param: {:?}", args); - args.contains(&Operand::Move(inner_param)).then_some((loc, term)) + args.iter() + .map(|a| &a.node) + .any(|a| a == &Operand::Move(inner_param)) + .then_some((loc, term)) }) else { debug!("no uses of inner_param found as a by-move call arg"); @@ -1301,14 +1305,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: Place<'tcx>, borrowed_place: Place<'tcx>, ) { - if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) = - (&place.projection[..], &borrowed_place.projection[..]) + let tcx = self.infcx.tcx; + let hir = tcx.hir(); + + if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)]) + | ( + [ProjectionElem::Deref, ProjectionElem::Index(index1)], + [ProjectionElem::Deref, ProjectionElem::Index(index2)], + ) = (&place.projection[..], &borrowed_place.projection[..]) { - err.help( - "consider using `.split_at_mut(position)` or similar method to obtain \ - two mutable non-overlapping sub-slices", - ) - .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices"); + let mut note_default_suggestion = || { + err.help( + "consider using `.split_at_mut(position)` or similar method to obtain \ + two mutable non-overlapping sub-slices", + ) + .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices"); + }; + + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { + note_default_suggestion(); + return; + }; + + let mut expr_finder = + FindExprBySpan::new(self.body.local_decls[*index1].source_info.span); + expr_finder.visit_expr(hir.body(body_id).value); + let Some(index1) = expr_finder.result else { + note_default_suggestion(); + return; + }; + + expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span); + expr_finder.visit_expr(hir.body(body_id).value); + let Some(index2) = expr_finder.result else { + note_default_suggestion(); + return; + }; + + let sm = tcx.sess.source_map(); + + let Ok(index1_str) = sm.span_to_snippet(index1.span) else { + note_default_suggestion(); + return; + }; + + let Ok(index2_str) = sm.span_to_snippet(index2.span) else { + note_default_suggestion(); + return; + }; + + let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| { + if let hir::Node::Expr(expr) = tcx.hir_node(id) + && let hir::ExprKind::Index(obj, ..) = expr.kind + { + Some(obj) + } else { + None + } + }) else { + note_default_suggestion(); + return; + }; + + let Ok(obj_str) = sm.span_to_snippet(object.span) else { + note_default_suggestion(); + return; + }; + + let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| { + if let hir::Node::Expr(call) = tcx.hir_node(id) + && let hir::ExprKind::Call(callee, ..) = call.kind + && let hir::ExprKind::Path(qpath) = callee.kind + && let hir::QPath::Resolved(None, res) = qpath + && let hir::def::Res::Def(_, did) = res.res + && tcx.is_diagnostic_item(sym::mem_swap, did) + { + Some(call) + } else { + None + } + }) else { + note_default_suggestion(); + return; + }; + + err.span_suggestion( + swap_call.span, + "use `.swap()` to swap elements at the specified indices instead", + format!("{obj_str}.swap({index1_str}, {index2_str})"), + Applicability::MachineApplicable, + ); } } @@ -3242,7 +3328,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { assigned_to, args ); for operand in args { - let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand + let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = + &operand.node else { continue; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 6606be2f9f4..4f66468a865 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -691,7 +691,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); // Check if one of the arguments to this function is the target place. let found_target = args.iter().any(|arg| { - if let Operand::Move(place) = arg { + if let Operand::Move(place) = arg.node { if let Some(potential) = place.as_local() { potential == target } else { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index b31325485db..b35d4e16ecc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::util::{call_kind, CallDesugaringKind}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; 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::infer::InferCtxtExt; @@ -111,9 +112,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("add_moved_or_invoked_closure_note: id={:?}", id); if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() { let closure = match args.first() { - Some(Operand::Copy(place) | Operand::Move(place)) - if target == place.local_or_deref_local() => - { + Some(Spanned { + node: Operand::Copy(place) | Operand::Move(place), .. + }) if target == place.local_or_deref_local() => { place.local_or_deref_local().unwrap() } _ => return false, @@ -1178,9 +1179,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { vec![(move_span.shrink_to_hi(), ".clone()".to_string())] }; - if let Some(errors) = - self.infcx.could_impl_trait(clone_trait, ty, self.param_env) - && !has_sugg + if let Some(errors) = self.infcx.type_implements_trait_shallow( + clone_trait, + ty, + self.param_env, + ) && !has_sugg { let msg = match &errors[..] { [] => "you can `clone` the value and consume it, but this \ @@ -1213,7 +1216,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Applicability::MaybeIncorrect, ); for error in errors { - if let FulfillmentErrorCode::CodeSelectionError( + if let FulfillmentErrorCode::SelectionError( SelectionError::Unimplemented, ) = error.code && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3b3d440df97..0a6b758efa5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1217,19 +1217,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { match self .infcx - .could_impl_trait(clone_trait, ty.peel_refs(), self.param_env) + .type_implements_trait_shallow( + clone_trait, + ty.peel_refs(), + self.param_env, + ) .as_deref() { Some([]) => { - // The type implements Clone. - err.span_help( - expr.span, - format!( - "you can `clone` the `{}` value and consume it, but this \ - might not be your desired behavior", - ty.peel_refs(), - ), - ); + // FIXME: This error message isn't useful, since we're just + // vaguely suggesting to clone a value that already + // implements `Clone`. + // + // A correct suggestion here would take into account the fact + // that inference may be affected by missing types on bindings, + // etc., to improve "tests/ui/borrowck/issue-91206.stderr", for + // example. } None => { if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = @@ -1288,7 +1291,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } // The type doesn't implement Clone because of unmet obligations. for error in errors { - if let traits::FulfillmentErrorCode::CodeSelectionError( + if let traits::FulfillmentErrorCode::SelectionError( traits::SelectionError::Unimplemented, ) = error.code && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 0457b4e6ddc..b304fb5589f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -703,7 +703,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro } => { self.consume_operand(loc, (func, span), flow_state); for arg in args { - self.consume_operand(loc, (arg, span), flow_state); + self.consume_operand(loc, (&arg.node, arg.span), flow_state); } self.mutate_place(loc, (*destination, span), Deep, flow_state); } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 5b764495922..cc8208e9dc3 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -12,6 +12,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; +use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::ResultsCursor; use rustc_span::symbol::sym; use std::env; @@ -27,7 +28,7 @@ use crate::{ facts::{AllFacts, AllFactsExt, RustcFacts}, location::LocationTable, polonius, - region_infer::{values::RegionValueElements, RegionInferenceContext}, + region_infer::RegionInferenceContext, renumber, type_check::{self, MirTypeckRegionConstraints, MirTypeckResults}, universal_regions::UniversalRegions, @@ -98,7 +99,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>( let universal_regions = Rc::new(universal_regions); - let elements = &Rc::new(RegionValueElements::new(body)); + let elements = &Rc::new(DenseLocationMap::new(body)); // Run the MIR type-checker. let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } = diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 232bd741825..5c9056272cc 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -120,7 +120,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { } => { self.consume_operand(location, func); for arg in args { - self.consume_operand(location, arg); + self.consume_operand(location, &arg.node); } self.mutate_place(location, *destination, Deep); } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 948221e9407..89b203f80a4 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -18,7 +18,8 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; +use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; @@ -30,8 +31,7 @@ use crate::{ nll::PoloniusOutput, region_infer::reverse_sccs::ReverseSccGraph, region_infer::values::{ - LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues, - ToElementIndex, + LivenessValues, PlaceholderIndices, RegionElement, RegionValues, ToElementIndex, }, type_check::{free_region_relations::UniversalRegionRelations, Locations}, universal_regions::UniversalRegions, @@ -330,7 +330,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>, type_tests: Vec<TypeTest<'tcx>>, liveness_constraints: LivenessValues, - elements: &Rc<RegionValueElements>, + elements: &Rc<DenseLocationMap>, ) -> Self { debug!("universal_regions: {:#?}", universal_regions); debug!("outlives constraints: {:#?}", outlives_constraints); @@ -1145,6 +1145,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let ty = ty.fold_with(&mut OpaqueFolder { tcx }); + let mut failed = false; let ty = tcx.fold_regions(ty, |r, _depth| { let r_vid = self.to_region_vid(r); @@ -1160,15 +1161,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { .filter(|&u_r| !self.universal_regions.is_local_free_region(u_r)) .find(|&u_r| self.eval_equal(u_r, r_vid)) .map(|u_r| ty::Region::new_var(tcx, u_r)) - // In the case of a failure, use `ReErased`. We will eventually - // return `None` in this case. - .unwrap_or(tcx.lifetimes.re_erased) + // In case we could not find a named region to map to, + // we will return `None` below. + .unwrap_or_else(|| { + failed = true; + r + }) }); debug!("try_promote_type_test_subject: folded ty = {:?}", ty); // This will be true if we failed to promote some region. - if ty.has_erased_regions() { + if failed { return None; } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index dc3ee849d00..e147f62011d 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -1,101 +1,18 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxIndexSet; use rustc_index::bit_set::SparseBitMatrix; use rustc_index::interval::IntervalSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::Idx; -use rustc_index::IndexVec; -use rustc_middle::mir::{BasicBlock, Body, Location}; +use rustc_middle::mir::{BasicBlock, Location}; use rustc_middle::ty::{self, RegionVid}; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use std::fmt::Debug; use std::rc::Rc; -use crate::dataflow::BorrowIndex; - -/// Maps between a `Location` and a `PointIndex` (and vice versa). -pub(crate) struct RegionValueElements { - /// For each basic block, how many points are contained within? - statements_before_block: IndexVec<BasicBlock, usize>, - - /// Map backward from each point to the basic block that it - /// belongs to. - basic_blocks: IndexVec<PointIndex, BasicBlock>, - - num_points: usize, -} - -impl RegionValueElements { - pub(crate) fn new(body: &Body<'_>) -> Self { - let mut num_points = 0; - let statements_before_block: IndexVec<BasicBlock, usize> = body - .basic_blocks - .iter() - .map(|block_data| { - let v = num_points; - num_points += block_data.statements.len() + 1; - v - }) - .collect(); - debug!("RegionValueElements: statements_before_block={:#?}", statements_before_block); - debug!("RegionValueElements: num_points={:#?}", num_points); - - let mut basic_blocks = IndexVec::with_capacity(num_points); - for (bb, bb_data) in body.basic_blocks.iter_enumerated() { - basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb)); - } - - Self { statements_before_block, basic_blocks, num_points } - } - - /// Total number of point indices - pub(crate) fn num_points(&self) -> usize { - self.num_points - } - - /// Converts a `Location` into a `PointIndex`. O(1). - pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { - let Location { block, statement_index } = location; - let start_index = self.statements_before_block[block]; - PointIndex::new(start_index + statement_index) - } - - /// Converts a `Location` into a `PointIndex`. O(1). - pub(crate) fn entry_point(&self, block: BasicBlock) -> PointIndex { - let start_index = self.statements_before_block[block]; - PointIndex::new(start_index) - } - - /// Return the PointIndex for the block start of this index. - pub(crate) fn to_block_start(&self, index: PointIndex) -> PointIndex { - PointIndex::new(self.statements_before_block[self.basic_blocks[index]]) - } - - /// Converts a `PointIndex` back to a location. O(1). - pub(crate) fn to_location(&self, index: PointIndex) -> Location { - assert!(index.index() < self.num_points); - let block = self.basic_blocks[index]; - let start_index = self.statements_before_block[block]; - let statement_index = index.index() - start_index; - Location { block, statement_index } - } - - /// Sometimes we get point-indices back from bitsets that may be - /// out of range (because they round up to the nearest 2^N number - /// of bits). Use this function to filter such points out if you - /// like. - pub(crate) fn point_in_range(&self, index: PointIndex) -> bool { - index.index() < self.num_points - } -} - -rustc_index::newtype_index! { - /// A single integer representing a `Location` in the MIR control-flow - /// graph. Constructed efficiently from `RegionValueElements`. - #[orderable] - #[debug_format = "PointIndex({})"] - pub struct PointIndex {} -} +use crate::BorrowIndex; rustc_index::newtype_index! { /// A single integer representing a `ty::Placeholder`. @@ -123,10 +40,17 @@ pub(crate) enum RegionElement { /// an interval matrix storing liveness ranges for each region-vid. pub(crate) struct LivenessValues { /// The map from locations to points. - elements: Rc<RegionValueElements>, + elements: Rc<DenseLocationMap>, + + /// Which regions are live. This is exclusive with the fine-grained tracking in `points`, and + /// currently only used for validating promoteds (which don't care about more precise tracking). + live_regions: Option<FxHashSet<RegionVid>>, /// For each region: the points where it is live. - points: SparseIntervalMatrix<RegionVid, PointIndex>, + /// + /// This is not initialized for promoteds, because we don't care *where* within a promoted a + /// region is live, only that it is. + points: Option<SparseIntervalMatrix<RegionVid, PointIndex>>, /// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at /// that point. @@ -155,24 +79,52 @@ impl LiveLoans { impl LivenessValues { /// Create an empty map of regions to locations where they're live. - pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self { + pub(crate) fn with_specific_points(elements: Rc<DenseLocationMap>) -> Self { LivenessValues { - points: SparseIntervalMatrix::new(elements.num_points), + live_regions: None, + points: Some(SparseIntervalMatrix::new(elements.num_points())), + elements, + loans: None, + } + } + + /// Create an empty map of regions to locations where they're live. + /// + /// Unlike `with_specific_points`, does not track exact locations where something is live, only + /// which regions are live. + pub(crate) fn without_specific_points(elements: Rc<DenseLocationMap>) -> Self { + LivenessValues { + live_regions: Some(Default::default()), + points: None, elements, loans: None, } } /// Iterate through each region that has a value in this set. - pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> { - self.points.rows() + pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ { + self.points.as_ref().expect("use with_specific_points").rows() + } + + /// Iterate through each region that has a value in this set. + // We are passing query instability implications to the caller. + #[rustc_lint_query_instability] + #[allow(rustc::potential_query_instability)] + pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> + '_ { + self.live_regions.as_ref().unwrap().iter().copied() } /// Records `region` as being live at the given `location`. pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) { - debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); let point = self.elements.point_from_location(location); - self.points.insert(region, point); + debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); + if let Some(points) = &mut self.points { + points.insert(region, point); + } else { + if self.elements.point_in_range(point) { + self.live_regions.as_mut().unwrap().insert(region); + } + } // When available, record the loans flowing into this region as live at the given point. if let Some(loans) = self.loans.as_mut() { @@ -185,7 +137,13 @@ impl LivenessValues { /// Records `region` as being live at all the given `points`. pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet<PointIndex>) { debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points); - self.points.union_row(region, points); + if let Some(this) = &mut self.points { + this.union_row(region, points); + } else { + if points.iter().any(|point| self.elements.point_in_range(point)) { + self.live_regions.as_mut().unwrap().insert(region); + } + } // When available, record the loans flowing into this region as live at the given points. if let Some(loans) = self.loans.as_mut() { @@ -201,23 +159,33 @@ impl LivenessValues { /// Records `region` as being live at all the control-flow points. pub(crate) fn add_all_points(&mut self, region: RegionVid) { - self.points.insert_all_into_row(region); + if let Some(points) = &mut self.points { + points.insert_all_into_row(region); + } else { + self.live_regions.as_mut().unwrap().insert(region); + } } /// Returns whether `region` is marked live at the given `location`. pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool { let point = self.elements.point_from_location(location); - self.points.row(region).is_some_and(|r| r.contains(point)) - } - - /// Returns whether `region` is marked live at any location. - pub(crate) fn is_live_anywhere(&self, region: RegionVid) -> bool { - self.live_points(region).next().is_some() + if let Some(points) = &self.points { + points.row(region).is_some_and(|r| r.contains(point)) + } else { + unreachable!( + "Should be using LivenessValues::with_specific_points to ask whether live at a location" + ) + } } /// Returns an iterator of all the points where `region` is live. fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ { - self.points + let Some(points) = &self.points else { + unreachable!( + "Should be using LivenessValues::with_specific_points to ask whether live at a location" + ) + }; + points .row(region) .into_iter() .flat_map(|set| set.iter()) @@ -298,7 +266,7 @@ impl PlaceholderIndices { /// it would also contain various points from within the function. #[derive(Clone)] pub(crate) struct RegionValues<N: Idx> { - elements: Rc<RegionValueElements>, + elements: Rc<DenseLocationMap>, placeholder_indices: Rc<PlaceholderIndices>, points: SparseIntervalMatrix<N, PointIndex>, free_regions: SparseBitMatrix<N, RegionVid>, @@ -313,14 +281,14 @@ impl<N: Idx> RegionValues<N> { /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. pub(crate) fn new( - elements: &Rc<RegionValueElements>, + elements: &Rc<DenseLocationMap>, num_universal_regions: usize, placeholder_indices: &Rc<PlaceholderIndices>, ) -> Self { let num_placeholders = placeholder_indices.len(); Self { elements: elements.clone(), - points: SparseIntervalMatrix::new(elements.num_points), + points: SparseIntervalMatrix::new(elements.num_points()), placeholder_indices: placeholder_indices.clone(), free_regions: SparseBitMatrix::new(num_universal_regions), placeholders: SparseBitMatrix::new(num_placeholders), @@ -372,7 +340,10 @@ impl<N: Idx> RegionValues<N> { /// elements for the region `from` from `values` and add them to /// the region `to` in `self`. pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) { - if let Some(set) = values.points.row(from) { + let Some(value_points) = &values.points else { + panic!("LivenessValues must track specific points for use in merge_liveness"); + }; + if let Some(set) = value_points.row(from) { self.points.union_row(to, set); } } @@ -486,7 +457,7 @@ impl ToElementIndex for ty::PlaceholderRegion { /// For debugging purposes, returns a pretty-printed string of the given points. pub(crate) fn pretty_print_points( - elements: &RegionValueElements, + elements: &DenseLocationMap, points: impl IntoIterator<Item = PointIndex>, ) -> String { pretty_print_region_elements( 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 4a76d877af0..011b5b760c2 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,5 +1,6 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; +use rustc_hir::def::DefKind; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; @@ -195,7 +196,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn create(mut self) -> CreateResult<'tcx> { - let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id()); + let tcx = self.infcx.tcx; + let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local(); + let span = tcx.def_span(defining_ty_def_id); // Insert the facts we know from the predicates. Why? Why not. let param_env = self.param_env; @@ -275,6 +278,26 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { normalized_inputs_and_output.push(norm_ty); } + // Add implied bounds from impl header. + if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { + for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { + let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = self + .param_env + .and(type_op::normalize::Normalize::new(ty)) + .fully_perform(self.infcx, span) + else { + tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}")); + continue; + }; + constraints.extend(c); + + // We currently add implied bounds from the normalized ty only. + // This is more conservative and matches wfcheck behavior. + let c = self.add_implied_bounds(norm_ty); + constraints.extend(c); + } + } + for c in constraints { self.push_region_constraints(c, span); } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 61b6bef3b87..59518f68ab1 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -7,6 +7,7 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). +use itertools::Itertools; use rustc_infer::infer::BoundRegionConversionTime; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; @@ -39,9 +40,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { user_provided_sig, ); - for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip( - // In MIR, closure args begin with an implicit `self`. Skip it! - body.args_iter().skip(1).map(|local| &body.local_decls[local]), + let is_coroutine_with_implicit_resume_ty = self.tcx().is_coroutine(mir_def_id.to_def_id()) + && user_provided_sig.inputs().is_empty(); + + for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip_eq( + // In MIR, closure args begin with an implicit `self`. + // Also, coroutines have a resume type which may be implicitly `()`. + body.args_iter() + .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 }) + .map(|local| &body.local_decls[local]), ) { self.ascribe_user_type_skip_wf( arg_decl.ty, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index 7433c94a0bc..da5456692ab 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -2,9 +2,9 @@ use rustc_data_structures::vec_linked_list as vll; use rustc_index::IndexVec; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{Body, Local, Location}; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use crate::def_use::{self, DefUse}; -use crate::region_infer::values::{PointIndex, RegionValueElements}; /// A map that cross references each local with the locations where it /// is defined (assigned), used, or dropped. Used during liveness @@ -60,7 +60,7 @@ impl vll::LinkElem for Appearance { impl LocalUseMap { pub(crate) fn build( live_locals: &[Local], - elements: &RegionValueElements, + elements: &DenseLocationMap, body: &Body<'_>, ) -> Self { let nones = IndexVec::from_elem(None, &body.local_decls); @@ -103,7 +103,7 @@ impl LocalUseMap { struct LocalUseMapBuild<'me> { local_use_map: &'me mut LocalUseMap, - elements: &'me RegionValueElements, + elements: &'me DenseLocationMap, // Vector used in `visit_local` to signal which `Local`s do we need // def/use/drop information on, constructed from `live_locals` (that @@ -144,7 +144,7 @@ impl LocalUseMapBuild<'_> { } fn insert( - elements: &RegionValueElements, + elements: &DenseLocationMap, first_appearance: &mut Option<AppearanceIndex>, appearances: &mut IndexVec<AppearanceIndex, Appearance>, location: Location, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index e137bc1be0a..51ae7d14e43 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; +use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::ResultsCursor; use std::rc::Rc; @@ -13,7 +14,7 @@ use crate::{ constraints::OutlivesConstraintSet, facts::{AllFacts, AllFactsExt}, location::LocationTable, - region_infer::values::{LivenessValues, RegionValueElements}, + region_infer::values::LivenessValues, universal_regions::UniversalRegions, }; @@ -34,7 +35,7 @@ mod trace; pub(super) fn generate<'mir, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &Rc<RegionValueElements>, + elements: &Rc<DenseLocationMap>, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, location_table: &LocationTable, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index c718d57bec3..eec128b5f1d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -7,6 +7,7 @@ use rustc_infer::infer::outlives::for_liveness; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -17,7 +18,7 @@ use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::ResultsCursor; use crate::{ - region_infer::values::{self, LiveLoans, PointIndex, RegionValueElements}, + region_infer::values::{self, LiveLoans}, type_check::liveness::local_use_map::LocalUseMap, type_check::liveness::polonius, type_check::NormalizeLocation, @@ -41,7 +42,7 @@ use crate::{ pub(super) fn trace<'mir, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &Rc<RegionValueElements>, + elements: &Rc<DenseLocationMap>, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec<Local>, @@ -105,7 +106,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { typeck: &'me mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping - elements: &'me RegionValueElements, + elements: &'me DenseLocationMap, /// MIR we are analyzing. body: &'me Body<'tcx>, @@ -570,7 +571,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { } fn make_all_regions_live( - elements: &RegionValueElements, + elements: &DenseLocationMap, typeck: &mut TypeChecker<'_, 'tcx>, value: impl TypeVisitable<TyCtxt<'tcx>>, live_at: &IntervalSet<PointIndex>, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9c0f53ddb86..cf28e62177f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -35,7 +35,9 @@ use rustc_middle::ty::{ OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, }; use rustc_middle::ty::{GenericArgsRef, UserArgs}; +use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; @@ -58,9 +60,7 @@ use crate::{ location::LocationTable, member_constraints::MemberConstraintSet, path_utils, - region_infer::values::{ - LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements, - }, + region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}, region_infer::TypeTest, type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, universal_regions::{DefiningTy, UniversalRegions}, @@ -133,7 +133,7 @@ pub(crate) fn type_check<'mir, 'tcx>( all_facts: &mut Option<AllFacts>, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, - elements: &Rc<RegionValueElements>, + elements: &Rc<DenseLocationMap>, upvars: &[&ty::CapturedPlace<'tcx>], use_polonius: bool, ) -> MirTypeckResults<'tcx> { @@ -141,7 +141,7 @@ pub(crate) fn type_check<'mir, 'tcx>( let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), - liveness_constraints: LivenessValues::new(elements.clone()), + liveness_constraints: LivenessValues::with_specific_points(elements.clone()), outlives_constraints: OutlivesConstraintSet::default(), member_constraints: MemberConstraintSet::default(), type_tests: Vec::default(), @@ -406,6 +406,16 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { instantiated_predicates, locations, ); + + assert!(!matches!( + tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), + Some(DefKind::Impl { of_trait: true }) + )); + self.cx.prove_predicates( + args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), + locations, + ConstraintCategory::Boring, + ); } } } @@ -545,7 +555,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let all_facts = &mut None; let mut constraints = Default::default(); let mut liveness_constraints = - LivenessValues::new(Rc::new(RegionValueElements::new(promoted_body))); + LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { @@ -584,17 +594,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } self.cx.borrowck_context.constraints.outlives_constraints.push(constraint) } - for region in liveness_constraints.regions() { - // If the region is live at at least one location in the promoted MIR, - // then add a liveness constraint to the main MIR for this region - // at the location provided as an argument to this method - if liveness_constraints.is_live_anywhere(region) { - self.cx - .borrowck_context - .constraints - .liveness_constraints - .add_location(region, location); - } + // If the region is live at at least one location in the promoted MIR, + // then add a liveness constraint to the main MIR for this region + // at the location provided as an argument to this method + // + // add_location doesn't care about ordering so not a problem for the live regions to be + // unordered. + #[allow(rustc::potential_query_instability)] + for region in liveness_constraints.live_regions_unordered() { + self.cx + .borrowck_context + .constraints + .liveness_constraints + .add_location(region, location); } } @@ -1359,7 +1371,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { TerminatorKind::Call { func, args, destination, call_source, target, .. } => { self.check_operand(func, term_location); for arg in args { - self.check_operand(arg, term_location); + self.check_operand(&arg.node, term_location); } let func_ty = func.ty(body, tcx); @@ -1580,7 +1592,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term: &Terminator<'tcx>, func: &Operand<'tcx>, sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], + args: &[Spanned<Operand<'tcx>>], term_location: Location, call_source: CallSource, ) { @@ -1593,7 +1605,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if self.tcx().is_intrinsic(def_id) { match self.tcx().item_name(def_id) { sym::simd_shuffle => { - if !matches!(args[2], Operand::Constant(_)) { + if !matches!(args[2], Spanned { node: Operand::Constant(_), .. }) { self.tcx() .dcx() .emit_err(SimdShuffleLastConst { span: term.source_info.span }); @@ -1606,7 +1618,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?func_ty); for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { - let op_arg_ty = op_arg.ty(body, self.tcx()); + let op_arg_ty = op_arg.node.ty(body, self.tcx()); let op_arg_ty = self.normalize(op_arg_ty, term_location); let category = if call_source.from_hir_call() { diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index addb41ff5fc..ae8a135f090 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -668,7 +668,11 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { kind: ty::BrEnv, }; let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let closure_ty = tcx.closure_env_ty(def_id, args, env_region).unwrap(); + let closure_ty = tcx.closure_env_ty( + Ty::new_closure(tcx, def_id, args), + args.as_closure().kind(), + env_region, + ); // The "inputs" of the closure in the // signature appear as a tuple. The MIR side diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index f60b73fbe9b..3e9b06a5b05 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -6,6 +6,7 @@ #![doc(rust_logo)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 4d44e340ae1..0631f796894 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -9,6 +9,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, Level}; use rustc_expand::base::*; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span}; +use std::assert_matches::assert_matches; use std::iter; use thin_vec::{thin_vec, ThinVec}; @@ -182,6 +183,16 @@ pub fn expand_test_or_bench( // creates $name: $expr let field = |name, expr| cx.field_imm(sp, Ident::from_str_and_span(name, sp), expr); + // Adds `#[coverage(off)]` to a closure, so it won't be instrumented in + // `-Cinstrument-coverage` builds. + // This requires `#[allow_internal_unstable(coverage_attribute)]` on the + // corresponding macro declaration in `core::macros`. + let coverage_off = |mut expr: P<ast::Expr>| { + assert_matches!(expr.kind, ast::ExprKind::Closure(_)); + expr.attrs.push(cx.attr_nested_word(sym::coverage, sym::off, sp)); + expr + }; + let test_fn = if is_bench { // A simple ident for a lambda let b = Ident::from_str_and_span("b", attr_sp); @@ -190,8 +201,9 @@ pub fn expand_test_or_bench( sp, cx.expr_path(test_path("StaticBenchFn")), thin_vec![ + // #[coverage(off)] // |b| self::test::assert_test_result( - cx.lambda1( + coverage_off(cx.lambda1( sp, cx.expr_call( sp, @@ -206,7 +218,7 @@ pub fn expand_test_or_bench( ], ), b, - ), // ) + )), // ) ], ) } else { @@ -214,8 +226,9 @@ pub fn expand_test_or_bench( sp, cx.expr_path(test_path("StaticTestFn")), thin_vec![ + // #[coverage(off)] // || { - cx.lambda0( + coverage_off(cx.lambda0( sp, // test::assert_test_result( cx.expr_call( @@ -230,7 +243,7 @@ pub fn expand_test_or_bench( ), // ) ], ), // } - ), // ) + )), // ) ], ) }; diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 74e7afee7bc..6d6a1200f50 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -246,12 +246,12 @@ checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "winapi", + "windows-sys", ] [[package]] diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index fdac789423c..c57e964168f 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -19,7 +19,7 @@ gimli = { version = "0.28", default-features = false, features = ["write"]} object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } indexmap = "2.0.0" -libloading = { version = "0.7.3", optional = true } +libloading = { version = "0.8.0", optional = true } smallvec = "1.8.1" [patch.crates-io] diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 795c8daec6a..abd70dd4458 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -11,6 +11,7 @@ use cranelift_module::ModuleError; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi}; use rustc_target::spec::abi::Abi; @@ -360,7 +361,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, source_info: mir::SourceInfo, func: &Operand<'tcx>, - args: &[Operand<'tcx>], + args: &[Spanned<Operand<'tcx>>], destination: Place<'tcx>, target: Option<BasicBlock>, ) { @@ -415,7 +416,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let extra_args = &args[fn_sig.inputs().skip_binder().len()..]; let extra_args = fx.tcx.mk_type_list_from_iter( - extra_args.iter().map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx))), + extra_args.iter().map(|op_arg| fx.monomorphize(op_arg.node.ty(fx.mir, fx.tcx))), ); let fn_abi = if let Some(instance) = instance { RevealAllLayoutCx(fx.tcx).fn_abi_of_instance(instance, extra_args) @@ -440,10 +441,10 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Unpack arguments tuple for closures let mut args = if fn_sig.abi() == Abi::RustCall { let (self_arg, pack_arg) = match args { - [pack_arg] => (None, codegen_call_argument_operand(fx, pack_arg)), + [pack_arg] => (None, codegen_call_argument_operand(fx, &pack_arg.node)), [self_arg, pack_arg] => ( - Some(codegen_call_argument_operand(fx, self_arg)), - codegen_call_argument_operand(fx, pack_arg), + Some(codegen_call_argument_operand(fx, &self_arg.node)), + codegen_call_argument_operand(fx, &pack_arg.node), ), _ => panic!("rust-call abi requires one or two arguments"), }; @@ -463,7 +464,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( } args } else { - args.iter().map(|arg| codegen_call_argument_operand(fx, arg)).collect::<Vec<_>>() + args.iter().map(|arg| codegen_call_argument_operand(fx, &arg.node)).collect::<Vec<_>>() }; // Pass the caller location for `#[track_caller]`. diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index a38a728c926..1345c4614e2 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -7,7 +7,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, generic_args: GenericArgsRef<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, target: Option<BasicBlock>, span: Span, diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs index c8f9c3997a6..f6f3b85d3ef 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs @@ -7,7 +7,7 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, _args: GenericArgsRef<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, target: Option<BasicBlock>, ) { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 81114cbf40d..994dc66835c 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -11,7 +11,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, _args: GenericArgsRef<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, target: Option<BasicBlock>, span: Span, @@ -175,9 +175,9 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( [x, y, kind] => (x, y, kind), _ => bug!("wrong number of args for intrinsic {intrinsic}"), }; - let x = codegen_operand(fx, x); - let y = codegen_operand(fx, y); - let kind = match kind { + let x = codegen_operand(fx, &x.node); + let y = codegen_operand(fx, &y.node); + let kind = match &kind.node { Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0, Operand::Copy(_) | Operand::Move(_) => unreachable!("{kind:?}"), }; @@ -287,8 +287,8 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( [a, b] => (a, b), _ => bug!("wrong number of args for intrinsic {intrinsic}"), }; - let a = codegen_operand(fx, a); - let b = codegen_operand(fx, b); + let a = codegen_operand(fx, &a.node); + let b = codegen_operand(fx, &b.node); // Based on the pseudocode at https://github.com/rust-lang/stdarch/blob/1cfbca8b38fd9b4282b2f054f61c6ca69fc7ce29/crates/core_arch/src/x86/avx2.rs#L2319-L2332 let zero = fx.bcx.ins().iconst(types::I8, 0); @@ -325,9 +325,9 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( [a, b, imm8] => (a, b, imm8), _ => bug!("wrong number of args for intrinsic {intrinsic}"), }; - let a = codegen_operand(fx, a); - let b = codegen_operand(fx, b); - let imm8 = codegen_operand(fx, imm8).load_scalar(fx); + let a = codegen_operand(fx, &a.node); + let b = codegen_operand(fx, &b.node); + let imm8 = codegen_operand(fx, &imm8.node).load_scalar(fx); let a_low = a.value_typed_lane(fx, fx.tcx.types.u128, 0).load_scalar(fx); let a_high = a.value_typed_lane(fx, fx.tcx.types.u128, 1).load_scalar(fx); @@ -956,14 +956,14 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let b = b.load_scalar(fx); let lb = lb.load_scalar(fx); - let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[4]) - { - imm8 - } else { - fx.tcx - .dcx() - .span_fatal(span, "Index argument for `_mm_cmpestri` is not a constant"); - }; + let imm8 = + if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[4].node) { + imm8 + } else { + fx.tcx + .dcx() + .span_fatal(span, "Index argument for `_mm_cmpestri` is not a constant"); + }; let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); @@ -1009,14 +1009,14 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let b = b.load_scalar(fx); let lb = lb.load_scalar(fx); - let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[4]) - { - imm8 - } else { - fx.tcx - .dcx() - .span_fatal(span, "Index argument for `_mm_cmpestrm` is not a constant"); - }; + let imm8 = + if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[4].node) { + imm8 + } else { + fx.tcx + .dcx() + .span_fatal(span, "Index argument for `_mm_cmpestrm` is not a constant"); + }; let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); @@ -1056,15 +1056,15 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let a = a.load_scalar(fx); let b = b.load_scalar(fx); - let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2]) - { - imm8 - } else { - fx.tcx.dcx().span_fatal( - span, - "Index argument for `_mm_clmulepi64_si128` is not a constant", - ); - }; + let imm8 = + if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[2].node) { + imm8 + } else { + fx.tcx.dcx().span_fatal( + span, + "Index argument for `_mm_clmulepi64_si128` is not a constant", + ); + }; let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); @@ -1093,15 +1093,15 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let a = a.load_scalar(fx); - let imm8 = if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[1]) - { - imm8 - } else { - fx.tcx.dcx().span_fatal( - span, - "Index argument for `_mm_aeskeygenassist_si128` is not a constant", - ); - }; + let imm8 = + if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[1].node) { + imm8 + } else { + fx.tcx.dcx().span_fatal( + span, + "Index argument for `_mm_aeskeygenassist_si128` is not a constant", + ); + }; let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 15249402a63..a27853fa0a8 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -5,7 +5,7 @@ macro_rules! intrinsic_args { ($fx:expr, $args:expr => ($($arg:tt),*); $intrinsic:expr) => { #[allow(unused_parens)] let ($($arg),*) = if let [$($arg),*] = $args { - ($(codegen_operand($fx, $arg)),*) + ($(codegen_operand($fx, &($arg).node)),*) } else { $crate::intrinsics::bug_on_incorrect_arg_count($intrinsic); }; @@ -22,6 +22,7 @@ use rustc_middle::ty; use rustc_middle::ty::layout::{HasParamEnv, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::GenericArgsRef; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Symbol}; pub(crate) use self::llvm::codegen_llvm_intrinsic_call; @@ -263,7 +264,7 @@ fn bool_to_zero_or_max_uint<'tcx>( pub(crate) fn codegen_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, instance: Instance<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], destination: CPlace<'tcx>, target: Option<BasicBlock>, source_info: mir::SourceInfo, @@ -301,7 +302,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( fn codegen_float_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: Symbol, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, ) -> bool { let (name, arg_count, ty, clif_ty) = match intrinsic { @@ -353,18 +354,21 @@ fn codegen_float_intrinsic_call<'tcx>( let (a, b, c); let args = match args { [x] => { - a = [codegen_operand(fx, x).load_scalar(fx)]; + a = [codegen_operand(fx, &x.node).load_scalar(fx)]; &a as &[_] } [x, y] => { - b = [codegen_operand(fx, x).load_scalar(fx), codegen_operand(fx, y).load_scalar(fx)]; + b = [ + codegen_operand(fx, &x.node).load_scalar(fx), + codegen_operand(fx, &y.node).load_scalar(fx), + ]; &b } [x, y, z] => { c = [ - codegen_operand(fx, x).load_scalar(fx), - codegen_operand(fx, y).load_scalar(fx), - codegen_operand(fx, z).load_scalar(fx), + codegen_operand(fx, &x.node).load_scalar(fx), + codegen_operand(fx, &y.node).load_scalar(fx), + codegen_operand(fx, &z.node).load_scalar(fx), ]; &c } @@ -422,7 +426,7 @@ fn codegen_regular_intrinsic_call<'tcx>( instance: Instance<'tcx>, intrinsic: Symbol, generic_args: GenericArgsRef<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, destination: Option<BasicBlock>, source_info: mir::SourceInfo, diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 78ea7c2dbfc..d56d17892d5 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -21,7 +21,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: Symbol, generic_args: GenericArgsRef<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], ret: CPlace<'tcx>, target: BasicBlock, span: Span, @@ -121,8 +121,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let [x, y] = args else { bug!("wrong number of args for intrinsic {intrinsic}"); }; - let x = codegen_operand(fx, x); - let y = codegen_operand(fx, y); + let x = codegen_operand(fx, &x.node); + let y = codegen_operand(fx, &y.node); if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); @@ -172,8 +172,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( bug!("wrong number of args for intrinsic {intrinsic}"); } }; - let x = codegen_operand(fx, x); - let y = codegen_operand(fx, y); + let x = codegen_operand(fx, &x.node); + let y = codegen_operand(fx, &y.node); if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); @@ -182,7 +182,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // Make sure this is actually an array, since typeck only checks the length-suffixed // version of this intrinsic. - let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx)); + let idx_ty = fx.monomorphize(idx.node.ty(fx.mir, fx.tcx)); let n: u16 = match idx_ty.kind() { ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len .try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) @@ -215,7 +215,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let indexes = { use rustc_middle::mir::interpret::*; - let idx_const = match idx { + let idx_const = match &idx.node { Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0, Operand::Copy(_) | Operand::Move(_) => unreachable!("{idx:?}"), }; @@ -269,12 +269,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( bug!("wrong number of args for intrinsic {intrinsic}"); } }; - let base = codegen_operand(fx, base); - let val = codegen_operand(fx, val); + let base = codegen_operand(fx, &base.node); + let val = codegen_operand(fx, &val.node); // FIXME validate let idx_const = if let Some(idx_const) = - crate::constant::mir_operand_get_const_val(fx, idx) + crate::constant::mir_operand_get_const_val(fx, &idx.node) { idx_const } else { @@ -304,7 +304,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( bug!("wrong number of args for intrinsic {intrinsic}"); } }; - let v = codegen_operand(fx, v); + let v = codegen_operand(fx, &v.node); if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); @@ -312,7 +312,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } let idx_const = if let Some(idx_const) = - crate::constant::mir_operand_get_const_val(fx, idx) + crate::constant::mir_operand_get_const_val(fx, &idx.node) { idx_const } else { diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index c12142e302d..3948a49ee2a 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -11,7 +11,7 @@ test = false bitflags = "2.4.1" itertools = "0.11" libc = "0.2" -measureme = "10.0.0" +measureme = "11" object = { version = "0.32.0", default-features = false, features = ["std", "read"] } rustc-demangle = "0.1.21" rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 1d1b6e6148d..f3b2caf9b80 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -145,6 +145,14 @@ pub unsafe fn create_module<'ll>( .replace("-Fi64", ""); } } + if llvm_version < (18, 0, 0) { + if sess.target.arch == "x86" || sess.target.arch == "x86_64" { + // LLVM 18 adjusts i128 to be 128-bit aligned on x86 variants. + // Earlier LLVMs leave this as default alignment, so remove it. + // See https://reviews.llvm.org/D86310 + target_data_layout = target_data_layout.replace("-i128:128", ""); + } + } // Ensure the data-layout values hardcoded remain the defaults. if sess.target.is_builtin { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index af1c6594446..5d497d4a188 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -14,7 +14,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::Emitter; -use rustc_errors::{translation::Translate, DiagCtxt, DiagnosticId, FatalError, Level}; +use rustc_errors::{translation::Translate, DiagCtxt, FatalError, Level}; use rustc_errors::{DiagnosticBuilder, DiagnosticMessage, Style}; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -1000,7 +1000,7 @@ type DiagnosticArgName<'source> = Cow<'source, str>; struct Diagnostic { msgs: Vec<(DiagnosticMessage, Style)>, args: FxHashMap<DiagnosticArgName<'static>, rustc_errors::DiagnosticArgValue<'static>>, - code: Option<DiagnosticId>, + code: Option<String>, lvl: Level, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index a1662f25e14..e35b4029b45 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::config::OptLevel; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{source_map::Spanned, sym, Span, Symbol}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; use rustc_target::abi::{self, HasDataLayout, WrappingRange}; use rustc_target::spec::abi::Abi; @@ -742,7 +742,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], destination: mir::Place<'tcx>, target: Option<mir::BasicBlock>, unwind: mir::UnwindAction, @@ -793,7 +793,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 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.ty(self.mir, bx.tcx()); + let op_ty = op_arg.node.ty(self.mir, bx.tcx()); self.monomorphize(op_ty) })); @@ -863,7 +863,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // checked by const-qualification, which also // promotes any complex rvalues to constants. if i == 2 && intrinsic == sym::simd_shuffle { - if let mir::Operand::Constant(constant) = arg { + if let mir::Operand::Constant(constant) = &arg.node { let (llval, ty) = self.simd_shuffle_indices(bx, constant); return OperandRef { val: Immediate(llval), @@ -874,7 +874,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - self.codegen_operand(bx, arg) + self.codegen_operand(bx, &arg.node) }) .collect(); @@ -910,7 +910,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut copied_constant_arguments = vec![]; 'make_args: for (i, arg) in first_args.iter().enumerate() { - let mut op = self.codegen_operand(bx, arg); + let mut op = self.codegen_operand(bx, &arg.node); if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { match op.val { @@ -988,7 +988,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // The callee needs to own the argument memory if we pass it // by-ref, so make a local copy of non-immediate constants. - match (arg, op.val) { + match (&arg.node, op.val) { (&mir::Operand::Copy(_), Ref(_, None, _)) | (&mir::Operand::Constant(_), Ref(_, None, _)) => { let tmp = PlaceRef::alloca(bx, op.layout); @@ -1003,7 +1003,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_argument(bx, op, &mut llargs, &fn_abi.args[i]); } let num_untupled = untuple.map(|tup| { - self.codegen_arguments_untupled(bx, tup, &mut llargs, &fn_abi.args[first_args.len()..]) + self.codegen_arguments_untupled( + bx, + &tup.node, + &mut llargs, + &fn_abi.args[first_args.len()..], + ) }); let needs_location = diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 9d22df50d4f..4236117d75b 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -225,17 +225,10 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - // see comment in eval_to_allocation_raw_provider for what we're doing here - if key.param_env.reveal() == Reveal::All { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_const_value_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::TooGeneric(_)) => {} - // deduplicate calls - other => return other, - } - } + // Const eval always happens in Reveal::All mode in order to be able to use the hidden types of + // opaque types. This is needed for trivial things like `size_of`, but also for using associated + // types that are not specified in the opaque type. + assert_eq!(key.param_env.reveal(), Reveal::All); // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. // Catch such calls and evaluate them instead of trying to load a constant's MIR. @@ -265,24 +258,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { - // Because the constant is computed twice (once per value of `Reveal`), we are at risk of - // reporting the same error twice here. To resolve this, we check whether we can evaluate the - // constant in the more restrictive `Reveal::UserFacing`, which most likely already was - // computed. For a large percentage of constants that will already have succeeded. Only - // associated constants of generic functions will fail due to not enough monomorphization - // information being available. - - // In case we fail in the `UserFacing` variant, we just do the real computation. - if key.param_env.reveal() == Reveal::All { - let mut key = key; - key.param_env = key.param_env.with_user_facing(); - match tcx.eval_to_allocation_raw(key) { - // try again with reveal all as requested - Err(ErrorHandled::TooGeneric(_)) => {} - // deduplicate calls - other => return other, - } - } + // Const eval always happens in Reveal::All mode in order to be able to use the hidden types of + // opaque types. This is needed for trivial things like `size_of`, but also for using associated + // types that are not specified in the opaque type. + + assert_eq!(key.param_env.reveal(), Reveal::All); if cfg!(debug_assertions) { // Make sure we format the instance even if we do not print it. // This serves as a regression test against an ICE on printing. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 7ff970661d6..3afd14eb574 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1209,21 +1209,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub_custom!(fluent::const_eval_copy_nonoverlapping_overlapping); } } + } - for i in 0..num_copies { - ptr::copy( - src_bytes, - dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication - size.bytes_usize(), - ); + let size_in_bytes = size.bytes_usize(); + // For particularly large arrays (where this is perf-sensitive) it's common that + // we're writing a single byte repeatedly. So, optimize that case to a memset. + if size_in_bytes == 1 && num_copies >= 1 { + // SAFETY: `src_bytes` would be read from anyway by copies below (num_copies >= 1). + // Since size_in_bytes = 1, then the `init.no_bytes_init()` check above guarantees + // that this read at type `u8` is OK -- it must be an initialized byte. + let value = *src_bytes; + dest_bytes.write_bytes(value, (size * num_copies).bytes_usize()); + } else if src_alloc_id == dest_alloc_id { + let mut dest_ptr = dest_bytes; + for _ in 0..num_copies { + ptr::copy(src_bytes, dest_ptr, size_in_bytes); + dest_ptr = dest_ptr.add(size_in_bytes); } } else { - for i in 0..num_copies { - ptr::copy_nonoverlapping( - src_bytes, - dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication - size.bytes_usize(), - ); + let mut dest_ptr = dest_bytes; + for _ in 0..num_copies { + ptr::copy_nonoverlapping(src_bytes, dest_ptr, size_in_bytes); + dest_ptr = dest_ptr.add(size_in_bytes); } } } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 2358caffc9b..7b993279f18 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -9,7 +9,7 @@ use rustc_middle::{ AdtDef, Instance, Ty, }, }; -use rustc_span::sym; +use rustc_span::{source_map::Spanned, sym}; use rustc_target::abi::{self, FieldIdx}; use rustc_target::abi::{ call::{ArgAbi, FnAbi, PassMode}, @@ -242,13 +242,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Evaluate the arguments of a function call pub(super) fn eval_fn_call_arguments( &self, - ops: &[mir::Operand<'tcx>], + ops: &[Spanned<mir::Operand<'tcx>>], ) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> { ops.iter() .map(|op| { - Ok(match op { + Ok(match &op.node { mir::Operand::Move(place) => FnArg::InPlace(self.eval_place(*place)?), - _ => FnArg::Copy(self.eval_operand(op, None)?), + _ => FnArg::Copy(self.eval_operand(&op.node, None)?), }) }) .collect() diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index ae9595d7e64..89c65d92325 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -804,7 +804,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // const-eval of the `begin_panic` fn assumes the argument is `&str` if Some(callee) == tcx.lang_items().begin_panic_fn() { - match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { + match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if ty.is_str() => return, _ => self.check_op(ops::PanicNonStr), } @@ -812,7 +812,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // const-eval of `#[rustc_const_panic_str]` functions assumes the argument is `&&str` if tcx.has_attr(callee, sym::rustc_const_panic_str) { - match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { + match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => { return; diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 0b73691204d..9c2f336e912 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -402,7 +402,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } for arg in args { - if let Operand::Move(place) = arg { + if let Operand::Move(place) = &arg.node { if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() { // This is bad! The callee will expect the memory to be aligned. self.fail( diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 23949deaade..9d598c32e6f 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -13,7 +13,7 @@ indexmap = { version = "2.0.0" } itertools = "0.11" jobserver_crate = { version = "0.1.27", package = "jobserver" } libc = "0.2" -measureme = "10.0.0" +measureme = "11" rustc-hash = "1.1.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_data_structures/src/binary_search_util/mod.rs b/compiler/rustc_data_structures/src/binary_search_util/mod.rs index bc8a6b9eac0..1c6e227cec4 100644 --- a/compiler/rustc_data_structures/src/binary_search_util/mod.rs +++ b/compiler/rustc_data_structures/src/binary_search_util/mod.rs @@ -18,27 +18,10 @@ where return &[]; }; - // Now search forward to find the *last* one. - let mut end = start; - let mut previous = start; - let mut step = 1; - loop { - end = end.saturating_add(step).min(size); - if end == size || key_fn(&data[end]) != *key { - break; - } - previous = end; - step *= 2; - } - step = end - previous; - while step > 1 { - let half = step / 2; - let mid = end - half; - if key_fn(&data[mid]) != *key { - end = mid; - } - step -= half; - } + // Find the first entry with key > `key`. Skip `start` entries since + // key_fn(&data[start]) == *key + let offset = start + 1; + let end = data[offset..].partition_point(|x| key_fn(x) <= *key) + offset; &data[start..end] } diff --git a/compiler/rustc_data_structures/src/hashes.rs b/compiler/rustc_data_structures/src/hashes.rs index 291ee5bbe26..1564eeb4bae 100644 --- a/compiler/rustc_data_structures/src/hashes.rs +++ b/compiler/rustc_data_structures/src/hashes.rs @@ -75,11 +75,21 @@ impl fmt::LowerHex for Hash64 { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct Hash128 { inner: u128, } +// We expect Hash128 to be well mixed. So there's no point in hashing both parts. +// +// This also allows using Hash128-containing types in UnHash-based hashmaps, which would otherwise +// debug_assert! that we're hashing more than a single u64. +impl std::hash::Hash for Hash128 { + fn hash<H: std::hash::Hasher>(&self, h: &mut H) { + h.write_u64(self.truncate().as_u64()); + } +} + impl Hash128 { #[inline] pub fn truncate(self) -> Hash64 { diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 266e54604a6..a9ccfbed411 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -177,7 +177,6 @@ cfg_match! { [Vec<T, A> where T: DynSync, A: std::alloc::Allocator + DynSync] [Box<T, A> where T: ?Sized + DynSync, A: std::alloc::Allocator + DynSync] [crate::sync::RwLock<T> where T: DynSend + DynSync] - [crate::sync::OneThread<T> where T] [crate::sync::WorkerLocal<T> where T: DynSend] [crate::intern::Interned<'a, T> where 'a, T: DynSync] [crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index afe26f80de8..52304c72a2f 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -314,7 +314,19 @@ impl_stable_traits_for_trivial_type!(char); impl_stable_traits_for_trivial_type!(()); impl_stable_traits_for_trivial_type!(Hash64); -impl_stable_traits_for_trivial_type!(Hash128); + +// We need a custom impl as the default hash function will only hash half the bits. For stable +// hashing we want to hash the full 128-bit hash. +impl<CTX> HashStable<CTX> for Hash128 { + #[inline] + fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) { + self.as_u128().hash(hasher); + } +} + +unsafe impl StableOrd for Hash128 { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl<CTX> HashStable<CTX> for ! { fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) { diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 48edfba8da0..adcb6ceaebf 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -43,7 +43,6 @@ pub use crate::marker::*; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; -use std::ops::{Deref, DerefMut}; mod lock; pub use lock::{Lock, LockGuard, Mode}; @@ -309,8 +308,6 @@ cfg_match! { use parking_lot::RwLock as InnerRwLock; - use std::thread; - /// This makes locks panic if they are already held. /// It is only useful when you are running in a single thread const ERROR_CHECKING: bool = false; @@ -445,56 +442,3 @@ impl<T: Clone> Clone for RwLock<T> { RwLock::new(self.borrow().clone()) } } - -/// A type which only allows its inner value to be used in one thread. -/// It will panic if it is used on multiple threads. -#[derive(Debug)] -pub struct OneThread<T> { - #[cfg(parallel_compiler)] - thread: thread::ThreadId, - inner: T, -} - -#[cfg(parallel_compiler)] -unsafe impl<T> std::marker::Sync for OneThread<T> {} -#[cfg(parallel_compiler)] -unsafe impl<T> std::marker::Send for OneThread<T> {} - -impl<T> OneThread<T> { - #[inline(always)] - fn check(&self) { - #[cfg(parallel_compiler)] - assert_eq!(thread::current().id(), self.thread); - } - - #[inline(always)] - pub fn new(inner: T) -> Self { - OneThread { - #[cfg(parallel_compiler)] - thread: thread::current().id(), - inner, - } - } - - #[inline(always)] - pub fn into_inner(value: Self) -> T { - value.check(); - value.inner - } -} - -impl<T> Deref for OneThread<T> { - type Target = T; - - fn deref(&self) -> &T { - self.check(); - &self.inner - } -} - -impl<T> DerefMut for OneThread<T> { - fn deref_mut(&mut self) -> &mut T { - self.check(); - &mut self.inner - } -} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e4baf26176..11dcf4108d4 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -35,7 +35,7 @@ use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths}; +use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType}; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; use rustc_session::{config, EarlyDiagCtxt, Session}; @@ -204,7 +204,7 @@ impl Callbacks for TimePassesCallbacks { // self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes) .then(|| config.opts.unstable_opts.time_passes_format); - config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; + config.opts.trimmed_def_paths = true; } } diff --git a/compiler/rustc_error_codes/src/error_codes/E0708.md b/compiler/rustc_error_codes/src/error_codes/E0708.md index 9287fc803d1..61a853ac446 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0708.md +++ b/compiler/rustc_error_codes/src/error_codes/E0708.md @@ -1,12 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler. + `async` non-`move` closures with parameters are currently not supported. Erroneous code example: -```compile_fail,edition2018,E0708 +```edition2018 #![feature(async_closure)] fn main() { - let add_one = async |num: u8| { // error! + let add_one = async |num: u8| { num + 1 }; } diff --git a/compiler/rustc_error_codes/src/error_codes/E0795.md b/compiler/rustc_error_codes/src/error_codes/E0795.md index 20f51441c29..ad77d72c913 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0795.md +++ b/compiler/rustc_error_codes/src/error_codes/E0795.md @@ -3,7 +3,7 @@ Invalid argument for the `offset_of!` macro. Erroneous code example: ```compile_fail,E0795 -#![feature(offset_of, offset_of_enum)] +#![feature(offset_of_enum, offset_of_nested)] let x = std::mem::offset_of!(Option<u8>, Some); ``` @@ -16,7 +16,7 @@ The offset of the contained `u8` in the `Option<u8>` can be found by specifying the field name `0`: ``` -#![feature(offset_of, offset_of_enum)] +#![feature(offset_of_enum, offset_of_nested)] let x: usize = std::mem::offset_of!(Option<u8>, Some.0); ``` diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 648c9118400..f0699a56f98 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -9,8 +9,8 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; use crate::translation::{to_fluent_args, Translate}; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, - LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, + CodeSuggestion, Diagnostic, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, + Level, MultiSpan, Style, SubDiagnostic, }; use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; use rustc_data_structures::sync::Lrc; @@ -127,7 +127,7 @@ impl AnnotateSnippetEmitter { level: &Level, messages: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, - code: &Option<DiagnosticId>, + code: &Option<String>, msp: &MultiSpan, _children: &[SubDiagnostic], _suggestions: &[CodeSuggestion], @@ -181,11 +181,7 @@ impl AnnotateSnippetEmitter { let snippet = Snippet { title: Some(Annotation { label: Some(&message), - id: code.as_ref().map(|c| match c { - DiagnosticId::Error(val) | DiagnosticId::Lint { name: val, .. } => { - val.as_str() - } - }), + id: code.as_deref(), annotation_type: annotation_type_for_level(*level), }), footer: vec![], diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 786aced5b4f..4934bc2450c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -104,7 +104,7 @@ pub struct Diagnostic { pub(crate) level: Level, pub messages: Vec<(DiagnosticMessage, Style)>, - pub code: Option<DiagnosticId>, + pub code: Option<String>, pub span: MultiSpan, pub children: Vec<SubDiagnostic>, pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, @@ -115,9 +115,9 @@ pub struct Diagnostic { /// `span` if there is one. Otherwise, it is `DUMMY_SP`. pub sort_span: Span, - /// If diagnostic is from Lint, custom hash function ignores notes - /// otherwise hash is based on the all the fields - pub is_lint: bool, + /// If diagnostic is from Lint, custom hash function ignores children. + /// Otherwise hash is based on the all the fields. + pub is_lint: Option<IsLint>, /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. @@ -146,13 +146,11 @@ impl fmt::Display for DiagnosticLocation { } #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] -pub enum DiagnosticId { - Error(String), - Lint { - name: String, - /// Indicates whether this lint should show up in cargo's future breakage report. - has_future_breakage: bool, - }, +pub struct IsLint { + /// The lint name. + pub(crate) name: String, + /// Indicates whether this lint should show up in cargo's future breakage report. + has_future_breakage: bool, } /// A "sub"-diagnostic attached to a parent diagnostic. @@ -231,7 +229,7 @@ impl Diagnostic { suggestions: Ok(vec![]), args: Default::default(), sort_span: DUMMY_SP, - is_lint: false, + is_lint: None, emitted_at: DiagnosticLocation::caller(), } } @@ -288,16 +286,13 @@ impl Diagnostic { /// Indicates whether this diagnostic should show up in cargo's future breakage report. pub(crate) fn has_future_breakage(&self) -> bool { - match self.code { - Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage, - _ => false, - } + matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. })) } pub(crate) fn is_force_warn(&self) -> bool { match self.level { Level::ForceWarning(_) => { - assert!(self.is_lint); + assert!(self.is_lint.is_some()); true } _ => false, @@ -893,12 +888,12 @@ impl Diagnostic { self } - pub fn is_lint(&mut self) -> &mut Self { - self.is_lint = true; + pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { + self.is_lint = Some(IsLint { name, has_future_breakage }); self } - pub fn code(&mut self, s: DiagnosticId) -> &mut Self { + pub fn code(&mut self, s: String) -> &mut Self { self.code = Some(s); self } @@ -908,8 +903,8 @@ impl Diagnostic { self } - pub fn get_code(&self) -> Option<DiagnosticId> { - self.code.clone() + pub fn get_code(&self) -> Option<&str> { + self.code.as_deref() } pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { @@ -995,7 +990,8 @@ impl Diagnostic { &Level, &[(DiagnosticMessage, Style)], Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>, - &Option<DiagnosticId>, + &Option<String>, + &Option<IsLint>, &MultiSpan, &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, Option<&[SubDiagnostic]>, @@ -1005,9 +1001,10 @@ impl Diagnostic { &self.messages, self.args().collect(), &self.code, + &self.is_lint, &self.span, &self.suggestions, - (if self.is_lint { None } else { Some(&self.children) }), + (if self.is_lint.is_some() { None } else { Some(&self.children) }), ) } } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index bd7c58d904e..87e2d295c7f 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,8 +1,8 @@ use crate::diagnostic::IntoDiagnosticArg; use crate::{DiagCtxt, Level, MultiSpan, StashKey}; use crate::{ - Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed, - ExplicitBug, SubdiagnosticMessage, + Diagnostic, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed, ExplicitBug, + SubdiagnosticMessage, }; use rustc_lint_defs::Applicability; use rustc_span::source_map::Spanned; @@ -395,8 +395,11 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!((span, with_span)( sp: impl Into<MultiSpan>, )); + forward!((is_lint, with_is_lint)( + name: String, has_future_breakage: bool, + )); forward!((code, with_code)( - s: DiagnosticId, + s: String, )); forward!((arg, with_arg)( name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, @@ -437,15 +440,11 @@ impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> { #[macro_export] macro_rules! struct_span_code_err { ($dcx:expr, $span:expr, $code:ident, $($message:tt)*) => ({ - $dcx.struct_span_err( - $span, - format!($($message)*), - ) - .with_code($crate::error_code!($code)) + $dcx.struct_span_err($span, format!($($message)*)).with_code($crate::error_code!($code)) }) } #[macro_export] macro_rules! error_code { - ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }}; + ($code:ident) => {{ stringify!($code).to_owned() }}; } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 987832e6937..23efdaea0eb 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -16,9 +16,9 @@ use crate::snippet::{ use crate::styled_buffer::StyledBuffer; use crate::translation::{to_fluent_args, Translate}; use crate::{ - diagnostic::DiagnosticLocation, CodeSuggestion, DiagCtxt, Diagnostic, DiagnosticId, - DiagnosticMessage, FluentBundle, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, - SubstitutionHighlight, SuggestionStyle, TerminalUrl, + diagnostic::DiagnosticLocation, CodeSuggestion, DiagCtxt, Diagnostic, DiagnosticMessage, + FluentBundle, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, + SuggestionStyle, TerminalUrl, }; use rustc_lint_defs::pluralize; @@ -1309,7 +1309,7 @@ impl HumanEmitter { msp: &MultiSpan, msgs: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, - code: &Option<DiagnosticId>, + code: &Option<String>, level: &Level, max_line_num_len: usize, is_secondary: bool, @@ -1336,14 +1336,13 @@ impl HumanEmitter { buffer.append(0, level.to_str(), Style::Level(*level)); label_width += level.to_str().len(); } - // only render error codes, not lint codes - if let Some(DiagnosticId::Error(ref code)) = *code { + if let Some(code) = code { buffer.append(0, "[", Style::Level(*level)); let code = if let TerminalUrl::Yes = self.terminal_url { let path = "https://doc.rust-lang.org/error_codes"; - format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07") + Cow::Owned(format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07")) } else { - code.clone() + Cow::Borrowed(code) }; buffer.append(0, &code, Style::Level(*level)); buffer.append(0, "]", Style::Level(*level)); @@ -2077,7 +2076,7 @@ impl HumanEmitter { level: &Level, messages: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, - code: &Option<DiagnosticId>, + code: &Option<String>, span: &MultiSpan, children: &[SubDiagnostic], suggestions: &[CodeSuggestion], diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 87bf9c23456..51b064f4c61 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -15,10 +15,9 @@ use termcolor::{ColorSpec, WriteColor}; use crate::emitter::{should_show_source_code, Emitter, HumanReadableErrorType}; use crate::registry::Registry; use crate::translation::{to_fluent_args, Translate}; -use crate::DiagnosticId; use crate::{ - CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, - TerminalUrl, + diagnostic::IsLint, CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, + SubDiagnostic, TerminalUrl, }; use rustc_lint_defs::Applicability; @@ -301,7 +300,8 @@ struct DiagnosticSpanMacroExpansion { #[derive(Serialize)] struct DiagnosticCode { - /// The code itself. + /// The error code (e.g. "E1234"), if the diagnostic has one. Or the lint + /// name, if it's a lint without an error code. code: String, /// An explanation for the code. explanation: Option<&'static str>, @@ -399,9 +399,21 @@ impl Diagnostic { let output = String::from_utf8(output).unwrap(); let translated_message = je.translate_messages(&diag.messages, &args); + + let code = if let Some(code) = &diag.code { + Some(DiagnosticCode { + code: code.to_string(), + explanation: je.registry.as_ref().unwrap().try_find_description(&code).ok(), + }) + } else if let Some(IsLint { name, .. }) = &diag.is_lint { + Some(DiagnosticCode { code: name.to_string(), explanation: None }) + } else { + None + }; + Diagnostic { message: translated_message.to_string(), - code: DiagnosticCode::map_opt_string(diag.code.clone(), je), + code, level: diag.level.to_str(), spans: DiagnosticSpan::from_multispan(&diag.span, &args, je), children: diag @@ -592,18 +604,3 @@ impl DiagnosticSpanLine { .unwrap_or_else(|_| vec![]) } } - -impl DiagnosticCode { - fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> { - s.map(|s| { - let s = match s { - DiagnosticId::Error(s) => s, - DiagnosticId::Lint { name, .. } => name, - }; - let je_result = - je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); - - DiagnosticCode { code: s, explanation: je_result.ok() } - }) - } -} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 63391a0faa6..141547b537d 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -30,7 +30,7 @@ extern crate tracing; extern crate self as rustc_errors; pub use diagnostic::{ - AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, + AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{ @@ -442,13 +442,13 @@ struct DiagCtxtInner { /// This is used for the `good_path_delayed_bugs` check. suppressed_expected_diag: bool, - /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid + /// This set contains the code of all emitted diagnostics to avoid /// emitting the same diagnostic with extended help (`--teach`) twice, which /// would be unnecessary repetition. - taught_diagnostics: FxHashSet<DiagnosticId>, + taught_diagnostics: FxHashSet<String>, /// Used to suggest rustc --explain `<error code>` - emitted_diagnostic_codes: FxIndexSet<DiagnosticId>, + emitted_diagnostic_codes: FxIndexSet<String>, /// This set contains a hash of every diagnostic that has been emitted by /// this `DiagCtxt`. These hashes is used to avoid emitting the same error @@ -676,7 +676,7 @@ impl DiagCtxt { let key = (span.with_parent(None), key); if diag.is_error() { - if diag.is_lint { + if diag.is_lint.is_some() { inner.lint_err_count += 1; } else { inner.err_count += 1; @@ -695,7 +695,7 @@ impl DiagCtxt { let key = (span.with_parent(None), key); let diag = inner.stashed_diagnostics.remove(&key)?; if diag.is_error() { - if diag.is_lint { + if diag.is_lint.is_some() { inner.lint_err_count -= 1; } else { inner.err_count -= 1; @@ -715,9 +715,7 @@ impl DiagCtxt { /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. /// - /// Attempting to `.emit()` the builder will only emit if either: - /// * `can_emit_warnings` is `true` - /// * `is_force_warn` was set in `DiagnosticId::Lint` + /// An `emit` call on the builder will only emit if `can_emit_warnings` is `true`. #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_warn( @@ -730,9 +728,7 @@ impl DiagCtxt { /// Construct a builder at the `Warning` level with the `msg`. /// - /// Attempting to `.emit()` the builder will only emit if either: - /// * `can_emit_warnings` is `true` - /// * `is_force_warn` was set in `DiagnosticId::Lint` + /// An `emit` call on the builder will only emit if `can_emit_warnings` is `true`. #[rustc_lint_diagnostics] #[track_caller] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { @@ -1011,11 +1007,12 @@ impl DiagCtxt { let mut error_codes = inner .emitted_diagnostic_codes .iter() - .filter_map(|x| match &x { - DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => { - Some(s.clone()) + .filter_map(|code| { + if registry.try_find_description(code).is_ok().clone() { + Some(code.clone()) + } else { + None } - _ => None, }) .collect::<Vec<_>>(); if !error_codes.is_empty() { @@ -1058,8 +1055,8 @@ impl DiagCtxt { /// /// Used to suppress emitting the same error multiple times with extended explanation when /// calling `-Zteach`. - pub fn must_teach(&self, code: &DiagnosticId) -> bool { - self.inner.borrow_mut().taught_diagnostics.insert(code.clone()) + pub fn must_teach(&self, code: &str) -> bool { + self.inner.borrow_mut().taught_diagnostics.insert(code.to_string()) } pub fn force_print_diagnostic(&self, db: Diagnostic) { @@ -1231,7 +1228,7 @@ impl DiagCtxtInner { for diag in diags { // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { - if diag.is_lint { + if diag.is_lint.is_some() { self.lint_err_count -= 1; } else { self.err_count -= 1; @@ -1363,7 +1360,7 @@ impl DiagCtxtInner { self.has_printed = true; } if diagnostic.is_error() { - if diagnostic.is_lint { + if diagnostic.is_lint.is_some() { self.lint_err_count += 1; } else { self.err_count += 1; diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 475dd348e7b..3e3b4814300 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -16,6 +16,9 @@ expand_attributes_wrong_form = expand_cannot_be_name_of_macro = `{$trait_ident}` cannot be a name of {$macro_type} macro +expand_collapse_debuginfo_illegal = + illegal value for attribute #[collapse_debuginfo(no|external|yes)] + expand_count_repetition_misplaced = `count` can not be placed inside the inner-most repetition diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0a1c4430397..29b70f33a6c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,5 +1,6 @@ #![deny(rustc::untranslatable_diagnostic)] +use crate::base::ast::NestedMetaItem; use crate::errors; use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirOwnership; @@ -19,6 +20,7 @@ use rustc_feature::Features; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools}; use rustc_parse::{parser, MACRO_ARGUMENTS}; +use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::errors::report_lit_error; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; @@ -664,8 +666,8 @@ pub enum SyntaxExtensionKind { /// A token-based attribute macro. Attr( /// An expander with signature (TokenStream, TokenStream) -> TokenStream. - /// The first TokenSteam is the attribute itself, the second is the annotated item. - /// The produced TokenSteam replaces the input TokenSteam. + /// The first TokenStream is the attribute itself, the second is the annotated item. + /// The produced TokenStream replaces the input TokenStream. Box<dyn AttrProcMacro + sync::DynSync + sync::DynSend>, ), @@ -685,7 +687,7 @@ pub enum SyntaxExtensionKind { /// A token-based derive macro. Derive( /// An expander with signature TokenStream -> TokenStream. - /// The produced TokenSteam is appended to the input TokenSteam. + /// The produced TokenStream is appended to the input TokenStream. /// /// FIXME: The text above describes how this should work. Currently it /// is handled identically to `LegacyDerive`. It should be migrated to @@ -761,6 +763,55 @@ impl SyntaxExtension { } } + fn collapse_debuginfo_by_name(sess: &Session, attr: &Attribute) -> CollapseMacroDebuginfo { + use crate::errors::CollapseMacroDebuginfoIllegal; + // #[collapse_debuginfo] without enum value (#[collapse_debuginfo(no/external/yes)]) + // considered as `yes` + attr.meta_item_list().map_or(CollapseMacroDebuginfo::Yes, |l| { + let [NestedMetaItem::MetaItem(item)] = &l[..] else { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: attr.span }); + return CollapseMacroDebuginfo::Unspecified; + }; + if !item.is_word() { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); + CollapseMacroDebuginfo::Unspecified + } else { + match item.name_or_empty() { + sym::no => CollapseMacroDebuginfo::No, + sym::external => CollapseMacroDebuginfo::External, + sym::yes => CollapseMacroDebuginfo::Yes, + _ => { + sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); + CollapseMacroDebuginfo::Unspecified + } + } + } + }) + } + + /// if-ext - if macro from different crate (related to callsite code) + /// | cmd \ attr | no | (unspecified) | external | yes | + /// | no | no | no | no | no | + /// | (unspecified) | no | no | if-ext | yes | + /// | external | no | if-ext | if-ext | yes | + /// | yes | yes | yes | yes | yes | + fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], is_local: bool) -> bool { + let collapse_debuginfo_attr = attr::find_by_name(attrs, sym::collapse_debuginfo) + .map(|v| Self::collapse_debuginfo_by_name(sess, v)) + .unwrap_or(CollapseMacroDebuginfo::Unspecified); + let flag = sess.opts.unstable_opts.collapse_macro_debuginfo; + let attr = collapse_debuginfo_attr; + let ext = !is_local; + #[rustfmt::skip] + let collapse_table = [ + [false, false, false, false], + [false, false, ext, true], + [false, ext, ext, true], + [true, true, true, true], + ]; + collapse_table[flag as usize][attr as usize] + } + /// Constructs a syntax extension with the given properties /// and other properties converted from attributes. pub fn new( @@ -772,6 +823,7 @@ impl SyntaxExtension { edition: Edition, name: Symbol, attrs: &[ast::Attribute], + is_local: bool, ) -> SyntaxExtension { let allow_internal_unstable = attr::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>(); @@ -780,8 +832,8 @@ impl SyntaxExtension { let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); - let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo); - tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); + let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, is_local); + tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro) .map(|attr| { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 2b43fae6852..4a1c00f0104 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -59,6 +59,13 @@ pub(crate) struct ResolveRelativePath { } #[derive(Diagnostic)] +#[diag(expand_collapse_debuginfo_illegal)] +pub(crate) struct CollapseMacroDebuginfoIllegal { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(expand_macro_const_stability)] pub(crate) struct MacroConstStability { #[primary_span] diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 363b52aef8a..1a39708ed8e 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -367,6 +367,7 @@ pub fn compile_declarative_macro( edition, def.ident.name, &def.attrs, + def.id != DUMMY_NODE_ID, ) }; let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new()); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 4442b67df6e..68b6f69854d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -469,7 +469,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // `#[collapse_debuginfo]` gated!( - collapse_debuginfo, Normal, template!(Word), WarnFollowing, + collapse_debuginfo, Normal, template!(Word, List: "no|external|yes"), ErrorFollowing, experimental!(collapse_debuginfo) ), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 2f2b551e6ec..371b651f5e8 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -549,7 +549,9 @@ declare_features! ( /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. (unstable, object_safe_for_dispatch, "1.40.0", Some(43561)), /// Allows using enums in offset_of! - (unstable, offset_of_enum, "1.75.0", Some(106655)), + (unstable, offset_of_enum, "1.75.0", Some(120141)), + /// Allows using multiple nested field accesses in offset_of! + (unstable, offset_of_nested, "CURRENT_RUSTC_VERSION", Some(120140)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows macro attributes on expressions, statements and non-inline modules. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b347f7035a..23881cfd7df 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -248,7 +248,7 @@ pub struct InferArg { } impl InferArg { - pub fn to_ty(&self) -> Ty<'_> { + pub fn to_ty(&self) -> Ty<'static> { Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id } } } @@ -841,7 +841,7 @@ pub struct OwnerNodes<'tcx> { } impl<'tcx> OwnerNodes<'tcx> { - fn node(&self) -> OwnerNode<'tcx> { + pub fn node(&self) -> OwnerNode<'tcx> { use rustc_index::Idx; let node = self.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. @@ -1015,7 +1015,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true, + Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), @@ -1042,7 +1042,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {} + Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), @@ -1205,6 +1205,9 @@ pub enum PatKind<'hir> { /// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)]) /// ``` Slice(&'hir [Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [Pat<'hir>]), + + /// A placeholder for a pattern that wasn't well formed in some way. + Err(ErrorGuaranteed), } /// A statement. @@ -1302,7 +1305,7 @@ pub enum UnsafeSource { UserProvided, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct BodyId { pub hir_id: HirId, } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index adc09025809..116de6fb04d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -655,7 +655,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) { walk_list!(visitor, visit_expr, lower_bound); walk_list!(visitor, visit_expr, upper_bound); } - PatKind::Never | PatKind::Wild => (), + PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { walk_list!(visitor, visit_pat, prepatterns); walk_list!(visitor, visit_pat, slice_pattern); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3f3b57ba94f..1cc1f11b3c8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -374,3 +374,30 @@ pub static OPERATORS: &'static [LangItem] = &[ LangItem::PartialEq, LangItem::PartialOrd, ]; + +pub static BINARY_OPERATORS: &'static [LangItem] = &[ + LangItem::Add, + LangItem::Sub, + LangItem::Mul, + LangItem::Div, + LangItem::Rem, + LangItem::BitXor, + LangItem::BitAnd, + LangItem::BitOr, + LangItem::Shl, + LangItem::Shr, + LangItem::AddAssign, + LangItem::SubAssign, + LangItem::MulAssign, + LangItem::DivAssign, + LangItem::RemAssign, + LangItem::BitXorAssign, + LangItem::BitAndAssign, + LangItem::BitOrAssign, + LangItem::ShlAssign, + LangItem::ShrAssign, + LangItem::Index, + LangItem::IndexMut, + LangItem::PartialEq, + LangItem::PartialOrd, +]; diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 97fa710b354..baa1635f731 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -12,7 +12,6 @@ use rustc_span::def_id::DefPathHash; pub trait HashStableContext: rustc_ast::HashStableContext + rustc_target::HashStableContext { - fn hash_body_id(&mut self, _: BodyId, hasher: &mut StableHasher); } impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId { @@ -80,12 +79,6 @@ impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for ForeignItemId } } -impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for BodyId { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - hcx.hash_body_id(*self, hasher) - } -} - // The following implementations of HashStable for `ItemId`, `TraitItemId`, and // `ImplItemId` deserve special attention. Normally we do not hash `NodeId`s within // the HIR, since they just signify a HIR nodes own path. But `ItemId` et al diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index b5ebc1fab76..648b569a217 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start +itertools = "0.11" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 2ad96a24891..401efff9242 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -108,14 +108,16 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` /// for more details. #[instrument(level = "debug", skip(self, ast_bounds, bounds))] - pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>( + pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'tcx>>>( &self, param_ty: Ty<'tcx>, ast_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List<ty::BoundVariableKind>, only_self_bounds: OnlySelfBounds, - ) { + ) where + 'tcx: 'hir, + { for ast_bound in ast_bounds { match ast_bound { hir::GenericBound::Trait(poly_trait_ref, modifier) => { @@ -179,7 +181,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { pub(crate) fn compute_bounds( &self, param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'_>], + ast_bounds: &[hir::GenericBound<'tcx>], filter: PredicateFilter, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index e2cd4d5f21c..f9628d1d6f3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -168,7 +168,7 @@ fn generic_arg_mismatch_err( /// instantiate a `GenericArg`. /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then /// creates a suitable inference variable. -pub fn create_args_for_parent_generic_args<'tcx, 'a>( +pub fn create_args_for_parent_generic_args<'tcx: 'a, 'a>( tcx: TyCtxt<'tcx>, def_id: DefId, parent_args: &[ty::GenericArg<'tcx>], diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index 3761d529517..b5f42e98127 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -78,14 +78,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool { let tcx = self.tcx(); let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; - let (hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - })) = tcx.hir_node_by_def_id(parent_id) - else { - return false; + let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { + (sig, generics, None) + } + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + owner_id, + .. + }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + _ => return false, }; let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return false; @@ -94,6 +97,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { objects.iter().all(|o| match o.trait_ref.path.res { + Res::Def(DefKind::Trait, id) if Some(id) == owner => { + // When we're dealing with a recursive trait, we don't want to downgrade + // the error, so we consider them to be object safe always. (#119652) + true + } Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id), _ => false, }) @@ -122,7 +130,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ], Applicability::MachineApplicable, ); - } else { + } else if diag.is_error() { // We'll emit the object safety error already, with a structured suggestion. diag.downgrade_to_delayed_bug(); } @@ -148,8 +156,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if !is_object_safe { diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); - // We'll emit the object safety error already, with a structured suggestion. - diag.downgrade_to_delayed_bug(); + if diag.is_error() { + // We'll emit the object safety error already, with a structured suggestion. + diag.downgrade_to_delayed_bug(); + } } else { let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 9f4f1413650..00f0d6e7c22 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -122,7 +122,7 @@ pub trait AstConv<'tcx> { &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx>; @@ -156,14 +156,14 @@ struct ConvertedBinding<'a, 'tcx> { hir_id: hir::HirId, item_name: Ident, kind: ConvertedBindingKind<'a, 'tcx>, - gen_args: &'a GenericArgs<'a>, + gen_args: &'tcx GenericArgs<'tcx>, span: Span, } #[derive(Debug)] enum ConvertedBindingKind<'a, 'tcx> { Equality(Spanned<ty::Term<'tcx>>), - Constraint(&'a [hir::GenericBound<'a>]), + Constraint(&'a [hir::GenericBound<'tcx>]), } /// New-typed boolean indicating whether explicit late-bound lifetimes @@ -215,12 +215,12 @@ pub struct GenericArgCountResult { } pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> { - fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool); + fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool); fn provided_kind( &mut self, param: &ty::GenericParamDef, - arg: &GenericArg<'_>, + arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx>; fn inferred_kind( @@ -294,7 +294,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, span: Span, def_id: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { let (args, _) = self.create_args_for_ast_path( span, @@ -351,7 +351,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def_id: DefId, parent_args: &[ty::GenericArg<'tcx>], seg: &hir::PathSegment<'_>, - generic_args: &'a hir::GenericArgs<'_>, + generic_args: &'a hir::GenericArgs<'tcx>, infer_args: bool, self_ty: Option<Ty<'tcx>>, constness: ty::BoundConstness, @@ -406,14 +406,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { struct SubstsForAstPathCtxt<'a, 'tcx> { astconv: &'a (dyn AstConv<'tcx> + 'a), def_id: DefId, - generic_args: &'a GenericArgs<'a>, + generic_args: &'a GenericArgs<'tcx>, span: Span, inferred_params: Vec<Span>, infer_args: bool, } impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> { - fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) { + fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool) { if did == self.def_id { (Some(self.generic_args), self.infer_args) } else { @@ -425,11 +425,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn provided_kind( &mut self, param: &ty::GenericParamDef, - arg: &GenericArg<'_>, + arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { let tcx = self.astconv.tcx(); - let mut handle_ty_args = |has_default, ty: &hir::Ty<'_>| { + let mut handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { if has_default { tcx.check_optional_stability( param.def_id, @@ -592,7 +592,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn create_assoc_bindings_for_generic_args<'a>( &self, - generic_args: &'a hir::GenericArgs<'_>, + generic_args: &'a hir::GenericArgs<'tcx>, ) -> Vec<ConvertedBinding<'a, 'tcx>> { // Convert associated-type bindings or constraints into a separate vector. // Example: Given this: @@ -640,7 +640,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { debug!( @@ -673,7 +673,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// are disallowed. Otherwise, they are pushed onto the vector given. pub fn instantiate_mono_trait_ref( &self, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &hir::TraitRef<'tcx>, self_ty: Ty<'tcx>, ) -> ty::TraitRef<'tcx> { self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); @@ -710,7 +710,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))] pub(crate) fn instantiate_poly_trait_ref( &self, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &hir::TraitRef<'tcx>, span: Span, constness: ty::BoundConstness, polarity: ty::ImplPolarity, @@ -788,7 +788,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, - trait_segment: &hir::PathSegment<'_>, + trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, // FIXME(effects) move all host param things in astconv to hir lowering constness: ty::BoundConstness, @@ -813,7 +813,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, - trait_segment: &'a hir::PathSegment<'a>, + trait_segment: &'a hir::PathSegment<'tcx>, is_impl: bool, constness: ty::BoundConstness, ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { @@ -847,7 +847,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, span: Span, did: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx(); let args = self.ast_path_args_for_ty(span, did, item_segment); @@ -1153,7 +1153,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, qself_ty: Ty<'tcx>, qself: &hir::Ty<'_>, - assoc_segment: &hir::PathSegment<'_>, + assoc_segment: &hir::PathSegment<'tcx>, permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { let tcx = self.tcx(); @@ -1428,7 +1428,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn lookup_inherent_assoc_ty( &self, name: Ident, - segment: &hir::PathSegment<'_>, + segment: &hir::PathSegment<'tcx>, adt_did: DefId, self_ty: Ty<'tcx>, block: hir::HirId, @@ -1446,7 +1446,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } let candidates: Vec<_> = tcx - .inherent_impls(adt_did) + .inherent_impls(adt_did)? .iter() .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?))) .collect(); @@ -1702,8 +1702,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, opt_self_ty: Option<Ty<'tcx>>, item_def_id: DefId, - trait_segment: &hir::PathSegment<'_>, - item_segment: &hir::PathSegment<'_>, + trait_segment: &hir::PathSegment<'tcx>, + item_segment: &hir::PathSegment<'tcx>, constness: ty::BoundConstness, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -2021,7 +2021,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn res_to_ty( &self, opt_self_ty: Option<Ty<'tcx>>, - path: &hir::Path<'_>, + path: &hir::Path<'tcx>, hir_id: hir::HirId, permit_variants: bool, ) -> Ty<'tcx> { @@ -2311,13 +2311,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Parses the programmer's textual representation of a type into our /// internal notion of a type. - pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { self.ast_ty_to_ty_inner(ast_ty, false, false) } /// Parses the programmer's textual representation of a type into our /// internal notion of a type. This is meant to be used within a path. - pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { self.ast_ty_to_ty_inner(ast_ty, false, true) } @@ -2432,7 +2432,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. #[instrument(level = "debug", skip(self), ret)] - fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> { + fn ast_ty_to_ty_inner( + &self, + ast_ty: &hir::Ty<'tcx>, + borrowed: bool, + in_path: bool, + ) -> Ty<'tcx> { let tcx = self.tcx(); let result_ty = match &ast_ty.kind { @@ -2609,7 +2614,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { + pub fn ty_of_arg(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { match ty.kind { hir::TyKind::Infer if expected_ty.is_some() => { self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span); @@ -2625,7 +2630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir_id: hir::HirId, unsafety: hir::Unsafety, abi: abi::Abi, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, generics: Option<&hir::Generics<'_>>, hir_ty: Option<&hir::Ty<'_>>, ) -> ty::PolyFnSig<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index f77f250cd28..8967b51749c 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -22,7 +22,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'_>], + hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, borrowed: bool, representation: DynKind, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 77914802bf7..03e2b0e0022 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -368,7 +368,7 @@ fn check_opaque_meets_bounds<'tcx>( // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias { .. } => { let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys); + let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; } 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 469e7a6a13c..5b264f6f034 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2,7 +2,7 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_errors::{pluralize, struct_span_code_err, Applicability, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -378,7 +378,7 @@ fn compare_method_predicate_entailment<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { @@ -702,7 +702,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // lifetime parameters. let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys), + infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), ); ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; @@ -1382,7 +1382,7 @@ fn compare_number_of_generics<'tcx>( kind = kind, ), ); - err.code(DiagnosticId::Error("E0049".into())); + err.code("E0049".into()); let msg = format!("expected {trait_count} {kind} parameter{}", pluralize!(trait_count),); @@ -2070,7 +2070,7 @@ pub(super) fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types); + let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index f7fc0c81b95..0d8de0cabd1 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -158,7 +158,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>( } let outlives_env = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types), + infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types), ); let errors = infcx.resolve_regions(&outlives_env); if !errors.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 5d5a4789734..1c0a1a69513 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -681,7 +681,8 @@ fn resolve_local<'tcx>( | PatKind::Never | PatKind::Path(_) | PatKind::Lit(_) - | PatKind::Range(_, _, _) => false, + | PatKind::Range(_, _, _) + | PatKind::Err(_) => false, } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4772bae58c4..3b9837ff7c2 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -15,6 +15,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::query::Providers; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, @@ -112,8 +113,6 @@ where let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types); - let errors = wfcx.select_all_or_error(); if !errors.is_empty() { let err = infcx.err_ctxt().report_fulfillment_errors(errors); @@ -128,10 +127,65 @@ where } } + debug!(?assumed_wf_types); + + let infcx_compat = infcx.fork(); + + // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?; - infcx.tainted_by_errors().error_reported() + let errors = infcx.resolve_regions(&outlives_env); + if errors.is_empty() { + return Ok(()); + } + + let is_bevy = 'is_bevy: { + // We don't want to emit this for dependents of Bevy, for now. + // See #119956 + let is_bevy_paramset = |def: ty::AdtDef<'_>| { + let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did)); + adt_did.contains("ParamSet") + }; + for ty in assumed_wf_types.iter() { + match ty.kind() { + ty::Adt(def, _) => { + if is_bevy_paramset(*def) { + break 'is_bevy true; + } + } + ty::Ref(_, ty, _) => match ty.kind() { + ty::Adt(def, _) => { + if is_bevy_paramset(*def) { + break 'is_bevy true; + } + } + _ => {} + }, + _ => {} + } + } + false + }; + + // If we have set `no_implied_bounds_compat`, then do not attempt compatibility. + // We could also just always enter if `is_bevy`, and call `implied_bounds_tys`, + // but that does result in slightly more work when this option is set and + // just obscures what we mean here anyways. Let's just be explicit. + if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { + let implied_bounds = + infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let errors_compat = infcx_compat.resolve_regions(&outlives_env); + if errors_compat.is_empty() { + Ok(()) + } else { + Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat)) + } + } else { + Err(infcx.err_ctxt().report_region_errors(body_def_id, &errors)) + } } fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> { @@ -723,7 +777,7 @@ fn resolve_regions_with_wf_tys<'tcx>( let infcx = tcx.infer_ctxt().build(); let outlives_environment = OutlivesEnvironment::with_bounds( param_env, - infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + infcx.implied_bounds_tys(param_env, id, wf_tys), ); let region_bound_pairs = outlives_environment.region_bound_pairs(); @@ -1736,7 +1790,7 @@ fn receiver_is_implemented<'tcx>( fn check_variances_for_type_defn<'tcx>( tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, - hir_generics: &hir::Generics<'_>, + hir_generics: &hir::Generics<'tcx>, ) { let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 3162004a634..abef365c3ca 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -13,32 +13,41 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; use rustc_span::symbol::sym; +use rustc_span::ErrorGuaranteed; use crate::errors; /// On-demand query: yields a map containing all types mapped to their inherent impls. -pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls { +pub fn crate_inherent_impls( + tcx: TyCtxt<'_>, + (): (), +) -> Result<&'_ CrateInherentImpls, ErrorGuaranteed> { let mut collect = InherentCollect { tcx, impls_map: Default::default() }; + let mut res = Ok(()); for id in tcx.hir().items() { - collect.check_item(id); + res = res.and(collect.check_item(id)); } - collect.impls_map + res?; + Ok(tcx.arena.alloc(collect.impls_map)) } -pub fn crate_incoherent_impls(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { - let crate_map = tcx.crate_inherent_impls(()); - tcx.arena.alloc_from_iter( +pub fn crate_incoherent_impls( + tcx: TyCtxt<'_>, + simp: SimplifiedType, +) -> Result<&[DefId], ErrorGuaranteed> { + let crate_map = tcx.crate_inherent_impls(())?; + Ok(tcx.arena.alloc_from_iter( crate_map.incoherent_impls.get(&simp).unwrap_or(&Vec::new()).iter().map(|d| d.to_def_id()), - ) + )) } /// On-demand query: yields a vector of the inherent impls for a specific type. -pub fn inherent_impls(tcx: TyCtxt<'_>, ty_def_id: LocalDefId) -> &[DefId] { - let crate_map = tcx.crate_inherent_impls(()); - match crate_map.inherent_impls.get(&ty_def_id) { +pub fn inherent_impls(tcx: TyCtxt<'_>, ty_def_id: LocalDefId) -> Result<&[DefId], ErrorGuaranteed> { + let crate_map = tcx.crate_inherent_impls(())?; + Ok(match crate_map.inherent_impls.get(&ty_def_id) { Some(v) => &v[..], None => &[], - } + }) } struct InherentCollect<'tcx> { @@ -47,14 +56,19 @@ struct InherentCollect<'tcx> { } impl<'tcx> InherentCollect<'tcx> { - fn check_def_id(&mut self, impl_def_id: LocalDefId, self_ty: Ty<'tcx>, ty_def_id: DefId) { + fn check_def_id( + &mut self, + impl_def_id: LocalDefId, + self_ty: Ty<'tcx>, + ty_def_id: DefId, + ) -> Result<(), ErrorGuaranteed> { if let Some(ty_def_id) = ty_def_id.as_local() { // Add the implementation to the mapping from implementation to base // type def ID, if there is a base type for this implementation and // the implementation does not have any associated traits. let vec = self.impls_map.inherent_impls.entry(ty_def_id).or_default(); vec.push(impl_def_id.to_def_id()); - return; + return Ok(()); } if self.tcx.features().rustc_attrs { @@ -62,18 +76,16 @@ impl<'tcx> InherentCollect<'tcx> { if !self.tcx.has_attr(ty_def_id, sym::rustc_has_incoherent_inherent_impls) { let impl_span = self.tcx.def_span(impl_def_id); - self.tcx.dcx().emit_err(errors::InherentTyOutside { span: impl_span }); - return; + return Err(self.tcx.dcx().emit_err(errors::InherentTyOutside { span: impl_span })); } for &impl_item in items { if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) { let impl_span = self.tcx.def_span(impl_def_id); - self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant { + return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant { span: impl_span, help_span: self.tcx.def_span(impl_item), - }); - return; + })); } } @@ -82,24 +94,28 @@ impl<'tcx> InherentCollect<'tcx> { } else { bug!("unexpected self type: {:?}", self_ty); } + Ok(()) } else { let impl_span = self.tcx.def_span(impl_def_id); - self.tcx.dcx().emit_err(errors::InherentTyOutsideNew { span: impl_span }); + Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideNew { span: impl_span })) } } - fn check_primitive_impl(&mut self, impl_def_id: LocalDefId, ty: Ty<'tcx>) { + fn check_primitive_impl( + &mut self, + impl_def_id: LocalDefId, + ty: Ty<'tcx>, + ) -> Result<(), ErrorGuaranteed> { let items = self.tcx.associated_item_def_ids(impl_def_id); if !self.tcx.hir().rustc_coherence_is_core() { if self.tcx.features().rustc_attrs { for &impl_item in items { if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) { let span = self.tcx.def_span(impl_def_id); - self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive { + return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive { span, help_span: self.tcx.def_span(impl_item), - }); - return; + })); } } } else { @@ -108,8 +124,7 @@ impl<'tcx> InherentCollect<'tcx> { if let ty::Ref(_, subty, _) = ty.kind() { note = Some(errors::InherentPrimitiveTyNote { subty: *subty }); } - self.tcx.dcx().emit_err(errors::InherentPrimitiveTy { span, note }); - return; + return Err(self.tcx.dcx().emit_err(errors::InherentPrimitiveTy { span, note })); } } @@ -118,11 +133,12 @@ impl<'tcx> InherentCollect<'tcx> { } else { bug!("unexpected primitive type: {:?}", ty); } + Ok(()) } - fn check_item(&mut self, id: hir::ItemId) { + fn check_item(&mut self, id: hir::ItemId) -> Result<(), ErrorGuaranteed> { if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl { of_trait: false }) { - return; + return Ok(()); } let id = id.owner_id.def_id; @@ -132,10 +148,10 @@ impl<'tcx> InherentCollect<'tcx> { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { - self.check_def_id(id, self_ty, data.principal_def_id().unwrap()); + self.check_def_id(id, self_ty, data.principal_def_id().unwrap()) } ty::Dynamic(..) => { - self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span }); + Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } ty::Bool | ty::Char @@ -151,7 +167,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::FnPtr(_) | ty::Tuple(..) => self.check_primitive_impl(id, self_ty), ty::Alias(..) | ty::Param(_) => { - self.tcx.dcx().emit_err(errors::InherentNominal { span: item_span }); + Err(self.tcx.dcx().emit_err(errors::InherentNominal { span: item_span })) } ty::FnDef(..) | ty::Closure(..) @@ -162,7 +178,8 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Infer(_) => { bug!("unexpected impl self type of impl: {:?} {:?}", id, self_ty); } - ty::Error(_) => {} + // We could bail out here, but that will silence other useful errors. + ty::Error(_) => Ok(()), } } } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 4c3455c7240..63ea0272014 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -6,16 +6,18 @@ use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Symbol; +use rustc_span::{ErrorGuaranteed, Symbol}; use rustc_trait_selection::traits::{self, SkipLeakCheck}; use smallvec::SmallVec; use std::collections::hash_map::Entry; -pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) { +pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuaranteed> { let mut inherent_overlap_checker = InherentOverlapChecker { tcx }; + let mut res = Ok(()); for id in tcx.hir().items() { - inherent_overlap_checker.check_item(id); + res = res.and(inherent_overlap_checker.check_item(id)); } + res } struct InherentOverlapChecker<'tcx> { @@ -58,10 +60,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> { == item2.ident(self.tcx).normalize_to_macros_2_0() } - fn check_for_duplicate_items_in_impl(&self, impl_: DefId) { + fn check_for_duplicate_items_in_impl(&self, impl_: DefId) -> Result<(), ErrorGuaranteed> { let impl_items = self.tcx.associated_items(impl_); let mut seen_items = FxHashMap::default(); + let mut res = Ok(()); for impl_item in impl_items.in_definition_order() { let span = self.tcx.def_span(impl_item.def_id); let ident = impl_item.ident(self.tcx); @@ -70,7 +73,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { match seen_items.entry(norm_ident) { Entry::Occupied(entry) => { let former = entry.get(); - struct_span_code_err!( + res = Err(struct_span_code_err!( self.tcx.dcx(), span, E0592, @@ -79,13 +82,14 @@ impl<'tcx> InherentOverlapChecker<'tcx> { ) .with_span_label(span, format!("duplicate definitions for `{ident}`")) .with_span_label(*former, format!("other definition for `{ident}`")) - .emit(); + .emit()); } Entry::Vacant(entry) => { entry.insert(span); } } } + res } fn check_for_common_items_in_impls( @@ -93,10 +97,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> { impl1: DefId, impl2: DefId, overlap: traits::OverlapResult<'_>, - ) { + ) -> Result<(), ErrorGuaranteed> { let impl_items1 = self.tcx.associated_items(impl1); let impl_items2 = self.tcx.associated_items(impl2); + let mut res = Ok(()); for &item1 in impl_items1.in_definition_order() { let collision = impl_items2 .filter_by_name_unhygienic(item1.name) @@ -128,9 +133,10 @@ impl<'tcx> InherentOverlapChecker<'tcx> { traits::add_placeholder_note(&mut err); } - err.emit(); + res = Err(err.emit()); } } + res } fn check_for_overlapping_inherent_impls( @@ -138,7 +144,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { overlap_mode: OverlapMode, impl1_def_id: DefId, impl2_def_id: DefId, - ) { + ) -> Result<(), ErrorGuaranteed> { let maybe_overlap = traits::overlapping_impls( self.tcx, impl1_def_id, @@ -150,17 +156,19 @@ impl<'tcx> InherentOverlapChecker<'tcx> { ); if let Some(overlap) = maybe_overlap { - self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); + self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap) + } else { + Ok(()) } } - fn check_item(&mut self, id: hir::ItemId) { + fn check_item(&mut self, id: hir::ItemId) -> Result<(), ErrorGuaranteed> { let def_kind = self.tcx.def_kind(id.owner_id); if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { - return; + return Ok(()); } - let impls = self.tcx.inherent_impls(id.owner_id); + let impls = self.tcx.inherent_impls(id.owner_id)?; let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id()); @@ -173,17 +181,18 @@ impl<'tcx> InherentOverlapChecker<'tcx> { // otherwise switch to an allocating algorithm with // faster asymptotic runtime. const ALLOCATING_ALGO_THRESHOLD: usize = 500; + let mut res = Ok(()); if impls.len() < ALLOCATING_ALGO_THRESHOLD { for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { - self.check_for_duplicate_items_in_impl(impl1_def_id); + res = res.and(self.check_for_duplicate_items_in_impl(impl1_def_id)); for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( + res = res.and(self.check_for_overlapping_inherent_impls( overlap_mode, impl1_def_id, impl2_def_id, - ); + )); } } } @@ -315,20 +324,21 @@ impl<'tcx> InherentOverlapChecker<'tcx> { impl_blocks.sort_unstable(); for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; - self.check_for_duplicate_items_in_impl(impl1_def_id); + res = res.and(self.check_for_duplicate_items_in_impl(impl1_def_id)); for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( + res = res.and(self.check_for_overlapping_inherent_impls( overlap_mode, impl1_def_id, impl2_def_id, - ); + )); } } } } } + res } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e557f36037b..d8ce2307995 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -348,7 +348,7 @@ impl<'tcx> ItemCtxt<'tcx> { ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } } - pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + pub fn to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { self.astconv().ast_ty_to_ty(ast_ty) } @@ -412,7 +412,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { @@ -1148,7 +1148,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<ty::PolyFnSig< fn infer_return_ty_for_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, - sig: &hir::FnSig<'_>, + sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'_>, def_id: LocalDefId, icx: &ItemCtxt<'tcx>, @@ -1352,14 +1352,14 @@ fn impl_trait_ref( 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(&args); + path_segments[last_segment].args = Some(tcx.hir_arena.alloc(args)); let path = hir::Path { span: ast_trait_ref.path.span, res: ast_trait_ref.path.res, - segments: &path_segments, + segments: tcx.hir_arena.alloc_slice(&path_segments), }; - let trait_ref = hir::TraitRef { path: &path, hir_ref_id: ast_trait_ref.hir_ref_id }; - icx.astconv().instantiate_mono_trait_ref(&trait_ref, selfty) + let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: ast_trait_ref.hir_ref_id }); + icx.astconv().instantiate_mono_trait_ref(trait_ref, selfty) } else { icx.astconv().instantiate_mono_trait_ref(ast_trait_ref, selfty) } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 1f7ca48234a..85093bc12b3 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -5,20 +5,22 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, def, Expr, ImplItem, Item, Node, TraitItem}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; use crate::errors::{TaitForwardCompat, TypeOf, UnconstrainedOpaqueType}; -pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) { +pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { + let mut res = Ok(()); if tcx.has_attr(CRATE_DEF_ID, sym::rustc_hidden_type_of_opaques) { for id in tcx.hir().items() { if matches!(tcx.def_kind(id.owner_id), DefKind::OpaqueTy) { let type_of = tcx.type_of(id.owner_id).instantiate_identity(); - tcx.dcx().emit_err(TypeOf { span: tcx.def_span(id.owner_id), type_of }); + res = Err(tcx.dcx().emit_err(TypeOf { span: tcx.def_span(id.owner_id), type_of })); } } } + res } /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions @@ -135,18 +137,25 @@ impl TaitConstraintLocator<'_> { return; } - if let Some(hir_sig) = self.tcx.hir_node_by_def_id(item_def_id).fn_decl() { - if hir_sig.output.get_infer_ret_ty().is_some() { - let guar = self.tcx.dcx().span_delayed_bug( - hir_sig.output.span(), - "inferring return types and opaque types do not mix well", - ); - self.found = Some(ty::OpaqueHiddenType { - span: DUMMY_SP, - ty: Ty::new_error(self.tcx, guar), - }); - return; - } + // Function items with `_` in their return type already emit an error, skip any + // "non-defining use" errors for them. + // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former + // excludes closures, which are allowed to have `_` in their return type. + let hir_node = self.tcx.hir_node_by_def_id(item_def_id); + debug_assert!( + !matches!(hir_node, Node::ForeignItem(..)), + "foreign items cannot constrain opaque types", + ); + if let Some(hir_sig) = hir_node.fn_sig() + && hir_sig.decl.output.get_infer_ret_ty().is_some() + { + let guar = self.tcx.dcx().span_delayed_bug( + hir_sig.decl.output.span(), + "inferring return types and opaque types do not mix well", + ); + self.found = + Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) }); + return; } // Calling `mir_borrowck` can lead to cycle errors through diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 3f9b1f384d7..2fe08ead72b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -17,7 +17,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::{Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; mod min_specialization; @@ -51,24 +51,29 @@ mod min_specialization; /// impl<'a> Trait<Foo> for Bar { type X = &'a i32; } /// // ^ 'a is unused and appears in assoc type, error /// ``` -fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { +fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) -> Result<(), ErrorGuaranteed> { let min_specialization = tcx.features().min_specialization; let module = tcx.hir_module_items(module_def_id); + let mut res = Ok(()); for id in module.items() { if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) { - enforce_impl_params_are_constrained(tcx, id.owner_id.def_id); + res = res.and(enforce_impl_params_are_constrained(tcx, id.owner_id.def_id)); if min_specialization { - check_min_specialization(tcx, id.owner_id.def_id); + res = res.and(check_min_specialization(tcx, id.owner_id.def_id)); } } } + res } pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_impl_wf, ..*providers }; } -fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { +fn enforce_impl_params_are_constrained( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { // Every lifetime used in an associated type must be constrained. let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); if impl_self_ty.references_error() { @@ -80,7 +85,10 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}", ), ); - return; + // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on + // `type_of` having been called much earlier, and thus this value being read from cache. + // Compilation must continue in order for other important diagnostics to keep showing up. + return Ok(()); } let impl_generics = tcx.generics_of(impl_def_id); let impl_predicates = tcx.predicates_of(impl_def_id); @@ -113,13 +121,19 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) }) .collect(); + let mut res = Ok(()); for param in &impl_generics.params { match param.kind { // Disallow ANY unconstrained type parameters. ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name); + res = Err(report_unused_parameter( + tcx, + tcx.def_span(param.def_id), + "type", + param_ty.name, + )); } } ty::GenericParamDefKind::Lifetime => { @@ -127,27 +141,28 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) if lifetimes_in_associated_types.contains(¶m_lt) && // (*) !input_parameters.contains(¶m_lt) { - report_unused_parameter( + res = Err(report_unused_parameter( tcx, tcx.def_span(param.def_id), "lifetime", param.name, - ); + )); } } ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { - report_unused_parameter( + res = Err(report_unused_parameter( tcx, tcx.def_span(param.def_id), "const", param_ct.name, - ); + )); } } } } + res // (*) This is a horrible concession to reality. I think it'd be // better to just ban unconstrained lifetimes outright, but in @@ -169,7 +184,12 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) // used elsewhere are not projected back out. } -fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) { +fn report_unused_parameter( + tcx: TyCtxt<'_>, + span: Span, + kind: &str, + name: Symbol, +) -> ErrorGuaranteed { let mut err = struct_span_code_err!( tcx.dcx(), span, @@ -188,5 +208,5 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol "proving the result of expressions other than the parameter are unique is not supported", ); } - err.emit(); + err.emit() } 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 93dd2342a4d..1b6a39d8162 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 @@ -82,10 +82,14 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt}; -pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { +pub(super) fn check_min_specialization( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { - check_always_applicable(tcx, impl_def_id, node); + check_always_applicable(tcx, impl_def_id, node)?; } + Ok(()) } fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> { @@ -109,42 +113,58 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti /// Check that `impl1` is a sound specialization #[instrument(level = "debug", skip(tcx))] -fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) { +fn check_always_applicable( + tcx: TyCtxt<'_>, + impl1_def_id: LocalDefId, + impl2_node: Node, +) -> Result<(), ErrorGuaranteed> { let span = tcx.def_span(impl1_def_id); - check_has_items(tcx, impl1_def_id, impl2_node, span); - - if let Ok((impl1_args, impl2_args)) = get_impl_args(tcx, impl1_def_id, impl2_node) { - let impl2_def_id = impl2_node.def_id(); - debug!(?impl2_def_id, ?impl2_args); - - let parent_args = if impl2_node.is_from_trait() { - impl2_args.to_vec() - } else { - unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args) - }; - - check_constness(tcx, impl1_def_id, impl2_node, span); - check_static_lifetimes(tcx, &parent_args, span); - check_duplicate_params(tcx, impl1_args, &parent_args, span); - check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span); - } + let mut res = check_has_items(tcx, impl1_def_id, impl2_node, span); + + let (impl1_args, impl2_args) = get_impl_args(tcx, impl1_def_id, impl2_node)?; + let impl2_def_id = impl2_node.def_id(); + debug!(?impl2_def_id, ?impl2_args); + + let parent_args = if impl2_node.is_from_trait() { + impl2_args.to_vec() + } else { + unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args) + }; + + res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span)); + res = res.and(check_static_lifetimes(tcx, &parent_args, span)); + res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span)); + res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); + + res } -fn check_has_items(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) { +fn check_has_items( + tcx: TyCtxt<'_>, + impl1_def_id: LocalDefId, + impl2_node: Node, + span: Span, +) -> Result<(), ErrorGuaranteed> { if let Node::Impl(impl2_id) = impl2_node && tcx.associated_item_def_ids(impl1_def_id).is_empty() { let base_impl_span = tcx.def_span(impl2_id); - tcx.dcx().emit_err(errors::EmptySpecialization { span, base_impl_span }); + return Err(tcx.dcx().emit_err(errors::EmptySpecialization { span, base_impl_span })); } + Ok(()) } /// Check that the specializing impl `impl1` is at least as const as the base /// impl `impl2` -fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) { +fn check_constness( + tcx: TyCtxt<'_>, + impl1_def_id: LocalDefId, + impl2_node: Node, + span: Span, +) -> Result<(), ErrorGuaranteed> { if impl2_node.is_from_trait() { // This isn't a specialization - return; + return Ok(()); } let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); @@ -152,9 +172,10 @@ fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, if let hir::Constness::Const = impl2_constness { if let hir::Constness::NotConst = impl1_constness { - tcx.dcx().emit_err(errors::ConstSpecialize { span }); + return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span })); } } + Ok(()) } /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two @@ -202,7 +223,7 @@ fn get_impl_args( return Err(guar); } - let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types); + let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, &assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { @@ -290,15 +311,17 @@ fn check_duplicate_params<'tcx>( impl1_args: GenericArgsRef<'tcx>, parent_args: &Vec<GenericArg<'tcx>>, span: Span, -) { +) -> Result<(), ErrorGuaranteed> { let mut base_params = cgp::parameters_for(parent_args, true); base_params.sort_by_key(|param| param.0); if let (_, [duplicate, ..]) = base_params.partition_dedup() { let param = impl1_args[duplicate.0 as usize]; - tcx.dcx() + return Err(tcx + .dcx() .struct_span_err(span, format!("specializing impl repeats parameter `{param}`")) - .emit(); + .emit()); } + Ok(()) } /// Check that `'static` lifetimes are not introduced by the specializing impl. @@ -313,10 +336,11 @@ fn check_static_lifetimes<'tcx>( tcx: TyCtxt<'tcx>, parent_args: &Vec<GenericArg<'tcx>>, span: Span, -) { +) -> Result<(), ErrorGuaranteed> { if tcx.any_free_region_meets(parent_args, |r| r.is_static()) { - tcx.dcx().emit_err(errors::StaticSpecialize { span }); + return Err(tcx.dcx().emit_err(errors::StaticSpecialize { span })); } + Ok(()) } /// Check whether predicates on the specializing impl (`impl1`) are allowed. @@ -337,7 +361,7 @@ fn check_predicates<'tcx>( impl2_node: Node, impl2_args: GenericArgsRef<'tcx>, span: Span, -) { +) -> Result<(), ErrorGuaranteed> { let impl1_predicates: Vec<_> = traits::elaborate( tcx, tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_args).into_iter(), @@ -399,14 +423,16 @@ fn check_predicates<'tcx>( } impl2_predicates.extend(traits::elaborate(tcx, always_applicable_traits)); + let mut res = Ok(()); for (clause, span) in impl1_predicates { if !impl2_predicates .iter() .any(|pred2| trait_predicates_eq(tcx, clause.as_predicate(), *pred2, span)) { - check_specialization_on(tcx, clause, span) + res = res.and(check_specialization_on(tcx, clause, span)) } } + res } /// Checks if some predicate on the specializing impl (`predicate1`) is the same @@ -443,19 +469,26 @@ fn trait_predicates_eq<'tcx>( } #[instrument(level = "debug", skip(tcx))] -fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, clause: ty::Clause<'tcx>, span: Span) { +fn check_specialization_on<'tcx>( + tcx: TyCtxt<'tcx>, + clause: ty::Clause<'tcx>, + span: Span, +) -> Result<(), ErrorGuaranteed> { match clause.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. - _ if clause.is_global() => (), + _ if clause.is_global() => Ok(()), // We allow specializing on explicitly marked traits with no associated // items. ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => { - if !matches!( + if matches!( trait_specialization_kind(tcx, clause), Some(TraitSpecializationKind::Marker) ) { - tcx.dcx() + Ok(()) + } else { + Err(tcx + .dcx() .struct_span_err( span, format!( @@ -463,17 +496,16 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, clause: ty::Clause<'tcx>, sp tcx.def_path_str(trait_ref.def_id), ), ) - .emit(); + .emit()) } } - ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { - tcx.dcx() - .struct_span_err( - span, - format!("cannot specialize on associated type `{projection_ty} == {term}`",), - ) - .emit(); - } + ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => Err(tcx + .dcx() + .struct_span_err( + span, + format!("cannot specialize on associated type `{projection_ty} == {term}`",), + ) + .emit()), ty::ClauseKind::ConstArgHasType(..) => { // FIXME(min_specialization), FIXME(const_generics): // It probably isn't right to allow _every_ `ConstArgHasType` but I am somewhat unsure @@ -483,12 +515,12 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, clause: ty::Clause<'tcx>, sp // While we do not support constructs like `<T, const N: T>` there is probably no risk of // soundness bugs, but when we support generic const parameter types this will need to be // revisited. + Ok(()) } - _ => { - tcx.dcx() - .struct_span_err(span, format!("cannot specialize on predicate `{clause}`")) - .emit(); - } + _ => Err(tcx + .dcx() + .struct_span_err(span, format!("cannot specialize on predicate `{clause}`")) + .emit()), } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index f5abb7261c0..0a3a71ba1a2 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -166,33 +166,29 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module)) }); - // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function - // or the compiler in general. if tcx.features().rustc_attrs { - tcx.sess.track_errors(|| { - tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); - })?; + tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx))?; } - tcx.sess.track_errors(|| { - tcx.sess.time("coherence_checking", || { - // Check impls constrain their parameters - tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)); + tcx.sess.time("coherence_checking", || { + // Check impls constrain their parameters + let res = + tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)); + // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function + // or the compiler in general. + res.and(tcx.sess.track_errors(|| { for &trait_def_id in tcx.all_local_trait_impls(()).keys() { tcx.ensure().coherent_trait(trait_def_id); } - - // these queries are executed for side-effects (error reporting): - tcx.ensure().crate_inherent_impls(()); - tcx.ensure().crate_inherent_impls_overlap_check(()); - }); + })) + // these queries are executed for side-effects (error reporting): + .and(tcx.ensure().crate_inherent_impls(())) + .and(tcx.ensure().crate_inherent_impls_overlap_check(())) })?; if tcx.features().rustc_attrs { - tcx.sess.track_errors(|| { - tcx.sess.time("variance_testing", || variance::test::test_variance(tcx)); - })?; + tcx.sess.time("variance_testing", || variance::test::test_variance(tcx))?; } tcx.sess.time("wf_checking", || { @@ -200,7 +196,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { })?; if tcx.features().rustc_attrs { - tcx.sess.track_errors(|| collect::test_opaque_hidden_types(tcx))?; + collect::test_opaque_hidden_types(tcx)?; } // Freeze definitions as we don't add new ones at this point. This improves performance by @@ -225,7 +221,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { /// A quasi-deprecated helper used in rustdoc and clippy to get /// the type from a HIR node. -pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { +pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. @@ -236,7 +232,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { pub fn hir_trait_to_predicates<'tcx>( tcx: TyCtxt<'tcx>, - hir_trait: &hir::TraitRef<'_>, + hir_trait: &hir::TraitRef<'tcx>, self_ty: Ty<'tcx>, ) -> Bounds<'tcx> { // In case there are any projections, etc., find the "environment" diff --git a/compiler/rustc_hir_analysis/src/outlives/test.rs b/compiler/rustc_hir_analysis/src/outlives/test.rs index dea3f1a9930..60cd8c39fa0 100644 --- a/compiler/rustc_hir_analysis/src/outlives/test.rs +++ b/compiler/rustc_hir_analysis/src/outlives/test.rs @@ -1,7 +1,8 @@ use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, ErrorGuaranteed}; -pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { +pub fn test_inferred_outlives(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { + let mut res = Ok(()); for id in tcx.hir().items() { // For unit testing: check for a special "rustc_outlives" // attribute and report an error with various results if found. @@ -22,7 +23,8 @@ pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { for p in pred { err.note(p); } - err.emit(); + res = Err(err.emit()); } } + res } diff --git a/compiler/rustc_hir_analysis/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs index 04d04304e70..b061d637138 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors.rs @@ -6,13 +6,13 @@ pub use self::{ missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*, }; -use rustc_errors::{DiagnosticBuilder, DiagnosticId}; +use rustc_errors::DiagnosticBuilder; use rustc_session::Session; pub trait StructuredDiagnostic<'tcx> { fn session(&self) -> &Session; - fn code(&self) -> DiagnosticId; + fn code(&self) -> String; fn diagnostic(&self) -> DiagnosticBuilder<'tcx> { let err = self.diagnostic_common(); 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 index 634904e3271..6f4435db411 100644 --- 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 @@ -1,5 +1,5 @@ use crate::{errors, structured_errors::StructuredDiagnostic}; -use rustc_errors::{DiagnosticBuilder, DiagnosticId}; +use rustc_errors::DiagnosticBuilder; use rustc_middle::ty::{Ty, TypeVisitableExt}; use rustc_session::Session; use rustc_span::Span; @@ -16,7 +16,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx, '_> { self.sess } - fn code(&self) -> DiagnosticId { + fn code(&self) -> String { rustc_errors::error_code!(E0617) } 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 index c68d74969a5..19cac4a38aa 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs @@ -1,5 +1,5 @@ use crate::{errors, structured_errors::StructuredDiagnostic}; -use rustc_errors::{DiagnosticBuilder, DiagnosticId}; +use rustc_errors::DiagnosticBuilder; use rustc_middle::ty::{Ty, TypeVisitableExt}; use rustc_session::Session; use rustc_span::Span; @@ -16,7 +16,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> { self.sess } - fn code(&self) -> DiagnosticId { + fn code(&self) -> String { rustc_errors::error_code!(E0607) } diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 6657e3fd872..ba81e7f1f81 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -1,7 +1,5 @@ use crate::structured_errors::StructuredDiagnostic; -use rustc_errors::{ - pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, MultiSpan, -}; +use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; use rustc_session::Session; @@ -1107,7 +1105,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { self.tcx.sess } - fn code(&self) -> DiagnosticId { + fn code(&self) -> String { rustc_errors::error_code!(E0107) } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 410706110c9..7d145ea1f23 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -3,6 +3,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html +use itertools::Itertools; use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -91,7 +92,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc fn visit_opaque(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> ControlFlow<!> { if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) { let child_variances = self.tcx.variances_of(def_id); - for (a, v) in args.iter().zip(child_variances) { + for (a, v) in args.iter().zip_eq(child_variances) { if *v != ty::Bivariant { a.visit_with(self)?; } diff --git a/compiler/rustc_hir_analysis/src/variance/test.rs b/compiler/rustc_hir_analysis/src/variance/test.rs index 5264d5aa26f..c211e1af046 100644 --- a/compiler/rustc_hir_analysis/src/variance/test.rs +++ b/compiler/rustc_hir_analysis/src/variance/test.rs @@ -2,19 +2,21 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; +use rustc_span::ErrorGuaranteed; use crate::errors; -pub fn test_variance(tcx: TyCtxt<'_>) { +pub fn test_variance(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { + let mut res = Ok(()); if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) { for id in tcx.hir().items() { if matches!(tcx.def_kind(id.owner_id), DefKind::OpaqueTy) { let variances_of = tcx.variances_of(id.owner_id); - tcx.dcx().emit_err(errors::VariancesOf { + res = Err(tcx.dcx().emit_err(errors::VariancesOf { span: tcx.def_span(id.owner_id), variances_of: format!("{variances_of:?}"), - }); + })); } } } @@ -25,10 +27,11 @@ pub fn test_variance(tcx: TyCtxt<'_>) { if tcx.has_attr(id.owner_id, sym::rustc_variance) { let variances_of = tcx.variances_of(id.owner_id); - tcx.dcx().emit_err(errors::VariancesOf { + res = Err(tcx.dcx().emit_err(errors::VariancesOf { span: tcx.def_span(id.owner_id), variances_of: format!("{variances_of:?}"), - }); + })); } } + res } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d36e0892d19..e76303bc6df 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1838,6 +1838,11 @@ impl<'a> State<'a> { self.commasep(Inconsistent, after, |s, p| s.print_pat(p)); self.word("]"); } + PatKind::Err(_) => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose(); + } } self.ann.post(self, AnnNode::Pat(pat)) } diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index b0c60304424..975f9375642 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +itertools = "0.11" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 7873257c4e3..2bb7caea3c4 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -2,6 +2,7 @@ use super::method::MethodCallee; use super::{FnCtxt, PlaceOp}; +use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; @@ -32,8 +33,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, autoderef: &Autoderef<'a, 'tcx>, ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> { - let mut obligations = vec![]; let steps = autoderef.steps(); + if steps.is_empty() { + return InferOk { obligations: vec![], value: vec![] }; + } + + let mut obligations = vec![]; let targets = steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); let steps: Vec<_> = steps @@ -54,7 +59,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } }) - .zip(targets) + .zip_eq(targets) .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) .collect(); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 1a4e03d50ca..d486f069989 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -293,49 +293,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_node: &hir::ExprKind<'_>, callee_span: Span, ) { + let hir::ExprKind::Block(..) = callee_node else { + // Only calls on blocks suggested here. + return; + }; + let hir = self.tcx.hir(); - let parent_hir_id = hir.parent_id(hir_id); - let parent_node = self.tcx.hir_node(parent_hir_id); - if let ( - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, kind, .. }), + let fn_decl_span = if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), + .. + }) = hir.get_parent(hir_id) + { + fn_decl_span + } else if let Some(( + _, + hir::Node::Expr(&hir::Expr { + hir_id: parent_hir_id, + kind: + hir::ExprKind::Closure(&hir::Closure { + kind: + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Closure, + )), + .. + }), .. }), - hir::ExprKind::Block(..), - ) = (parent_node, callee_node) + )) = hir.parent_iter(hir_id).nth(3) { - let fn_decl_span = if matches!( - kind, - hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( - hir::CoroutineDesugaring::Async, - hir::CoroutineSource::Closure - ),) - ) { - // Actually need to unwrap one more layer of HIR to get to - // the _real_ closure... - let async_closure = hir.parent_id(parent_hir_id); - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), - .. - }) = self.tcx.hir_node(async_closure) - { - fn_decl_span - } else { - return; - } - } else { + // Actually need to unwrap one more layer of HIR to get to + // the _real_ closure... + let async_closure = hir.parent_id(parent_hir_id); + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), + .. + }) = self.tcx.hir_node(async_closure) + { fn_decl_span - }; + } else { + return; + } + } else { + return; + }; - let start = fn_decl_span.shrink_to_lo(); - let end = callee_span.shrink_to_hi(); - err.multipart_suggestion( - "if you meant to create this closure and immediately call it, surround the \ + let start = fn_decl_span.shrink_to_lo(); + let end = callee_span.shrink_to_hi(); + err.multipart_suggestion( + "if you meant to create this closure and immediately call it, surround the \ closure with parentheses", - vec![(start, "(".to_string()), (end, ")".to_string())], - Applicability::MaybeIncorrect, - ); - } + vec![(start, "(".to_string()), (end, ")".to_string())], + Applicability::MaybeIncorrect, + ); } /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b6b33299315..f51cc97b45d 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::ArgKind; @@ -49,7 +49,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: Span, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - trace!("decl = {:#?}", closure.fn_decl); + let tcx = self.tcx; + let body = tcx.hir().body(closure.body); + let expr_def_id = closure.def_id; // It's always helpful for inference if we know the kind of // closure sooner rather than later, so first examine the expected @@ -61,35 +63,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => (None, None), }; - self.check_closure(closure, expr_span, expected_kind, expected_sig) - } - - #[instrument(skip(self, closure), level = "debug", ret)] - fn check_closure( - &self, - closure: &hir::Closure<'tcx>, - expr_span: Span, - opt_kind: Option<ty::ClosureKind>, - expected_sig: Option<ExpectedSig<'tcx>>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let body = tcx.hir().body(closure.body); - - trace!("decl = {:#?}", closure.fn_decl); - let expr_def_id = closure.def_id; - debug!(?expr_def_id); - let ClosureSignatures { bound_sig, liberated_sig } = self.sig_of_closure(expr_def_id, closure.fn_decl, closure.kind, expected_sig); debug!(?bound_sig, ?liberated_sig); + let parent_args = + GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id())); + + let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr_span, + }); + // FIXME: We could probably actually just unify this further -- // instead of having a `FnSig` and a `Option<CoroutineTypes>`, // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`, // similar to how `ty::GenSig` is a distinct data structure. - let coroutine_types = match closure.kind { - hir::ClosureKind::Closure => None, + let (closure_ty, coroutine_types) = match closure.kind { + hir::ClosureKind::Closure => { + // Tuple up the arguments and insert the resulting function type into + // the `closures` table. + let sig = bound_sig.map_bound(|sig| { + tcx.mk_fn_sig( + [Ty::new_tup(tcx, sig.inputs())], + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }); + + debug!(?sig, ?expected_kind); + + let closure_kind_ty = match expected_kind { + Some(kind) => Ty::from_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => self.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish closure kind inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr_span, + }), + }; + + let closure_args = ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args, + closure_kind_ty, + closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig), + tupled_upvars_ty, + }, + ); + + (Ty::new_closure(tcx, expr_def_id.to_def_id(), closure_args.args), None) + } hir::ClosureKind::Coroutine(kind) => { let yield_ty = match kind { hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) @@ -135,80 +165,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resume type defaults to `()` if the coroutine has no argument. let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); - Some(CoroutineTypes { resume_ty, yield_ty }) - } - }; - - let mut fcx = FnCtxt::new(self, self.param_env, closure.def_id); - check_fn( - &mut fcx, - liberated_sig, - coroutine_types, - closure.fn_decl, - expr_def_id, - body, - // Closure "rust-call" ABI doesn't support unsized params - false, - ); - - let parent_args = - GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id())); - - let tupled_upvars_ty = self.next_root_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); - - match closure.kind { - hir::ClosureKind::Closure => { - assert_eq!(coroutine_types, None); - // Tuple up the arguments and insert the resulting function type into - // the `closures` table. - let sig = bound_sig.map_bound(|sig| { - tcx.mk_fn_sig( - [Ty::new_tup(tcx, sig.inputs())], - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) - }); - - debug!(?sig, ?opt_kind); - - let closure_kind_ty = match opt_kind { - Some(kind) => Ty::from_closure_kind(tcx, kind), - - // Create a type variable (for now) to represent the closure kind. - // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.next_root_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }), - }; - - let closure_args = ty::ClosureArgs::new( - tcx, - ty::ClosureArgsParts { - parent_args, - closure_kind_ty, - closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig), - tupled_upvars_ty, - }, - ); - - Ty::new_closure(tcx, expr_def_id.to_def_id(), closure_args.args) - } - hir::ClosureKind::Coroutine(_) => { - let Some(CoroutineTypes { resume_ty, yield_ty }) = coroutine_types else { - bug!("expected coroutine to have yield/resume types"); - }; - let interior = fcx.next_ty_var(TypeVariableOrigin { + let interior = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: body.value.span, }); - fcx.deferred_coroutine_interiors.borrow_mut().push(( + self.deferred_coroutine_interiors.borrow_mut().push(( expr_def_id, body.id(), interior, @@ -226,9 +187,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - Ty::new_coroutine(tcx, expr_def_id.to_def_id(), coroutine_args.args) + ( + Ty::new_coroutine(tcx, expr_def_id.to_def_id(), coroutine_args.args), + Some(CoroutineTypes { resume_ty, yield_ty }), + ) } - } + }; + + check_fn( + &mut FnCtxt::new(self, self.param_env, closure.def_id), + liberated_sig, + coroutine_types, + closure.fn_decl, + expr_def_id, + body, + // Closure "rust-call" ABI doesn't support unsized params + false, + ); + + closure_ty } /// Given the expected type, figures out what it can about this closure we @@ -364,36 +341,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let trait_def_id = projection.trait_def_id(tcx); - - let is_fn = tcx.is_fn_trait(trait_def_id); - - let coroutine_trait = tcx.lang_items().coroutine_trait(); - let is_gen = coroutine_trait == Some(trait_def_id); - - if !is_fn && !is_gen { - debug!("not fn or coroutine"); + // For now, we only do signature deduction based off of the `Fn` traits. + if !tcx.is_fn_trait(trait_def_id) { return None; } - // Check that we deduce the signature from the `<_ as std::ops::Coroutine>::Return` - // associated item and not yield. - if is_gen && self.tcx.associated_item(projection.projection_def_id()).name != sym::Return { - debug!("not `Return` assoc item of `Coroutine`"); - return None; - } - - let input_tys = if is_fn { - let arg_param_ty = projection.skip_binder().projection_ty.args.type_at(1); - let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); - debug!(?arg_param_ty); + let arg_param_ty = projection.skip_binder().projection_ty.args.type_at(1); + let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); + debug!(?arg_param_ty); - match arg_param_ty.kind() { - &ty::Tuple(tys) => tys, - _ => return None, - } - } else { - // Coroutines with a `()` resume type may be defined with 0 or 1 explicit arguments, - // else they must have exactly 1 argument. For now though, just give up in this case. + let ty::Tuple(input_tys) = *arg_param_ty.kind() else { return None; }; @@ -416,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, expected_sig: Option<ExpectedSig<'tcx>>, ) -> ClosureSignatures<'tcx> { @@ -433,7 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure_no_expectation( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, ) -> ClosureSignatures<'tcx> { let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, closure_kind); @@ -492,7 +449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure_with_expectation( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { @@ -549,7 +506,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure_with_mismatched_number_of_arguments( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, expected_sig: ExpectedSig<'tcx>, ) -> ClosureSignatures<'tcx> { let expr_map_node = self.tcx.hir_node_by_def_id(expr_def_id); @@ -590,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn merge_supplied_sig_with_expectation( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, mut expected_sigs: ClosureSignatures<'tcx>, ) -> InferResult<'tcx, ClosureSignatures<'tcx>> { @@ -684,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn supplied_sig_of_closure( &self, expr_def_id: LocalDefId, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, ) -> ty::PolyFnSig<'tcx> { let astconv: &dyn AstConv<'_> = self; @@ -720,10 +677,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } // All `gen {}` and `async gen {}` must return unit. - hir::ClosureKind::Coroutine( - hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) - | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _), - ) => self.tcx.types.unit, + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen, + _, + )) => self.tcx.types.unit, // For async blocks, we just fall back to `_` here. // For closures/coroutines, we know nothing about the return @@ -797,16 +754,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { get_future_output(obligation.predicate, obligation.cause.span) })? } + ty::Alias(ty::Projection, _) => { + return Some(Ty::new_error_with_message( + self.tcx, + closure_span, + "this projection should have been projected to an opaque type", + )); + } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx .explicit_item_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), - _ => span_bug!( - closure_span, - "async fn coroutine return type not an inference variable: {ret_ty}" - ), + _ => { + span_bug!(closure_span, "invalid async fn coroutine return type: {ret_ty:?}") + } }; let output_ty = self.normalize(closure_span, output_ty); @@ -880,7 +843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// all parameters are of type `ty::Error`. fn error_sig_of_closure( &self, - decl: &hir::FnDecl<'_>, + decl: &hir::FnDecl<'tcx>, guar: ErrorGuaranteed, ) -> ty::PolyFnSig<'tcx> { let astconv: &dyn AstConv<'_> = self; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index af47455c16d..aca17ec77a4 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -26,7 +26,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, - DiagnosticId, ErrorGuaranteed, StashKey, + ErrorGuaranteed, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -941,7 +941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut err = self.dcx().struct_span_err(op_span, "invalid left-hand side of assignment"); - err.code(DiagnosticId::Error(err_code.into())); + err.code(err_code.into()); err.span_label(lhs.span, "cannot assign to this expression"); self.comes_from_while_condition(lhs.hir_id, |expr| { @@ -1315,7 +1315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_method_call( &self, expr: &'tcx hir::Expr<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &'tcx hir::PathSegment<'tcx>, rcvr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, @@ -1526,13 +1526,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_repeat_element_needs_copy_bound(element, count, element_ty); - self.register_wf_obligation( - Ty::new_array_with_const_len(tcx, t, count).into(), - expr.span, - traits::WellFormed(None), - ); + let ty = Ty::new_array_with_const_len(tcx, t, count); + + self.register_wf_obligation(ty.into(), expr.span, traits::WellFormed(None)); - Ty::new_array_with_const_len(tcx, t, count) + ty } fn check_repeat_element_needs_copy_bound( @@ -1629,7 +1627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &hir::Expr<'_>, expected: Expectation<'tcx>, - qpath: &QPath<'_>, + qpath: &QPath<'tcx>, fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { @@ -2104,7 +2102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut items = self .tcx .inherent_impls(def_id) - .iter() + .into_iter() + .flatten() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. .filter(|item| { @@ -3247,6 +3246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let container = self.to_ty(container).normalized; + if let Some(ident_2) = fields.get(1) + && !self.tcx.features().offset_of_nested + { + rustc_session::parse::feature_err( + &self.tcx.sess, + sym::offset_of_nested, + ident_2.span, + "only a single ident or integer is stable as the field in offset_of", + ) + .emit(); + } + let mut field_indices = Vec::with_capacity(fields.len()); let mut current_container = container; let mut fields = fields.into_iter(); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ed3dd1e39df..3ecf6c5e428 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -458,11 +458,15 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { needs_to_be_read = true; } } - PatKind::Or(_) | PatKind::Box(_) | PatKind::Ref(..) | PatKind::Wild => { + PatKind::Or(_) + | PatKind::Box(_) + | PatKind::Ref(..) + | PatKind::Wild + | PatKind::Err(_) => { // If the PatKind is Or, Box, or Ref, the decision is made later // as these patterns contains subpatterns - // If the PatKind is Wild, the decision is made based on the other patterns being - // examined + // If the PatKind is Wild or Err, the decision is made based on the other patterns + // being examined } } })? diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a4cd9ccc984..3ea409827b4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -377,13 +377,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { RawTy { raw: ty, normalized: self.normalize(span, ty) } } - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> { + pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> RawTy<'tcx> { let t = self.astconv().ast_ty_to_ty(ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); self.handle_raw_ty(ast_t.span, t) } - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { let ty = self.to_ty(ast_ty); debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); @@ -1073,7 +1073,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, span), level = "debug")] pub fn instantiate_value_path( &self, - segments: &[hir::PathSegment<'_>], + segments: &'tcx [hir::PathSegment<'tcx>], self_ty: Option<RawTy<'tcx>>, res: Res, span: Span, @@ -1260,13 +1260,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, path_segs: &'a [PathSeg], infer_args_for_err: &'a FxHashSet<usize>, - segments: &'a [hir::PathSegment<'a>], + segments: &'tcx [hir::PathSegment<'tcx>], } impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, - ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { + ) -> (Option<&'a hir::GenericArgs<'tcx>>, bool) { if let Some(&PathSeg(_, index)) = self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) { @@ -1287,7 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn provided_kind( &mut self, param: &ty::GenericParamDef, - arg: &GenericArg<'_>, + arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 76360239c45..1ad79cb78c4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Finally, for ambiguity-related errors, we actually want to look // for a parameter that is the source of the inference type left // over in this predicate. - if let traits::FulfillmentErrorCode::CodeAmbiguity { .. } = error.code { + if let traits::FulfillmentErrorCode::Ambiguity { .. } = error.code { fallback_param_to_point_at = None; self_param_to_point_at = None; param_to_point_at = @@ -361,10 +361,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: &traits::FulfillmentError<'tcx>, span: Span, ) -> bool { - if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::OutputTypeParameterMismatch( - box traits::SelectionOutputTypeParameterMismatch { expected_trait_ref, .. }, - ), + if let traits::FulfillmentErrorCode::SelectionError( + traits::SelectionError::SignatureMismatch(box traits::SignatureMismatchData { + expected_trait_ref, + .. + }), ) = error.code && let ty::Closure(def_id, _) | ty::Coroutine(def_id, ..) = expected_trait_ref.skip_binder().self_ty().kind() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8cd5ed3494b..ddb4224b60d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -9,11 +9,10 @@ use crate::{ struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag, }; +use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{ - pluralize, Applicability, Diagnostic, DiagnosticId, ErrorGuaranteed, MultiSpan, StashKey, -}; +use rustc_errors::{pluralize, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -421,7 +420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys .iter() .copied() - .zip(expected_input_tys.iter().copied()) + .zip_eq(expected_input_tys.iter().copied()) .map(|vars| self.resolve_vars_if_possible(vars)), ); @@ -678,7 +677,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pluralize!("was", provided_args.len()) ), ); - err.code(DiagnosticId::Error(err_code.to_owned())); + err.code(err_code.to_owned()); err.multipart_suggestion_verbose( "wrap these arguments in parentheses to construct a tuple", vec![ @@ -828,7 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pluralize!("was", provided_args.len()) ), ) - .with_code(DiagnosticId::Error(err_code.to_owned())) + .with_code(err_code.to_owned()) }; // As we encounter issues, keep track of what we want to provide for the suggestion @@ -1328,7 +1327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_struct_path( &self, - qpath: &QPath<'_>, + qpath: &QPath<'tcx>, hir_id: hir::HirId, ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { let path_span = qpath.span(); @@ -1784,7 +1783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The newly resolved definition is written into `type_dependent_defs`. fn finish_resolving_struct_path( &self, - qpath: &QPath<'_>, + qpath: &QPath<'tcx>, path_span: Span, hir_id: hir::HirId, ) -> (Res, RawTy<'tcx>) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index fde3d41faec..e6c2091d85a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -190,10 +190,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn errors_reported_since_creation(&self) -> bool { self.dcx().err_count() > self.err_count_on_creation } - - pub fn next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { - Ty::new_var(self.tcx, self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT)) - } } impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { @@ -287,7 +283,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { &self, span: Span, item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, + item_segment: &hir::PathSegment<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { let trait_ref = self.instantiate_binder_with_fresh_vars( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b542132d71c..2778ada655e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -784,7 +784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_missing_return_type( &self, err: &mut Diagnostic, - fn_decl: &hir::FnDecl<'_>, + fn_decl: &hir::FnDecl<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, can_suggest: bool, @@ -995,7 +995,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, expr: &'tcx hir::Expr<'tcx>, - fn_decl: &hir::FnDecl<'_>, + fn_decl: &hir::FnDecl<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, id: hir::HirId, @@ -1468,7 +1468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_associated_const( &self, err: &mut Diagnostic, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expected_ty: Ty<'tcx>, ) -> bool { let Some((DefKind::AssocFn, old_def_id)) = @@ -1623,7 +1623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } else { if let Some(errors) = - self.could_impl_trait(clone_trait_did, expected_ty, self.param_env) + self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env) { match &errors[..] { [] => {} @@ -1648,7 +1648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } for error in errors { - if let traits::FulfillmentErrorCode::CodeSelectionError( + if let traits::FulfillmentErrorCode::SelectionError( traits::SelectionError::Unimplemented, ) = error.code && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 36dd06d944c..80467ca9381 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -52,7 +52,7 @@ use crate::expectation::Expectation; use crate::fn_ctxt::RawTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{struct_span_code_err, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{struct_span_code_err, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -60,6 +60,7 @@ use rustc_hir::{HirIdMap, Node}; use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::check_abi; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::ObligationInspector; use rustc_middle::query::Providers; use rustc_middle::traits; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -139,7 +140,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity(); - typeck_with_fallback(tcx, def_id, fallback) + typeck_with_fallback(tcx, def_id, fallback, None) } /// Used only to get `TypeckResults` for type inference during error recovery. @@ -149,14 +150,28 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T let span = tcx.hir().span(tcx.local_def_id_to_hir_id(def_id)); Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used") }; - typeck_with_fallback(tcx, def_id, fallback) + typeck_with_fallback(tcx, def_id, fallback, None) } -#[instrument(level = "debug", skip(tcx, fallback), ret)] +/// Same as `typeck` but `inspect` is invoked on evaluation of each root obligation. +/// Inspecting obligations only works with the new trait solver. +/// This function is *only to be used* by external tools, it should not be +/// called from within rustc. Note, this is not a query, and thus is not cached. +pub fn inspect_typeck<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + inspect: ObligationInspector<'tcx>, +) -> &'tcx ty::TypeckResults<'tcx> { + let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity(); + typeck_with_fallback(tcx, def_id, fallback, Some(inspect)) +} + +#[instrument(level = "debug", skip(tcx, fallback, inspector), ret)] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, fallback: impl Fn() -> Ty<'tcx> + 'tcx, + inspector: Option<ObligationInspector<'tcx>>, ) -> &'tcx ty::TypeckResults<'tcx> { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". @@ -178,6 +193,9 @@ fn typeck_with_fallback<'tcx>( let param_env = tcx.param_env(def_id); let inh = Inherited::new(tcx, def_id); + if let Some(inspector) = inspector { + inh.infcx.attach_obligation_inspector(inspector); + } let mut fcx = FnCtxt::new(&inh, param_env, def_id); if let Some(hir::FnSig { header, decl, .. }) = fn_sig { @@ -369,7 +387,7 @@ fn report_unexpected_variant_res( let err = tcx .dcx() .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) - .with_code(DiagnosticId::Error(err_code.into())); + .with_code(err_code.into()); match res { Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => { let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html"; diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index ce3a4b4c80c..1ce0240f7b8 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -767,7 +767,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | PatKind::Lit(..) | PatKind::Range(..) | PatKind::Never - | PatKind::Wild => { + | PatKind::Wild + | PatKind::Err(_) => { // always ok } } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index b2ead3cd40b..6f18cc51b11 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -50,7 +50,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, unadjusted_self_ty: Ty<'tcx>, pick: &probe::Pick<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &'tcx hir::PathSegment<'tcx>, ) -> ConfirmResult<'tcx> { debug!( "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})", @@ -68,7 +68,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, unadjusted_self_ty: Ty<'tcx>, pick: &probe::Pick<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &hir::PathSegment<'tcx>, ) -> ConfirmResult<'tcx> { let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr); confirm_cx.skip_record_for_diagnostics = true; @@ -90,7 +90,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { &mut self, unadjusted_self_ty: Ty<'tcx>, pick: &probe::Pick<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &hir::PathSegment<'tcx>, ) -> ConfirmResult<'tcx> { // Adjust the self expression the user provided and obtain the adjusted type. let self_ty = self.adjust_self_ty(unadjusted_self_ty, pick); @@ -346,7 +346,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn instantiate_method_args( &mut self, pick: &probe::Pick<'tcx>, - seg: &hir::PathSegment<'_>, + seg: &hir::PathSegment<'tcx>, parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { // Determine the values for the generic parameters of the method. @@ -370,13 +370,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { struct MethodSubstsCtxt<'a, 'tcx> { cfcx: &'a ConfirmContext<'a, 'tcx>, pick: &'a probe::Pick<'tcx>, - seg: &'a hir::PathSegment<'a>, + seg: &'a hir::PathSegment<'tcx>, } impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, - ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { + ) -> (Option<&'a hir::GenericArgs<'tcx>>, bool) { if def_id == self.pick.item.def_id { if let Some(data) = self.seg.args { return (Some(data), false); @@ -388,7 +388,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn provided_kind( &mut self, param: &ty::GenericParamDef, - arg: &GenericArg<'_>, + arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index c746fb8af89..af172b59a14 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lookup_method( &self, self_ty: Ty<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &'tcx hir::PathSegment<'tcx>, span: Span, call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, @@ -255,7 +255,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lookup_method_for_diagnostic( &self, self_ty: Ty<'tcx>, - segment: &hir::PathSegment<'_>, + segment: &hir::PathSegment<'tcx>, span: Span, call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 10c31d8c641..4dc802008d0 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -711,14 +711,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else { bug!("unexpected incoherent type: {:?}", self_ty) }; - for &impl_def_id in self.tcx.incoherent_impls(simp) { + for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter().flatten() { self.assemble_inherent_impl_probe(impl_def_id); } } fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { - let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id); - for &impl_def_id in impl_def_ids.iter() { + let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id).into_iter().flatten(); + for &impl_def_id in impl_def_ids { self.assemble_inherent_impl_probe(impl_def_id); } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 1f01c6b7406..0b8a25eedaf 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -359,7 +359,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(adt_def, _) = ty.kind() { self.tcx .inherent_impls(adt_def.did()) - .iter() + .into_iter() + .flatten() .any(|def_id| self.associated_value(*def_id, item_name).is_some()) } else { false @@ -825,11 +826,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "auto trait is invoked with no method error, but no error reported?", ); } - Some(Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..), - .. - })) => { + Some( + Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..), + .. + }) + // We may also encounter unsatisfied GAT or method bounds + | Node::TraitItem(hir::TraitItem { ident, .. }) + | Node::ImplItem(hir::ImplItem { ident, .. }), + ) => { skip_list.insert(p); let entry = spanned_predicates.entry(ident.span); let entry = entry.or_insert_with(|| { @@ -840,7 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); entry.2.push(p); } - Some(node) => unreachable!("encountered `{node:?}`"), + Some(node) => unreachable!("encountered `{node:?}` due to `{cause:#?}`"), None => (), } } @@ -1043,7 +1049,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut inherent_impls_candidate = self .tcx .inherent_impls(adt.did()) - .iter() + .into_iter() + .flatten() .copied() .filter(|def_id| { if let Some(assoc) = self.associated_value(*def_id, item_name) { @@ -1098,7 +1105,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the {item_kind} was found for\n{type_candidates}{additional_types}" )); } else { - 'outer: for inherent_impl_did in self.tcx.inherent_impls(adt.did()) { + 'outer: for inherent_impl_did in + self.tcx.inherent_impls(adt.did()).into_iter().flatten() + { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { @@ -1452,9 +1461,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; - let mut items = self - .tcx - .inherent_impls(adt_def.did()) + // FIXME(oli-obk): try out bubbling this error up one level and cancelling the other error in that case. + let Ok(impls) = self.tcx.inherent_impls(adt_def.did()) else { return }; + let mut items = impls .iter() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. @@ -1818,7 +1827,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { simplify_type(tcx, ty, TreatParams::AsCandidateKey) .and_then(|simp| { tcx.incoherent_impls(simp) - .iter() + .into_iter() + .flatten() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 95813cb68a6..37106867277 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -177,9 +177,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin }; let ty = match pat.kind { - PatKind::Wild => expected, - // FIXME(never_patterns): check the type is uninhabited. If that is not possible within - // typeck, do that in a later phase. + PatKind::Wild | PatKind::Err(_) => expected, + // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), @@ -325,6 +324,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Ref(..) => AdjustMode::Reset, // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild + // A malformed pattern doesn't have an expected type, so let's just accept any type. + | PatKind::Err(_) // Bindings also work with whatever the expected type is, // and moreover if we peel references off, that will give us the wrong binding type. // Also, we can have a subpattern `binding @ pat`. @@ -754,7 +755,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Box(..) | PatKind::Ref(..) | PatKind::Lit(..) - | PatKind::Range(..) => break 'block None, + | PatKind::Range(..) + | PatKind::Err(_) => break 'block None, }, // Don't provide suggestions in other cases @@ -856,7 +858,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_struct( &self, pat: &'tcx Pat<'tcx>, - qpath: &hir::QPath<'_>, + qpath: &hir::QPath<'tcx>, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, expected: Ty<'tcx>, diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 7b8d91702b3..6f909a0cc9d 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -128,9 +128,11 @@ impl<'tcx> IfThisChanged<'tcx> { if attr.has_name(sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { - None => { - DepNode::from_def_path_hash(self.tcx, def_path_hash, dep_kinds::hir_owner) - } + None => DepNode::from_def_path_hash( + self.tcx, + def_path_hash, + dep_kinds::hir_owner_nodes, + ), Some(n) => { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index f6acc60190a..842cc9fae9b 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -57,8 +57,7 @@ const BASE_FN: &[&str] = &[ /// DepNodes for Hir, which is pretty much everything const BASE_HIR: &[&str] = &[ - // hir_owner and hir_owner_nodes should be computed for all nodes - label_strs::hir_owner, + // hir_owner_nodes should be computed for all nodes label_strs::hir_owner_nodes, ]; diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs index ac374a41eb6..72567b43a5f 100644 --- a/compiler/rustc_index_macros/src/lib.rs +++ b/compiler/rustc_index_macros/src/lib.rs @@ -31,11 +31,17 @@ mod newtype; /// - `#[max = 0xFFFF_FFFD]`: specifies the max value, which allows niche /// optimizations. The default max value is 0xFFFF_FF00. /// - `#[gate_rustc_only]`: makes parts of the generated code nightly-only. +/// +/// `SpecOptionPartialEq` is specialized by this macro, so using it requires enabling +/// `#![feature(min_specialization)]` for the crate. #[proc_macro] #[cfg_attr( feature = "nightly", allow_internal_unstable(step_trait, rustc_attrs, trusted_step, spec_option_partial_eq) )] +// FIXME: Remove the above comment about `min_specialization` once bootstrap is bumped, +// and the corresponding one on SpecOptionPartialEq +#[cfg_attr(all(feature = "nightly", not(bootstrap)), allow_internal_unstable(min_specialization))] 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 df1318c835e..0b25628b9e1 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -104,7 +104,7 @@ impl Parse for Newtype { #gate_rustc_only impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name { fn encode(&self, e: &mut E) { - e.emit_u32(self.private); + e.emit_u32(self.as_u32()); } } } @@ -164,7 +164,7 @@ impl Parse for Newtype { #[inline] fn eq(l: &Option<Self>, r: &Option<Self>) -> bool { if #max_val < u32::MAX { - l.map(|i| i.private).unwrap_or(#max_val+1) == r.map(|i| i.private).unwrap_or(#max_val+1) + l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1) } else { match (l, r) { (Some(l), Some(r)) => r == l, @@ -188,7 +188,7 @@ impl Parse for Newtype { #[cfg_attr(#gate_rustc_only_cfg, rustc_layout_scalar_valid_range_end(#max))] #[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)] #vis struct #name { - private: u32, + private_use_as_methods_instead: u32, } #(#consts)* @@ -238,7 +238,7 @@ impl Parse for Newtype { /// Prefer using `from_u32`. #[inline] #vis const unsafe fn from_u32_unchecked(value: u32) -> Self { - Self { private: value } + Self { private_use_as_methods_instead: value } } /// Extracts the value of this index as a `usize`. @@ -250,7 +250,7 @@ impl Parse for Newtype { /// Extracts the value of this index as a `u32`. #[inline] #vis const fn as_u32(self) -> u32 { - self.private + self.private_use_as_methods_instead } /// Extracts the value of this index as a `usize`. @@ -263,6 +263,7 @@ impl Parse for Newtype { impl std::ops::Add<usize> for #name { type Output = Self; + #[inline] fn add(self, other: usize) -> Self { Self::from_usize(self.index() + other) } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index e60e3ffeaa7..0f1af81d9f0 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -90,6 +90,7 @@ impl<'tcx> InferCtxt<'tcx> { universe: self.universe.clone(), intercrate, next_trait_solver: self.next_trait_solver, + obligation_inspector: self.obligation_inspector.clone(), } } } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 8cca4c6231f..1f071a9ff0b 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -483,7 +483,13 @@ impl<'tcx> InferCtxt<'tcx> { let result_subst = CanonicalVarValues { var_values: self.tcx.mk_args_from_iter( query_response.variables.iter().enumerate().map(|(index, info)| { - if info.is_existential() { + if info.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all, we have to deal with them for now. + self.instantiate_canonical_var(cause.span, info, |u| { + universe_map[u.as_usize()] + }) + } else if info.is_existential() { match opt_values[BoundVar::new(index)] { Some(k) => k, None => self.instantiate_canonical_var(cause.span, info, |u| { @@ -491,9 +497,11 @@ impl<'tcx> InferCtxt<'tcx> { }), } } else { - self.instantiate_canonical_var(cause.span, info, |u| { - universe_map[u.as_usize()] - }) + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + opt_values[BoundVar::new(index)].expect( + "expected placeholder to be unified with itself during response", + ) } }), ), 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 4aefadf590d..03c8e08aa01 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 @@ -13,7 +13,9 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::infer::unify_key::{ + ConstVariableOrigin, ConstVariableOriginKind, ConstVariableValue, +}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{self, InferConst}; @@ -41,8 +43,8 @@ pub enum TypeAnnotationNeeded { E0284, } -impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded { - fn into(self) -> rustc_errors::DiagnosticId { +impl Into<String> for TypeAnnotationNeeded { + fn into(self) -> String { match self { Self::E0282 => rustc_errors::error_code!(E0282), Self::E0283 => rustc_errors::error_code!(E0283), @@ -178,17 +180,23 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte } }; printer.ty_infer_name_resolver = Some(Box::new(ty_getter)); - let const_getter = move |ct_vid| { - if infcx.probe_const_var(ct_vid).is_ok() { + let const_getter = move |ct_vid| match infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(ct_vid) + { + ConstVariableValue::Known { value: _ } => { warn!("resolved const var in error message"); - } - if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = - infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind - { - return Some(name); - } else { None } + ConstVariableValue::Unknown { origin, universe: _ } => { + if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = origin.kind { + return Some(name); + } else { + None + } + } }; printer.const_infer_name_resolver = Some(Box::new(const_getter)); printer @@ -303,7 +311,12 @@ impl<'tcx> InferCtxt<'tcx> { GenericArgKind::Const(ct) => { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { let origin = - self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { value } => { + bug!("resolved infer var: {vid:?} {value}") + } + ConstVariableValue::Unknown { origin, universe: _ } => origin, + }; if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = origin.kind { diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c7cab048db1..d256994d8d1 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -146,14 +146,8 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { ty::ConstKind::Infer(ty::InferConst::Var(v)) => { - let opt_ct = self - .infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(v) - .val - .known(); + let opt_ct = + self.infcx.inner.borrow_mut().const_unification_table().probe_value(v).known(); self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty()) } ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => { diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs index 8ca97ae1b8e..99033922bdf 100644 --- a/compiler/rustc_infer/src/infer/fudge.rs +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -1,4 +1,4 @@ -use rustc_middle::infer::unify_key::ConstVidKey; +use rustc_middle::infer::unify_key::{ConstVariableOriginKind, ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; @@ -28,10 +28,17 @@ fn const_vars_since_snapshot<'tcx>( snapshot_var_len: usize, ) -> (Range<ConstVid>, Vec<ConstVariableOrigin>) { let range = vars_since_snapshot(table, snapshot_var_len); + ( range.start.vid..range.end.vid, (range.start.index()..range.end.index()) - .map(|index| table.probe_value(ConstVid::from_u32(index)).origin) + .map(|index| match table.probe_value(ConstVid::from_u32(index)) { + ConstVariableValue::Known { value: _ } => ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: rustc_span::DUMMY_SP, + }, + ConstVariableValue::Unknown { origin, universe: _ } => origin, + }) .collect(), ) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e164041c599..1eab8575fc0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -13,7 +13,9 @@ use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; use self::opaque_types::OpaqueTypeStorage; pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; -use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine, TraitEngineExt}; +use crate::traits::{ + self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, TraitEngineExt, +}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -23,8 +25,8 @@ use rustc_data_structures::unify as ut; use rustc_errors::{DiagCtxt, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::{select, DefiningAnchor}; @@ -334,6 +336,8 @@ pub struct InferCtxt<'tcx> { pub intercrate: bool, next_trait_solver: bool, + + pub obligation_inspector: Cell<Option<ObligationInspector<'tcx>>>, } impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { @@ -708,6 +712,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, next_trait_solver, + obligation_inspector: Cell::new(None), } } } @@ -1086,7 +1091,7 @@ impl<'tcx> InferCtxt<'tcx> { .inner .borrow_mut() .const_unification_table() - .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } }) + .new_key(ConstVariableValue::Unknown { origin, universe }) .vid; ty::Const::new_var(self.tcx, vid, ty) } @@ -1095,10 +1100,7 @@ impl<'tcx> InferCtxt<'tcx> { self.inner .borrow_mut() .const_unification_table() - .new_key(ConstVarValue { - origin, - val: ConstVariableValue::Unknown { universe: self.universe() }, - }) + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) .vid } @@ -1217,10 +1219,7 @@ impl<'tcx> InferCtxt<'tcx> { .inner .borrow_mut() .const_unification_table() - .new_key(ConstVarValue { - origin, - val: ConstVariableValue::Unknown { universe: self.universe() }, - }) + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) .vid; ty::Const::new_var( self.tcx, @@ -1410,9 +1409,9 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn probe_const_var(&self, vid: ty::ConstVid) -> Result<ty::Const<'tcx>, ty::UniverseIndex> { - match self.inner.borrow_mut().const_unification_table().probe_value(vid).val { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { ConstVariableValue::Known { value } => Ok(value), - ConstVariableValue::Unknown { universe } => Err(universe), + ConstVariableValue::Unknown { origin: _, universe } => Err(universe), } } @@ -1709,7 +1708,7 @@ impl<'tcx> InferCtxt<'tcx> { // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. // // Not `inlined_probe_value(v)` because this call site is colder. - match self.inner.borrow_mut().const_unification_table().probe_value(v).val { + match self.inner.borrow_mut().const_unification_table().probe_value(v) { ConstVariableValue::Unknown { .. } => false, ConstVariableValue::Known { .. } => true, } @@ -1724,6 +1723,15 @@ impl<'tcx> InferCtxt<'tcx> { } } } + + /// Attach a callback to be invoked on each root obligation evaluated in the new trait solver. + pub fn attach_obligation_inspector(&self, inspector: ObligationInspector<'tcx>) { + debug_assert!( + self.obligation_inspector.get().is_none(), + "shouldn't override a set obligation inspector" + ); + self.obligation_inspector.set(Some(inspector)); + } } impl<'tcx> TypeErrCtxt<'_, 'tcx> { @@ -1876,7 +1884,6 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> { .borrow_mut() .const_unification_table() .probe_value(vid) - .val .known() .unwrap_or(ct), ty::ConstKind::Infer(InferConst::EffectVar(vid)) => self diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 11b5b437eff..db46b39ce25 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -631,13 +631,6 @@ impl<'tcx> InferCtxt<'tcx> { ct_op: |ct| ct, }); - if let ty::ClauseKind::Projection(projection) = predicate.kind().skip_binder() { - if projection.term.references_error() { - // No point on adding any obligations since there's a type error involved. - obligations.clear(); - return; - } - } // Require that the predicate holds for the concrete type. debug!(?predicate); obligations.push(traits::Obligation::new( diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 4b254fc7df5..9e1dab12b4d 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -30,14 +30,12 @@ use super::sub::Sub; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_middle::infer::canonical::OriginalQueryValues; -use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{RelateResult, TypeRelation}; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{AliasRelationDirection, TyVar}; use rustc_middle::ty::{IntType, UintType}; -use rustc_span::DUMMY_SP; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -328,8 +326,12 @@ impl<'tcx> InferCtxt<'tcx> { ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let span = - self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span; + let span = match self.inner.borrow_mut().const_unification_table().probe_value(target_vid) { + ConstVariableValue::Known { value } => { + bug!("instantiating a known const var: {target_vid:?} {value} {ct}") + } + ConstVariableValue::Unknown { origin, universe: _ } => origin.span, + }; // FIXME(generic_const_exprs): Occurs check failures for unevaluated // constants and generic expressions are not yet handled correctly. let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize( @@ -340,16 +342,10 @@ impl<'tcx> InferCtxt<'tcx> { ty::Variance::Invariant, )?; - self.inner.borrow_mut().const_unification_table().union_value( - target_vid, - ConstVarValue { - origin: ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstInference, - span: DUMMY_SP, - }, - val: ConstVariableValue::Known { value }, - }, - ); + self.inner + .borrow_mut() + .const_unification_table() + .union_value(target_vid, ConstVariableValue::Known { value }); Ok(value) } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 27d37fd9369..417c8695e24 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -3,7 +3,7 @@ use std::mem; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; -use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; +use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::MaxUniverse; @@ -431,22 +431,19 @@ where let mut inner = self.infcx.inner.borrow_mut(); let variable_table = &mut inner.const_unification_table(); - let var_value = variable_table.probe_value(vid); - match var_value.val { + match variable_table.probe_value(vid) { ConstVariableValue::Known { value: u } => { drop(inner); self.relate(u, u) } - ConstVariableValue::Unknown { universe } => { + ConstVariableValue::Unknown { origin, universe } => { if self.for_universe.can_name(universe) { Ok(c) } else { let new_var_id = variable_table - .new_key(ConstVarValue { - origin: var_value.origin, - val: ConstVariableValue::Unknown { - universe: self.for_universe, - }, + .new_key(ConstVariableValue::Unknown { + origin, + universe: self.for_universe, }) .vid; Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty())) diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index f317ccee691..959b0903127 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,12 +1,8 @@ -use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use super::{FixupError, FixupResult, InferCtxt, Span}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use super::{FixupError, FixupResult, InferCtxt}; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; -use std::ops::ControlFlow; - /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC VAR RESOLVER @@ -105,88 +101,6 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticRegionResolver<'a, 'tcx } /////////////////////////////////////////////////////////////////////////// -// UNRESOLVED TYPE FINDER - -/// The unresolved type **finder** walks a type searching for -/// type variables that don't yet have a value. The first unresolved type is stored. -/// It does not construct the fully resolved type (which might -/// involve some hashing and so forth). -pub struct UnresolvedTypeOrConstFinder<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, -} - -impl<'a, 'tcx> UnresolvedTypeOrConstFinder<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - UnresolvedTypeOrConstFinder { infcx } - } -} - -impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for UnresolvedTypeOrConstFinder<'a, 'tcx> { - type BreakTy = (ty::Term<'tcx>, Option<Span>); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - let t = self.infcx.shallow_resolve(t); - if let ty::Infer(infer_ty) = *t.kind() { - // Since we called `shallow_resolve` above, this must - // be an (as yet...) unresolved inference variable. - let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { - let mut inner = self.infcx.inner.borrow_mut(); - let ty_vars = &inner.type_variables(); - if let TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition(_, _), - span, - } = ty_vars.var_origin(ty_vid) - { - Some(span) - } else { - None - } - } else { - None - }; - ControlFlow::Break((t.into(), ty_var_span)) - } else if !t.has_non_region_infer() { - // All const/type variables in inference types must already be resolved, - // no need to visit the contents. - ControlFlow::Continue(()) - } else { - // Otherwise, keep visiting. - t.super_visit_with(self) - } - } - - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - let ct = self.infcx.shallow_resolve(ct); - if let ty::ConstKind::Infer(i) = ct.kind() { - // Since we called `shallow_resolve` above, this must - // be an (as yet...) unresolved inference variable. - let ct_var_span = if let ty::InferConst::Var(vid) = i { - let mut inner = self.infcx.inner.borrow_mut(); - let ct_vars = &mut inner.const_unification_table(); - if let ConstVariableOrigin { - span, - kind: ConstVariableOriginKind::ConstParameterDefinition(_, _), - } = ct_vars.probe_value(vid).origin - { - Some(span) - } else { - None - } - } else { - None - }; - ControlFlow::Break((ct.into(), ct_var_span)) - } else if !ct.has_non_region_infer() { - // All const/type variables in inference types must already be resolved, - // no need to visit the contents. - ControlFlow::Continue(()) - } else { - // Otherwise, keep visiting. - ct.super_visit_with(self) - } - } -} - -/////////////////////////////////////////////////////////////////////////// // FULL TYPE RESOLUTION /// Full type resolution replaces all type and region variables with diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index b9be178916c..72ec07375ac 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -13,13 +13,15 @@ use std::hash::{Hash, Hasher}; use hir::def_id::LocalDefId; use rustc_hir as hir; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Const, ToPredicate, Ty, TyCtxt}; use rustc_span::Span; -pub use self::FulfillmentErrorCode::*; pub use self::ImplSource::*; pub use self::SelectionError::*; +use crate::infer::InferCtxt; pub use self::engine::{TraitEngine, TraitEngineExt}; pub use self::project::MismatchedProjectionTypes; @@ -117,6 +119,11 @@ pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>; pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; +/// A callback that can be provided to `inspect_typeck`. Invoked on evaluation +/// of root obligations. +pub type ObligationInspector<'tcx> = + fn(&InferCtxt<'tcx>, &PredicateObligation<'tcx>, Result<Certainty, NoSolution>); + pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, pub code: FulfillmentErrorCode<'tcx>, @@ -129,12 +136,12 @@ pub struct FulfillmentError<'tcx> { #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { /// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented. - CodeCycle(Vec<PredicateObligation<'tcx>>), - CodeSelectionError(SelectionError<'tcx>), - CodeProjectionError(MismatchedProjectionTypes<'tcx>), - CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate - CodeConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>), - CodeAmbiguity { + Cycle(Vec<PredicateObligation<'tcx>>), + SelectionError(SelectionError<'tcx>), + ProjectionError(MismatchedProjectionTypes<'tcx>), + SubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate + ConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>), + Ambiguity { /// Overflow reported from the new solver `-Znext-solver`, which will /// be reported as an regular error as opposed to a fatal error. overflow: bool, diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 51c06c8970b..3b4050fcd27 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -37,18 +37,19 @@ impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> { impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use traits::FulfillmentErrorCode::*; match *self { - super::CodeSelectionError(ref e) => write!(f, "{e:?}"), - super::CodeProjectionError(ref e) => write!(f, "{e:?}"), - super::CodeSubtypeError(ref a, ref b) => { + SelectionError(ref e) => write!(f, "{e:?}"), + ProjectionError(ref e) => write!(f, "{e:?}"), + SubtypeError(ref a, ref b) => { write!(f, "CodeSubtypeError({a:?}, {b:?})") } - super::CodeConstEquateError(ref a, ref b) => { + ConstEquateError(ref a, ref b) => { write!(f, "CodeConstEquateError({a:?}, {b:?})") } - super::CodeAmbiguity { overflow: false } => write!(f, "Ambiguity"), - super::CodeAmbiguity { overflow: true } => write!(f, "Overflow"), - super::CodeCycle(ref cycle) => write!(f, "Cycle({cycle:?})"), + Ambiguity { overflow: false } => write!(f, "Ambiguity"), + Ambiguity { overflow: true } => write!(f, "Overflow"), + Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), } } } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 319e8175809..a238eacda44 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -libloading = "0.7.1" +libloading = "0.8.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 32fba6ade88..5ca88090996 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -218,7 +218,9 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg { } } - if values.is_empty() && !values_any_specified && !any_specified { + if !values_specified && !any_specified { + // `cfg(name)` is equivalent to `cfg(name, values(none()))` so add + // an implicit `none()` values.insert(None); } else if !values.is_empty() && values_any_specified { error!( diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index cfa46447845..764306ce6ec 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,9 +1,10 @@ #![feature(box_patterns)] #![feature(decl_macro)] +#![feature(error_iter)] #![feature(internal_output_capture)] -#![feature(thread_spawn_unchecked)] #![feature(lazy_cell)] #![feature(let_chains)] +#![feature(thread_spawn_unchecked)] #![feature(try_blocks)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 588139a303c..555c822ad6d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -4,10 +4,10 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::{ build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg, - 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, + CollapseMacroDebuginfo, 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, }; use rustc_session::lint::Level; @@ -742,6 +742,7 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); + tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 9fd44e46b31..76b9e8de75f 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -162,15 +162,21 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( } fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn { + fn format_err(e: &(dyn std::error::Error + 'static)) -> String { + e.sources().map(|e| format!(": {e}")).collect() + } let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {path:?}: {err}"); + let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err)); early_dcx.early_fatal(err); }); let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") } .unwrap_or_else(|e| { - let err = format!("couldn't load codegen backend: {e}"); - early_dcx.early_fatal(err); + let e = format!( + "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}", + format_err(&e) + ); + early_dcx.early_fatal(e); }); // Intentionally leak the dynamic library. We can't ever unload it diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index abec12f52a6..0a632c4d12a 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -59,6 +59,9 @@ pub enum EscapeError { /// Non-ascii character in byte literal, byte string literal, or raw byte string literal. NonAsciiCharInByte, + // `\0` in a C string literal. + NulInCStr, + /// After a line ending with '\', the next line contains whitespace /// characters that are not skipped. UnskippedWhitespaceWarning, @@ -122,10 +125,20 @@ where { match mode { CStr => { - unescape_non_raw_common(src, mode, callback); + unescape_non_raw_common(src, mode, &mut |r, mut result| { + if let Ok(CStrUnit::Byte(0) | CStrUnit::Char('\0')) = result { + result = Err(EscapeError::NulInCStr); + } + callback(r, result) + }); } RawCStr => { - check_raw_common(src, mode, &mut |r, result| callback(r, result.map(CStrUnit::Char))); + check_raw_common(src, mode, &mut |r, mut result| { + if let Ok('\0') = result { + result = Err(EscapeError::NulInCStr); + } + callback(r, result.map(CStrUnit::Char)) + }); } Char | Byte | Str | RawStr | ByteStr | RawByteStr => unreachable!(), } diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 9e6a6f70eac..312874db3f5 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -354,6 +354,15 @@ pub(super) fn builtin( Applicability::MaybeIncorrect, ); } + } else { + db.note(format!("no expected values for `{name}`")); + + let sp = if let Some((_value, value_span)) = value { + name_span.to(value_span) + } else { + name_span + }; + db.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); } // We don't want to suggest adding values to well known names @@ -373,6 +382,8 @@ pub(super) fn builtin( if name == sym::feature { if let Some((value, _value_span)) = value { db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); + } else { + db.help("consider defining some features in `Cargo.toml`"); } } else if !is_cfg_a_well_know_name { db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0f4528d1d5c..39decf1faab 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1166,7 +1166,7 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, + | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false, keep_space); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index fb908fe2db1..0b7ecff8148 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -211,7 +211,7 @@ impl DiagnosticDeriveVariantBuilder { let code = nested.parse::<syn::LitStr>()?; tokens.extend(quote! { - diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); + diag.code(#code.to_string()); }); } else { span_err(path.span().unwrap(), "unknown argument") diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 08cc8173eb0..79d3482472a 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -libloading = "0.7.1" +libloading = "0.8.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 49e849964be..20e3ae3ba94 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1027,6 +1027,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.edition, Symbol::intern(name), &attrs, + false, ) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 912c2f36eb3..c3d6c21c402 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -283,7 +283,7 @@ provide! { tcx, def_id, other, cdata, tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index)) } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } - inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } + inherent_impls => { Ok(cdata.get_inherent_implementations_for_type(tcx, def_id.index)) } item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } is_mir_available => { cdata.is_item_mir_available(def_id.index) } is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) } @@ -328,7 +328,7 @@ provide! { tcx, def_id, other, cdata, traits => { tcx.arena.alloc_from_iter(cdata.get_traits()) } trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } - crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) } + crate_incoherent_impls => { Ok(cdata.get_incoherent_impls(tcx, other)) } dep_kind => { cdata.dep_kind } module_children => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a458b528a97..2d4e49e27d9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1485,7 +1485,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } let inherent_impls = tcx.with_stable_hashing_context(|hcx| { - tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true) + tcx.crate_inherent_impls(()).unwrap().inherent_impls.to_sorted(&hcx, true) }); for (def_id, impls) in inherent_impls { record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| { @@ -2028,7 +2028,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { empty_proc_macro!(self); let tcx = self.tcx; let all_impls = tcx.with_stable_hashing_context(|hcx| { - tcx.crate_inherent_impls(()).incoherent_impls.to_sorted(&hcx, true) + tcx.crate_inherent_impls(()).unwrap().incoherent_impls.to_sorted(&hcx, true) }); let all_impls: Vec<_> = all_impls diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 8e6ca645f80..82e56c9cbe2 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -10,7 +10,7 @@ derive_more = "0.99.17" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" -measureme = "10.0.0" +measureme = "11" polonius-engine = "0.13.0" rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 8a4fd01437f..e745913fabc 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -114,6 +114,7 @@ macro_rules! arena_types { [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, + [] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls, ]); ) } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 1574f0f1b31..949b786e3a7 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1,4 +1,4 @@ -use crate::hir::{ModuleItems, Owner}; +use crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; @@ -108,7 +108,7 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> { if self.current_id.local_id.index() != 0 { self.current_id.local_id = ItemLocalId::new(0); if let Some(node) = self.map.tcx.hir_owner(self.current_id.owner) { - return Some((self.current_id.owner, node.node)); + return Some((self.current_id.owner, node)); } } if self.current_id == CRATE_HIR_ID { @@ -126,23 +126,23 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> { // If this `HirId` doesn't have an entry, skip it and look for its `parent_id`. if let Some(node) = self.map.tcx.hir_owner(self.current_id.owner) { - return Some((self.current_id.owner, node.node)); + return Some((self.current_id.owner, node)); } } } } impl<'tcx> TyCtxt<'tcx> { + #[inline] + fn hir_owner(self, owner: OwnerId) -> Option<OwnerNode<'tcx>> { + Some(self.hir_owner_nodes(owner).as_owner()?.node()) + } + /// Retrieves the `hir::Node` corresponding to `id`, returning `None` if cannot be found. pub fn opt_hir_node(self, id: HirId) -> Option<Node<'tcx>> { - if id.local_id == ItemLocalId::from_u32(0) { - let owner = self.hir_owner(id.owner)?; - Some(owner.node.into()) - } else { - let owner = self.hir_owner_nodes(id.owner).as_owner()?; - let node = owner.nodes[id.local_id].as_ref()?; - Some(node.node) - } + let owner = self.hir_owner_nodes(id.owner).as_owner()?; + let node = owner.nodes[id.local_id].as_ref()?; + Some(node.node) } /// Retrieves the `hir::Node` corresponding to `id`, returning `None` if cannot be found. @@ -174,7 +174,7 @@ impl<'hir> Map<'hir> { #[inline] pub fn root_module(self) -> &'hir Mod<'hir> { - match self.tcx.hir_owner(CRATE_OWNER_ID).map(|o| o.node) { + match self.tcx.hir_owner(CRATE_OWNER_ID) { Some(OwnerNode::Crate(item)) => item, _ => bug!(), } @@ -242,27 +242,27 @@ impl<'hir> Map<'hir> { pub fn get_generics(self, id: LocalDefId) -> Option<&'hir Generics<'hir>> { let node = self.tcx.hir_owner(OwnerId { def_id: id })?; - node.node.generics() + node.generics() } pub fn owner(self, id: OwnerId) -> OwnerNode<'hir> { - self.tcx.hir_owner(id).unwrap_or_else(|| bug!("expected owner for {:?}", id)).node + self.tcx.hir_owner(id).unwrap_or_else(|| bug!("expected owner for {:?}", id)) } pub fn item(self, id: ItemId) -> &'hir Item<'hir> { - self.tcx.hir_owner(id.owner_id).unwrap().node.expect_item() + self.tcx.hir_owner(id.owner_id).unwrap().expect_item() } pub fn trait_item(self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.tcx.hir_owner(id.owner_id).unwrap().node.expect_trait_item() + self.tcx.hir_owner(id.owner_id).unwrap().expect_trait_item() } pub fn impl_item(self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.tcx.hir_owner(id.owner_id).unwrap().node.expect_impl_item() + self.tcx.hir_owner(id.owner_id).unwrap().expect_impl_item() } pub fn foreign_item(self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { - self.tcx.hir_owner(id.owner_id).unwrap().node.expect_foreign_item() + self.tcx.hir_owner(id.owner_id).unwrap().expect_foreign_item() } pub fn body(self, id: BodyId) -> &'hir Body<'hir> { @@ -436,7 +436,7 @@ impl<'hir> Map<'hir> { pub fn get_module(self, module: LocalModDefId) -> (&'hir Mod<'hir>, Span, HirId) { let hir_id = HirId::make_owner(module.to_local_def_id()); - match self.tcx.hir_owner(hir_id.owner).map(|o| o.node) { + match self.tcx.hir_owner(hir_id.owner) { Some(OwnerNode::Item(&Item { span, kind: ItemKind::Mod(m), .. })) => (m, span, hir_id), Some(OwnerNode::Crate(item)) => (item, item.spans.inner_span, hir_id), node => panic!("not a module: {node:?}"), @@ -726,11 +726,10 @@ impl<'hir> Map<'hir> { pub fn get_foreign_abi(self, hir_id: HirId) -> Abi { let parent = self.get_parent_item(hir_id); - if let Some(node) = self.tcx.hir_owner(parent) { - if let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) = node.node - { - return *abi; - } + if let Some(node) = self.tcx.hir_owner(parent) + && let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) = node + { + return *abi; } bug!( "expected foreign mod or inlined parent, found {}", @@ -742,33 +741,32 @@ impl<'hir> Map<'hir> { self.tcx .hir_owner(OwnerId { def_id }) .unwrap_or_else(|| bug!("expected owner for {:?}", def_id)) - .node } pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> { match self.tcx.hir_owner(OwnerId { def_id: id }) { - Some(Owner { node: OwnerNode::Item(item), .. }) => item, + Some(OwnerNode::Item(item)) => item, _ => bug!("expected item, found {}", self.node_to_string(HirId::make_owner(id))), } } pub fn expect_impl_item(self, id: LocalDefId) -> &'hir ImplItem<'hir> { match self.tcx.hir_owner(OwnerId { def_id: id }) { - Some(Owner { node: OwnerNode::ImplItem(item), .. }) => item, + Some(OwnerNode::ImplItem(item)) => item, _ => bug!("expected impl item, found {}", self.node_to_string(HirId::make_owner(id))), } } pub fn expect_trait_item(self, id: LocalDefId) -> &'hir TraitItem<'hir> { match self.tcx.hir_owner(OwnerId { def_id: id }) { - Some(Owner { node: OwnerNode::TraitItem(item), .. }) => item, + Some(OwnerNode::TraitItem(item)) => item, _ => bug!("expected trait item, found {}", self.node_to_string(HirId::make_owner(id))), } } pub fn get_fn_output(self, def_id: LocalDefId) -> Option<&'hir FnRetTy<'hir>> { match self.tcx.hir_owner(OwnerId { def_id }) { - Some(Owner { node, .. }) => node.fn_decl().map(|fn_decl| &fn_decl.output), + Some(node) => node.fn_decl().map(|fn_decl| &fn_decl.output), _ => None, } } @@ -782,7 +780,7 @@ impl<'hir> Map<'hir> { pub fn expect_foreign_item(self, id: OwnerId) -> &'hir ForeignItem<'hir> { match self.tcx.hir_owner(id) { - Some(Owner { node: OwnerNode::ForeignItem(item), .. }) => item, + Some(OwnerNode::ForeignItem(item)) => item, _ => { bug!( "expected foreign item, found {}", diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index af99c7d55c3..2d4d5deaefa 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -8,34 +8,12 @@ pub mod place; use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::*; -use rustc_query_system::ich::StableHashingContext; use rustc_span::{ErrorGuaranteed, ExpnId, DUMMY_SP}; -/// Top-level HIR node for current owner. This only contains the node for which -/// `HirId::local_id == 0`, and excludes bodies. -/// -/// This struct exists to encapsulate all access to the hir_owner query in this module, and to -/// implement HashStable without hashing bodies. -#[derive(Copy, Clone, Debug)] -pub struct Owner<'tcx> { - node: OwnerNode<'tcx>, -} - -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> { - #[inline] - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - // Perform a shallow hash instead using the deep hash saved in `OwnerNodes`. This lets us - // differentiate queries that depend on the full HIR tree from those that only depend on - // the item signature. - hcx.without_hir_bodies(|hcx| self.node.hash_stable(hcx, hasher)); - } -} - /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. #[derive(Debug, HashStable, Encodable, Decodable)] @@ -149,11 +127,6 @@ pub fn provide(providers: &mut Providers) { providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; - providers.hir_owner = |tcx, id| { - let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?; - let node = owner.node(); - Some(Owner { node }) - }; providers.opt_local_def_id_to_hir_id = |tcx, id| { let owner = tcx.hir_crate(()).owners[id].map(|_| ()); Some(match owner { @@ -162,7 +135,13 @@ pub fn provide(providers: &mut Providers) { MaybeOwner::NonOwner(hir_id) => hir_id, }) }; - providers.hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners[id.def_id].map(|i| &i.nodes); + providers.hir_owner_nodes = |tcx, id| { + if let Some(i) = tcx.hir_crate(()).owners.get(id.def_id) { + i.map(|i| &i.nodes) + } else { + MaybeOwner::Phantom + } + }; providers.hir_owner_parent = |tcx, id| { // Accessing the local_parent is ok since its value is hashed as part of `id`'s DefPathHash. tcx.opt_local_parent(id.def_id).map_or(CRATE_HIR_ID, |parent| { diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 6e50e894046..c35799ef47f 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -120,7 +120,7 @@ pub enum ConstVariableOriginKind { #[derive(Copy, Clone, Debug)] pub enum ConstVariableValue<'tcx> { Known { value: ty::Const<'tcx> }, - Unknown { universe: ty::UniverseIndex }, + Unknown { origin: ConstVariableOrigin, universe: ty::UniverseIndex }, } impl<'tcx> ConstVariableValue<'tcx> { @@ -134,12 +134,6 @@ impl<'tcx> ConstVariableValue<'tcx> { } } -#[derive(Copy, Clone, Debug)] -pub struct ConstVarValue<'tcx> { - pub origin: ConstVariableOrigin, - pub val: ConstVariableValue<'tcx>, -} - #[derive(PartialEq, Copy, Clone, Debug)] pub struct ConstVidKey<'tcx> { pub vid: ty::ConstVid, @@ -153,7 +147,7 @@ impl<'tcx> From<ty::ConstVid> for ConstVidKey<'tcx> { } impl<'tcx> UnifyKey for ConstVidKey<'tcx> { - type Value = ConstVarValue<'tcx>; + type Value = ConstVariableValue<'tcx>; #[inline] fn index(&self) -> u32 { self.vid.as_u32() @@ -167,23 +161,23 @@ impl<'tcx> UnifyKey for ConstVidKey<'tcx> { } } -impl<'tcx> UnifyValue for ConstVarValue<'tcx> { +impl<'tcx> UnifyValue for ConstVariableValue<'tcx> { type Error = NoError; fn unify_values(&value1: &Self, &value2: &Self) -> Result<Self, Self::Error> { - Ok(match (value1.val, value2.val) { + match (value1, value2) { (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { bug!("equating two const variables, both of which have known values") } // If one side is known, prefer that one. - (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => value1, - (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => value2, + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => Ok(value1), + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => Ok(value2), // If both sides are *unknown*, it hardly matters, does it? ( - ConstVariableValue::Unknown { universe: universe1 }, - ConstVariableValue::Unknown { universe: universe2 }, + ConstVariableValue::Unknown { origin, universe: universe1 }, + ConstVariableValue::Unknown { origin: _, universe: universe2 }, ) => { // If we unify two unbound variables, ?T and ?U, then whatever // value they wind up taking (which must be the same value) must @@ -191,12 +185,9 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. let universe = cmp::min(universe1, universe2); - ConstVarValue { - val: ConstVariableValue::Unknown { universe }, - origin: value1.origin, - } + Ok(ConstVariableValue::Unknown { origin, universe }) } - }) + } } } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index c5e4dfaf19e..4ab16cf19ba 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, MultiSpan}; +use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, @@ -322,8 +322,6 @@ pub fn struct_lint_level( err.span(span); } - err.is_lint(); - // If this code originates in a foreign macro, aka something that this crate // did not itself author, then it's likely that there's nothing this crate // can do about it. We probably want to skip the lint entirely. @@ -351,8 +349,7 @@ pub fn struct_lint_level( // suppressed the lint due to macros. err.primary_message(msg); - let name = lint.name_lower(); - err.code(DiagnosticId::Lint { name, has_future_breakage }); + err.is_lint(lint.name_lower(), has_future_breakage); // Lint diagnostics that are covered by the expect level will not be emitted outside // the compiler. It is therefore not necessary to add any information for the user. diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 092b59deeff..b20df1b2343 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -145,7 +145,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> EvalToConstValueResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. - let inputs = self.erase_regions(param_env.and(cid)); + let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); if let Some(span) = span { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span)) @@ -164,7 +164,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> EvalToValTreeResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. - let inputs = self.erase_regions(param_env.and(cid)); + let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); debug!(?inputs); if let Some(span) = span { // The query doesn't know where it is being invoked, so we need to fix the span. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 36f5ba161d5..624ae8c22f9 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1672,13 +1672,19 @@ mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; // tidy-alphabetical-start - static_assert_size!(BasicBlockData<'_>, 136); + // This can be removed after i128:128 is in the bootstrap compiler's target. + #[cfg(not(bootstrap))] + static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(SourceScopeData<'_>, 72); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); - static_assert_size!(Terminator<'_>, 104); - static_assert_size!(TerminatorKind<'_>, 88); + // This can be removed after i128:128 is in the bootstrap compiler's target. + #[cfg(not(bootstrap))] + static_assert_size!(Terminator<'_>, 112); + // This can be removed after i128:128 is in the bootstrap compiler's target. + #[cfg(not(bootstrap))] + static_assert_size!(TerminatorKind<'_>, 96); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 1a6b0f4031d..3b60eba2dfe 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -142,15 +142,17 @@ fn dump_matched_mir_node<'tcx, F>( } } -/// Returns the file basename portion (without extension) of a filename path -/// where we should dump a MIR representation output files. -fn dump_file_basename<'tcx>( +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path<'tcx>( tcx: TyCtxt<'tcx>, + extension: &str, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, -) -> String { +) -> PathBuf { let source = body.source; let promotion_id = match source.promoted { Some(id) => format!("-{id:?}"), @@ -186,32 +188,31 @@ fn dump_file_basename<'tcx>( _ => String::new(), }; - format!( - "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}", - ) -} - -/// Returns the path to the filename where we should dump a given MIR. -/// Also used by other bits of code (e.g., NLL inference) that dump -/// graphviz data or other things. -fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf { let mut file_path = PathBuf::new(); file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir)); - let file_name = format!("{basename}.{extension}",); + let file_name = format!( + "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}", + ); file_path.push(&file_name); file_path } -/// Attempts to open the MIR dump file with the given name and extension. -fn create_dump_file_with_basename( - tcx: TyCtxt<'_>, - file_basename: &str, +/// Attempts to open a file where we should dump a given MIR or other +/// bit of MIR-related data. Used by `mir-dump`, but also by other +/// bits of code (e.g., NLL inference) that dump graphviz data or +/// other things, and hence takes the extension as an argument. +pub fn create_dump_file<'tcx>( + tcx: TyCtxt<'tcx>, extension: &str, + pass_num: bool, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, ) -> io::Result<io::BufWriter<fs::File>> { - let file_path = dump_path(tcx, file_basename, extension); + let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body); if let Some(parent) = file_path.parent() { fs::create_dir_all(parent).map_err(|e| { io::Error::new( @@ -225,25 +226,6 @@ fn create_dump_file_with_basename( })?)) } -/// Attempts to open a file where we should dump a given MIR or other -/// bit of MIR-related data. Used by `mir-dump`, but also by other -/// bits of code (e.g., NLL inference) that dump graphviz data or -/// other things, and hence takes the extension as an argument. -pub fn create_dump_file<'tcx>( - tcx: TyCtxt<'tcx>, - extension: &str, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, -) -> io::Result<io::BufWriter<fs::File>> { - create_dump_file_with_basename( - tcx, - &dump_file_basename(tcx, pass_num, pass_name, disambiguator, body), - extension, - ) -} - /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies @@ -785,7 +767,7 @@ impl<'tcx> TerminatorKind<'tcx> { Call { func, args, destination, .. } => { write!(fmt, "{destination:?} = ")?; write!(fmt, "{func:?}(")?; - for (index, arg) in args.iter().enumerate() { + for (index, arg) in args.iter().map(|a| &a.node).enumerate() { if index > 0 { write!(fmt, ", ")?; } @@ -1116,10 +1098,10 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> | ProjectionElem::Subtype(_) | ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { - write!(fmt, "(").unwrap(); + write!(fmt, "(")?; } ProjectionElem::Deref => { - write!(fmt, "(*").unwrap(); + write!(fmt, "(*")?; } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 462076d750f..6ebe57e29da 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -16,6 +16,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::def_id::DefId; use rustc_hir::{self, CoroutineKind}; use rustc_index::IndexVec; +use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_ast::Mutability; @@ -673,7 +674,9 @@ pub enum TerminatorKind<'tcx> { /// 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: Vec<Operand<'tcx>>, + /// The span for each arg is also included + /// (e.g. `a` and `b` in `x.foo(a, b)`). + args: Vec<Spanned<Operand<'tcx>>>, /// Where the returned value will be written destination: Place<'tcx>, /// Where to go after this call returns. If none, the call necessarily diverges. diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 234ccee5546..5597609c7d7 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -229,6 +229,18 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(c) => c.const_.ty(), } } + + pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span + where + D: HasLocalDecls<'tcx>, + { + match self { + &Operand::Copy(ref l) | &Operand::Move(ref l) => { + local_decls.local_decls()[l.local].source_info.span + } + Operand::Constant(c) => c.span, + } + } } impl<'tcx> BinOp { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 2ccf5a9f6f7..4696f54c897 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -524,7 +524,7 @@ macro_rules! make_mir_visitor { } => { self.visit_operand(func, location); for arg in args { - self.visit_operand(arg, location); + self.visit_operand(&$($mutability)? arg.node, location); } self.visit_place( destination, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b9200f1abf1..778b4308d2e 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -74,10 +74,18 @@ impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> { type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()]; } +impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> { + type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()]; +} + impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> { type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()]; } +impl<T> EraseType for Result<&'_ [T], rustc_errors::ErrorGuaranteed> { + type Result = [u8; size_of::<Result<&'static [()], rustc_errors::ErrorGuaranteed>>()]; +} + impl<T> EraseType for Result<&'_ T, traits::CodegenObligationError> { type Result = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()]; } @@ -160,10 +168,6 @@ impl<T> EraseType for Option<&'_ [T]> { type Result = [u8; size_of::<Option<&'static [()]>>()]; } -impl EraseType for Option<rustc_middle::hir::Owner<'_>> { - type Result = [u8; size_of::<Option<rustc_middle::hir::Owner<'static>>>()]; -} - impl EraseType for Option<mir::DestructuredConstant<'_>> { type Result = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()]; } @@ -320,7 +324,6 @@ macro_rules! tcx_lifetime { } tcx_lifetime! { - rustc_middle::hir::Owner, rustc_middle::middle::exported_symbols::ExportedSymbol, rustc_middle::mir::Const, rustc_middle::mir::DestructuredConstant, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 945f17d5df2..69d3974184d 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -9,6 +9,7 @@ use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{GenericArg, GenericArgsRef}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, ModDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirId, OwnerId}; +use rustc_query_system::query::DefIdCacheSelector; use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -152,7 +153,7 @@ impl Key for LocalDefId { } impl Key for DefId { - type CacheSelector = DefaultCacheSelector<Self>; + type CacheSelector = DefIdCacheSelector; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1dc77220881..a9dc7f5d11a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -174,14 +174,6 @@ rustc_queries! { cache_on_disk_if { true } } - /// Gives access to the HIR node for the HIR owner `key`. - /// - /// This can be conveniently accessed by methods on `tcx.hir()`. - /// Avoid calling this query directly. - query hir_owner(key: hir::OwnerId) -> Option<crate::hir::Owner<'tcx>> { - desc { |tcx| "getting HIR owner of `{}`", tcx.def_path_str(key) } - } - /// Gives access to the HIR ID for the given `LocalDefId` owner `key` if any. /// /// Definitions that were generated with no HIR, would be fed to return `None`. @@ -859,13 +851,13 @@ rustc_queries! { /// Maps a `DefId` of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. - query inherent_impls(key: DefId) -> &'tcx [DefId] { + query inherent_impls(key: DefId) -> Result<&'tcx [DefId], ErrorGuaranteed> { desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } - query incoherent_impls(key: SimplifiedType) -> &'tcx [DefId] { + query incoherent_impls(key: SimplifiedType) -> Result<&'tcx [DefId], ErrorGuaranteed> { desc { |tcx| "collecting all inherent impls for `{:?}`", key } } @@ -961,8 +953,9 @@ rustc_queries! { desc { |tcx| "checking deathness of variables in {}", describe_as_module(key, tcx) } } - query check_mod_impl_wf(key: LocalModDefId) -> () { + query check_mod_impl_wf(key: LocalModDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } + ensure_forwards_result_if_red } query check_mod_type_wf(key: LocalModDefId) -> Result<(), ErrorGuaranteed> { @@ -1011,15 +1004,16 @@ rustc_queries! { /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. - query crate_inherent_impls(k: ()) -> &'tcx CrateInherentImpls { - arena_cache + query crate_inherent_impls(k: ()) -> Result<&'tcx CrateInherentImpls, ErrorGuaranteed> { desc { "finding all inherent impls defined in crate" } + ensure_forwards_result_if_red } /// Checks all types in the crate for overlap in their inherent impls. Reports errors. /// Not meant to be used directly outside of coherence. - query crate_inherent_impls_overlap_check(_: ()) -> () { + query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> { desc { "check for overlap between inherent impls defined in this crate" } + ensure_forwards_result_if_red } /// Checks whether all impls in the crate pass the overlap check, returning @@ -1645,7 +1639,7 @@ rustc_queries! { /// /// Do not call this directly, but instead use the `incoherent_impls` query. /// This query is only used to get the data necessary for that query. - query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> &'tcx [DefId] { + query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> Result<&'tcx [DefId], ErrorGuaranteed> { desc { |tcx| "collecting all impls for a type in a crate" } separate_provide_extern } @@ -1949,7 +1943,7 @@ rustc_queries! { desc { "normalizing `{}`", goal.value } } - query implied_outlives_bounds( + query implied_outlives_bounds_compat( goal: CanonicalTyGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, @@ -1958,6 +1952,15 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}`", goal.value.value } } + query implied_outlives_bounds( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, + NoSolution, + > { + desc { "computing implied outlives bounds v2 for `{}`", goal.value.value } + } + /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index a41d4f1ad58..8d88488e167 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -174,7 +174,7 @@ pub fn query_ensure<'tcx, Cache>( } #[inline] -pub fn query_ensure_error_guaranteed<'tcx, Cache>( +pub fn query_ensure_error_guaranteed<'tcx, Cache, T>( tcx: TyCtxt<'tcx>, execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>, query_cache: &Cache, @@ -182,14 +182,16 @@ pub fn query_ensure_error_guaranteed<'tcx, Cache>( check_cache: bool, ) -> Result<(), ErrorGuaranteed> where - Cache: QueryCache<Value = super::erase::Erase<Result<(), ErrorGuaranteed>>>, + Cache: QueryCache<Value = super::erase::Erase<Result<T, ErrorGuaranteed>>>, + Result<T, ErrorGuaranteed>: EraseType, { let key = key.into_query_param(); if let Some(res) = try_get_cached(tcx, query_cache, &key) { - super::erase::restore(res) + super::erase::restore(res).map(drop) } else { execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) .map(super::erase::restore) + .map(|res| res.map(drop)) // Either we actually executed the query, which means we got a full `Result`, // or we can just assume the query succeeded, because it was green in the // incremental cache. If it is green, that means that the previous compilation @@ -205,7 +207,7 @@ macro_rules! query_ensure { query_ensure($($args)*) }; ([(ensure_forwards_result_if_red) $($rest:tt)*]$($args:tt)*) => { - query_ensure_error_guaranteed($($args)*) + query_ensure_error_guaranteed($($args)*).map(|_| ()) }; ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { query_ensure!([$($modifiers)*]$($args)*) @@ -667,5 +669,7 @@ mod sealed { pub use sealed::IntoQueryParam; +use super::erase::EraseType; + #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index bebee8df10e..135680b9349 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -604,7 +604,7 @@ pub enum SelectionError<'tcx> { /// After a closure impl has selected, its "outputs" were evaluated /// (which for closures includes the "input" type params) and they /// didn't resolve. See `confirm_poly_trait_refs` for more. - OutputTypeParameterMismatch(Box<SelectionOutputTypeParameterMismatch<'tcx>>), + SignatureMismatch(Box<SignatureMismatchData<'tcx>>), /// The trait pointed by `DefId` is not object safe. TraitNotObjectSafe(DefId), /// A given constant couldn't be evaluated. @@ -618,7 +618,7 @@ pub enum SelectionError<'tcx> { } #[derive(Clone, Debug, TypeVisitable)] -pub struct SelectionOutputTypeParameterMismatch<'tcx> { +pub struct SignatureMismatchData<'tcx> { pub found_trait_ref: ty::PolyTraitRef<'tcx>, pub expected_trait_ref: ty::PolyTraitRef<'tcx>, pub terr: ty::error::TypeError<'tcx>, diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 5f02dd22961..61d96cad57d 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> { /// case they are called implied bounds). They are fed to the /// `OutlivesEnv` which in turn is supplied to the region checker and /// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)] pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ad9296a4cc8..a5d9e0fcf44 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2535,8 +2535,7 @@ impl<'tcx> TyCtxt<'tcx> { if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() { return span; } - let collapse_debuginfo_enabled = self.features().collapse_debuginfo; - hygiene::walk_chain_collapsed(span, upto, collapse_debuginfo_enabled) + hygiene::walk_chain_collapsed(span, upto, self.features().collapse_debuginfo) } #[inline] diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index a10bdc6012c..4028de27cae 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -15,7 +15,6 @@ use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{DefIdMap, DefIdSet, ModDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPathDataName}; use rustc_hir::LangItem; -use rustc_session::config::TrimmedDefPaths; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_session::Limit; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -365,26 +364,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// Try to see if this path can be trimmed to a unique symbol name. fn try_print_trimmed_def_path(&mut self, def_id: DefId) -> Result<bool, PrintError> { - if with_forced_trimmed_paths() { - let trimmed = self.force_print_trimmed_def_path(def_id)?; - if trimmed { - return Ok(true); - } + if with_forced_trimmed_paths() && self.force_print_trimmed_def_path(def_id)? { + return Ok(true); } - if !self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths - || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) - || with_no_trimmed_paths() - || with_crate_prefix() + if self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths + && self.tcx().sess.opts.trimmed_def_paths + && !with_no_trimmed_paths() + && !with_crate_prefix() + && let Some(symbol) = self.tcx().trimmed_def_paths(()).get(&def_id) { - return Ok(false); - } - - match self.tcx().trimmed_def_paths(()).get(&def_id) { - None => Ok(false), - Some(symbol) => { - write!(self, "{}", Ident::with_dummy_span(*symbol))?; - Ok(true) - } + write!(self, "{}", Ident::with_dummy_span(*symbol))?; + Ok(true) + } else { + Ok(false) } } @@ -1021,7 +1013,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { .extend( // Group the return ty with its def id, if we had one. entry.return_ty.map(|ty| { - (tcx.require_lang_item(LangItem::FnOnce, None), ty) + (tcx.require_lang_item(LangItem::FnOnceOutput, None), ty) }), ); } @@ -3080,18 +3072,19 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N /// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`]. // this is pub to be able to intra-doc-link it pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> { - let mut map: DefIdMap<Symbol> = Default::default(); - - if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { - // Trimming paths is expensive and not optimized, since we expect it to only be used for error reporting. - // - // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` - // wrapper can be used to suppress this query, in exchange for full paths being formatted. - tcx.sess.good_path_delayed_bug( - "trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging", - ); - } - + assert!(tcx.sess.opts.trimmed_def_paths); + + // Trimming paths is expensive and not optimized, since we expect it to only be used for error + // reporting. + // + // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` + // wrapper can be used to suppress this query, in exchange for full paths being formatted. + tcx.sess.good_path_delayed_bug( + "trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging", + ); + + // Once constructed, unique namespace+symbol pairs will have a `Some(_)` entry, while + // non-unique pairs will have a `None` entry. let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option<DefId>> = &mut FxHashMap::default(); @@ -3121,6 +3114,8 @@ pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> { } }); + // Put the symbol from all the unique namespace+symbol pairs into `map`. + let mut map: DefIdMap<Symbol> = Default::default(); for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() { use std::collections::hash_map::Entry::{Occupied, Vacant}; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7c869adbd83..11b579a1f85 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -9,6 +9,7 @@ use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt}; use rustc_hir::def::Namespace; +use rustc_span::source_map::Spanned; use rustc_target::abi::TyAndLayout; use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, WithInfcx}; @@ -819,3 +820,27 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for TyAndLayout<'tcx, Ty<'tcx>> { visitor.visit_ty(self.ty) } } + +impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>> + Debug + Clone> TypeVisitable<TyCtxt<'tcx>> + for Spanned<T> +{ + fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.node.visit_with(visitor)?; + self.span.visit_with(visitor)?; + ControlFlow::Continue(()) + } +} + +impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>> + Debug + Clone> TypeFoldable<TyCtxt<'tcx>> + for Spanned<T> +{ + fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(Spanned { + node: self.node.try_fold_with(folder)?, + span: self.span.try_fold_with(folder)?, + }) + } +} diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 227a0753d04..d85b541d363 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -254,16 +254,28 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } /// Query provider for `incoherent_impls`. -pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { +pub(super) fn incoherent_impls_provider( + tcx: TyCtxt<'_>, + simp: SimplifiedType, +) -> Result<&[DefId], ErrorGuaranteed> { let mut impls = Vec::new(); + let mut res = Ok(()); for cnum in iter::once(LOCAL_CRATE).chain(tcx.crates(()).iter().copied()) { - for &impl_def_id in tcx.crate_incoherent_impls((cnum, simp)) { + let incoherent_impls = match tcx.crate_incoherent_impls((cnum, simp)) { + Ok(impls) => impls, + Err(e) => { + res = Err(e); + continue; + } + }; + for &impl_def_id in incoherent_impls { impls.push(impl_def_id) } } debug!(?impls); + res?; - tcx.arena.alloc_slice(&impls) + Ok(tcx.arena.alloc_slice(&impls)) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 372f11a5acc..ef621f29bfd 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -604,19 +604,15 @@ impl<'tcx> TyCtxt<'tcx> { /// wrapped in a binder. pub fn closure_env_ty( self, - closure_def_id: DefId, - closure_args: GenericArgsRef<'tcx>, + closure_ty: Ty<'tcx>, + closure_kind: ty::ClosureKind, env_region: ty::Region<'tcx>, - ) -> Option<Ty<'tcx>> { - let closure_ty = Ty::new_closure(self, closure_def_id, closure_args); - let closure_kind_ty = closure_args.as_closure().kind_ty(); - let closure_kind = closure_kind_ty.to_opt_closure_kind()?; - let env_ty = match closure_kind { + ) -> Ty<'tcx> { + match closure_kind { ty::ClosureKind::Fn => Ty::new_imm_ref(self, env_region, closure_ty), ty::ClosureKind::FnMut => Ty::new_mut_ref(self, env_region, closure_ty), ty::ClosureKind::FnOnce => closure_ty, - }; - Some(env_ty) + } } /// Returns `true` if the node pointed to by `def_id` is a `static` item. diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs index 9f1e4ac11c2..0ca4fce5da9 100644 --- a/compiler/rustc_middle/src/util/find_self_call.rs +++ b/compiler/rustc_middle/src/util/find_self_call.rs @@ -2,6 +2,7 @@ use crate::mir::*; use crate::ty::GenericArgsRef; use crate::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; +use rustc_span::source_map::Spanned; /// Checks if the specified `local` is used as the `self` parameter of a method call /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is @@ -23,7 +24,13 @@ pub fn find_self_call<'tcx>( tcx.opt_associated_item(def_id) { debug!("find_self_call: args={:?}", fn_args); - if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args { + if let [ + Spanned { + node: Operand::Move(self_place) | Operand::Copy(self_place), .. + }, + .., + ] = **args + { if self_place.as_local() == Some(local) { return Some((def_id, fn_args)); } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 6d681dc295e..d71f7121322 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start either = "1" +itertools = "0.11" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 615b553434f..2f11cb123ee 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa mir_build_non_const_path = runtime values cannot be referenced in patterns +mir_build_non_empty_never_pattern = + mismatched types + .label = a never pattern must be used on an uninhabited type + .note = the matched value is of type `{$ty}` + mir_build_non_exhaustive_match_all_arms_guarded = match arms with guards don't count towards exhaustivity diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index e3dea2212df..5428333a116 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -2,6 +2,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::ty::cast::mir_cast_kind; use rustc_middle::{mir::*, thir::*, ty}; +use rustc_span::source_map::Spanned; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -162,7 +163,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { let fun = self.parse_operand(*fun)?; let args = args .iter() - .map(|arg| self.parse_operand(*arg)) + .map(|arg| + Ok(Spanned { node: self.parse_operand(*arg)?, span: self.thir.exprs[*arg].span } ) + ) .collect::<PResult<Vec<_>>>()?; Ok(TerminatorKind::Call { func: fun, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index f799be165ec..6e8af7bb6df 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -2,6 +2,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::ty::util::IntTypeExt; +use rustc_span::source_map::Spanned; use rustc_target::abi::{Abi, FieldIdx, Primitive}; use crate::build::expr::as_place::PlaceBase; @@ -15,7 +16,7 @@ use rustc_middle::thir::*; use rustc_middle::ty::cast::{mir_cast_kind, CastTy}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns an rvalue suitable for use until the end of the current @@ -156,7 +157,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { synth_info, TerminatorKind::Call { func: exchange_malloc, - args: vec![Operand::Move(size), Operand::Move(align)], + args: vec![ + Spanned { node: Operand::Move(size), span: DUMMY_SP }, + Spanned { node: Operand::Move(align), span: DUMMY_SP }, + ], destination: storage, target: Some(success), unwind: UnwindAction::Continue, @@ -733,7 +737,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.diverge_from(block); block = success; } - this.record_operands_moved(&[value_operand]); + this.record_operands_moved(&[Spanned { node: value_operand, span: DUMMY_SP }]); } block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), IndexVec::new())) } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 060a3b521a4..2978491d646 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -9,6 +9,7 @@ use rustc_hir as hir; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::CanonicalUserTypeAnnotation; +use rustc_span::source_map::Spanned; use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -248,7 +249,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let args: Vec<_> = args .into_iter() .copied() - .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) + .map(|arg| Spanned { + node: unpack!(block = this.as_local_call_operand(block, arg)), + span: this.thir.exprs[arg].span, + }) .collect(); let success = this.cfg.start_new_block(); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 53e5d70f946..990be30b2d6 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -17,8 +17,9 @@ use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::GenericArg; use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; use rustc_span::def_id::DefId; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; use std::cmp::Ordering; @@ -271,7 +272,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: None, const_: method, })), - args: vec![Operand::Move(ref_string)], + args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }], destination: ref_str, target: Some(eq_block), unwind: UnwindAction::Continue, @@ -526,7 +527,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: method, })), - args: vec![Operand::Copy(val), expect], + args: vec![ + Spanned { node: Operand::Copy(val), span: DUMMY_SP }, + Spanned { node: expect, span: DUMMY_SP }, + ], destination: eq_result, target: Some(eq_block), unwind: UnwindAction::Continue, diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index b8d08319422..714c5f2686e 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -1,5 +1,6 @@ use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; +use itertools::Itertools; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_ast::attr; @@ -654,7 +655,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - ty::ClosureKind::FnOnce => closure_ty, }; ( - [self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), + [self_ty].into_iter().chain(sig.inputs()[0].tuple_fields()).collect(), sig.output(), None, ) @@ -835,7 +836,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.upvars = tcx .closure_captures(self.def_id) .iter() - .zip(capture_tys) + .zip_eq(capture_tys) .enumerate() .map(|(i, (captured_place, ty))| { let name = captured_place.to_symbol(); diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 48b237f3ae6..6827797df37 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -91,6 +91,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{ExprId, LintLevel}; use rustc_session::lint::Level; +use rustc_span::source_map::Spanned; use rustc_span::{Span, DUMMY_SP}; #[derive(Debug)] @@ -1020,14 +1021,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// spurious borrow-check errors -- the problem, ironically, is /// not the `DROP(_X)` itself, but the (spurious) unwind pathways /// that it creates. See #64391 for an example. - pub(crate) fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) { + pub(crate) fn record_operands_moved(&mut self, operands: &[Spanned<Operand<'tcx>>]) { let local_scope = self.local_scope(); let scope = self.scopes.scopes.last_mut().unwrap(); assert_eq!(scope.region_scope, local_scope, "local scope is not the topmost scope!",); // look for moves of a local variable, like `MOVE(_X)` - let locals_moved = operands.iter().flat_map(|operand| match operand { + let locals_moved = operands.iter().flat_map(|operand| match operand.node { Operand::Copy(_) | Operand::Constant(_) => None, Operand::Move(place) => place.as_local(), }); diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 61ad99acf38..e3cc21cef11 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -788,6 +788,16 @@ pub struct FloatPattern; #[diag(mir_build_pointer_pattern)] pub struct PointerPattern; +#[derive(Diagnostic)] +#[diag(mir_build_non_empty_never_pattern)] +#[note] +pub struct NonEmptyNeverPattern<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'tcx>, +} + #[derive(LintDiagnostic)] #[diag(mir_build_indirect_structural_match)] #[note(mir_build_type_not_structural_tip)] diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 78d72b30284..22094c112fc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -2,6 +2,7 @@ use crate::errors; use crate::thir::cx::region::Scope; use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; +use itertools::Itertools; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -565,7 +566,7 @@ impl<'tcx> Cx<'tcx> { .tcx .closure_captures(def_id) .iter() - .zip(args.upvar_tys()) + .zip_eq(args.upvar_tys()) .map(|(captured_place, ty)| { let upvars = self.capture_upvar(expr, captured_place, ty); self.thir.exprs.push(upvars) diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index fa3dd2afa85..5d0bb3954cc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -117,50 +117,32 @@ impl<'tcx> Cx<'tcx> { pat_from_hir(self.tcx, self.param_env, self.typeck_results(), p) } - fn closure_env_param(&self, owner_def: LocalDefId, owner_id: HirId) -> Option<Param<'tcx>> { - match self.tcx.def_kind(owner_def) { - DefKind::Closure if self.tcx.is_coroutine(owner_def.to_def_id()) => { - let coroutine_ty = self.typeck_results.node_type(owner_id); - let coroutine_param = Param { - ty: coroutine_ty, - pat: None, - ty_span: None, - self_kind: None, - hir_id: None, - }; - Some(coroutine_param) + fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option<Param<'tcx>> { + if self.tcx.def_kind(owner_def) != DefKind::Closure { + return None; + } + + let closure_ty = self.typeck_results.node_type(expr_id); + Some(match *closure_ty.kind() { + ty::Coroutine(..) => { + Param { ty: closure_ty, pat: None, ty_span: None, self_kind: None, hir_id: None } } - DefKind::Closure => { - let closure_ty = self.typeck_results.node_type(owner_id); - - let ty::Closure(closure_def_id, closure_args) = *closure_ty.kind() else { - bug!("closure expr does not have closure type: {:?}", closure_ty); - }; - - let bound_vars = - self.tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Region(ty::BrEnv)]); - let br = ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind: ty::BrEnv, - }; - let env_region = ty::Region::new_bound(self.tcx, ty::INNERMOST, br); - let closure_env_ty = - self.tcx.closure_env_ty(closure_def_id, closure_args, env_region).unwrap(); - let liberated_closure_env_ty = self.tcx.instantiate_bound_regions_with_erased( - ty::Binder::bind_with_vars(closure_env_ty, bound_vars), + ty::Closure(_, closure_args) => { + let closure_env_ty = self.tcx.closure_env_ty( + closure_ty, + closure_args.as_closure().kind(), + self.tcx.lifetimes.re_erased, ); - let env_param = Param { - ty: liberated_closure_env_ty, + Param { + ty: closure_env_ty, pat: None, ty_span: None, self_kind: None, hir_id: None, - }; - - Some(env_param) + } } - _ => None, - } + _ => bug!("unexpected closure type: {closure_ty}"), + }) } fn explicit_params<'a>( diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index f6c5e4a5cd6..693dc49c6e8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } else { // Check the pattern for some things unrelated to exhaustiveness. let refutable = if cx.refutable { Refutable } else { Irrefutable }; + let mut err = Ok(()); pat.walk_always(|pat| { check_borrow_conflicts_in_at_patterns(self, pat); check_for_bindings_named_same_as_variants(self, pat, refutable); + err = err.and(check_never_pattern(cx, pat)); }); + err?; Ok(cx.pattern_arena.alloc(cx.lower_pat(pat))) } } @@ -289,7 +292,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool { use ExprKind::*; match &scrutinee.kind { - // Both pointers and references can validly point to a place with invalid data. + // Pointers can validly point to a place with invalid data. It is undecided whether + // references can too, so we conservatively assume they can. Deref { .. } => false, // Inherit validity of the parent place, unless the parent is an union. Field { lhs, .. } => { @@ -811,6 +815,19 @@ fn check_for_bindings_named_same_as_variants( } } +/// Check that never patterns are only used on inhabited types. +fn check_never_pattern<'tcx>( + cx: &MatchCheckCtxt<'_, 'tcx>, + pat: &Pat<'tcx>, +) -> Result<(), ErrorGuaranteed> { + if let PatKind::Never = pat.kind { + if !cx.is_uninhabited(pat.ty) { + return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty })); + } + } + Ok(()) +} + fn report_irrefutable_let_patterns( tcx: TyCtxt<'_>, id: HirId, @@ -1113,7 +1130,7 @@ fn collect_non_exhaustive_tys<'tcx>( non_exhaustive_tys.insert(pat.ty().inner()); } if let Constructor::IntRange(range) = pat.ctor() { - if cx.is_range_beyond_boundaries(range, pat.ty()) { + if cx.is_range_beyond_boundaries(range, *pat.ty()) { // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`. non_exhaustive_tys.insert(pat.ty().inner()); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index c4987634b78..ff7e985bdfd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -345,6 +345,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + + hir::PatKind::Err(guar) => PatKind::Error(guar), }; Box::new(Pat { span, ty, kind }) diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index c9930565186..862876f53c7 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -7,6 +7,8 @@ use rustc_middle::traits::Reveal; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::source_map::Spanned; +use rustc_span::DUMMY_SP; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use std::{fmt, iter}; @@ -652,7 +654,10 @@ where [ty.into()], self.source_info.span, ), - args: vec![Operand::Move(Place::from(ref_place))], + args: vec![Spanned { + node: Operand::Move(Place::from(ref_place)), + span: DUMMY_SP, + }], destination: unit_temp, target: Some(succ), unwind: unwind.into_action(), diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index f0b21fd4184..b805f8ca23e 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -34,6 +34,7 @@ mod errors; mod framework; pub mod impls; pub mod move_paths; +pub mod points; pub mod rustc_peek; pub mod storage; pub mod un_derefer; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index cae35765308..38ee26c5a87 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -469,7 +469,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { } => { self.gather_operand(func); for arg in args { - self.gather_operand(arg); + self.gather_operand(&arg.node); } if let Some(_bb) = target { self.create_move_path(destination); diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs new file mode 100644 index 00000000000..ff17ce1fe07 --- /dev/null +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -0,0 +1,156 @@ +use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor}; +use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::interval::SparseIntervalMatrix; +use rustc_index::Idx; +use rustc_index::IndexVec; +use rustc_middle::mir::{self, BasicBlock, Body, Location}; + +/// Maps between a `Location` and a `PointIndex` (and vice versa). +pub struct DenseLocationMap { + /// For each basic block, how many points are contained within? + statements_before_block: IndexVec<BasicBlock, usize>, + + /// Map backward from each point to the basic block that it + /// belongs to. + basic_blocks: IndexVec<PointIndex, BasicBlock>, + + num_points: usize, +} + +impl DenseLocationMap { + #[inline] + pub fn new(body: &Body<'_>) -> Self { + let mut num_points = 0; + let statements_before_block: IndexVec<BasicBlock, usize> = body + .basic_blocks + .iter() + .map(|block_data| { + let v = num_points; + num_points += block_data.statements.len() + 1; + v + }) + .collect(); + + let mut basic_blocks = IndexVec::with_capacity(num_points); + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { + basic_blocks.extend((0..=bb_data.statements.len()).map(|_| bb)); + } + + Self { statements_before_block, basic_blocks, num_points } + } + + /// Total number of point indices + #[inline] + pub fn num_points(&self) -> usize { + self.num_points + } + + /// Converts a `Location` into a `PointIndex`. O(1). + #[inline] + pub fn point_from_location(&self, location: Location) -> PointIndex { + let Location { block, statement_index } = location; + let start_index = self.statements_before_block[block]; + PointIndex::new(start_index + statement_index) + } + + /// Returns the `PointIndex` for the first statement in the given `BasicBlock`. O(1). + #[inline] + pub fn entry_point(&self, block: BasicBlock) -> PointIndex { + let start_index = self.statements_before_block[block]; + PointIndex::new(start_index) + } + + /// Return the PointIndex for the block start of this index. + #[inline] + pub fn to_block_start(&self, index: PointIndex) -> PointIndex { + PointIndex::new(self.statements_before_block[self.basic_blocks[index]]) + } + + /// Converts a `PointIndex` back to a location. O(1). + #[inline] + pub fn to_location(&self, index: PointIndex) -> Location { + assert!(index.index() < self.num_points); + let block = self.basic_blocks[index]; + let start_index = self.statements_before_block[block]; + let statement_index = index.index() - start_index; + Location { block, statement_index } + } + + /// Sometimes we get point-indices back from bitsets that may be + /// out of range (because they round up to the nearest 2^N number + /// of bits). Use this function to filter such points out if you + /// like. + #[inline] + pub fn point_in_range(&self, index: PointIndex) -> bool { + index.index() < self.num_points + } +} + +rustc_index::newtype_index! { + /// A single integer representing a `Location` in the MIR control-flow + /// graph. Constructed efficiently from `DenseLocationMap`. + #[orderable] + #[debug_format = "PointIndex({})"] + pub struct PointIndex {} +} + +/// Add points depending on the result of the given dataflow analysis. +pub fn save_as_intervals<'tcx, N, R>( + elements: &DenseLocationMap, + body: &mir::Body<'tcx>, + mut results: R, +) -> SparseIntervalMatrix<N, PointIndex> +where + N: Idx, + R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet<N>>, +{ + let values = SparseIntervalMatrix::new(elements.num_points()); + let mut visitor = Visitor { elements, values }; + visit_results( + body, + body.basic_blocks.reverse_postorder().iter().copied(), + &mut results, + &mut visitor, + ); + visitor.values +} + +struct Visitor<'a, N: Idx> { + elements: &'a DenseLocationMap, + values: SparseIntervalMatrix<N, PointIndex>, +} + +impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<'_, N> +where + N: Idx, +{ + type FlowState = ChunkedBitSet<N>; + + fn visit_statement_after_primary_effect( + &mut self, + _results: &mut R, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + location: Location, + ) { + let point = self.elements.point_from_location(location); + // Use internal iterator manually as it is much more efficient. + state.iter().for_each(|node| { + self.values.insert(node, point); + }); + } + + fn visit_terminator_after_primary_effect( + &mut self, + _results: &mut R, + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + location: Location, + ) { + let point = self.elements.point_from_location(location); + // Use internal iterator manually as it is much more efficient. + state.iter().for_each(|node| { + self.values.insert(node, point); + }); + } +} diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 5373876d1c2..08a5d70fb6f 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -209,7 +209,7 @@ impl PeekCall { assert_eq!(fn_args.len(), 1); let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0)); - let arg = match &args[0] { + let arg = match &args[0].node { Operand::Copy(place) | Operand::Move(place) => { if let Some(local) = place.as_local() { local diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 379e96e263c..eaa36e0cc91 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -675,9 +675,9 @@ fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local let terminator = bb_data.terminator.take().unwrap(); if let TerminatorKind::Call { mut args, destination, target, .. } = terminator.kind { let arg = args.pop().unwrap(); - let local = arg.place().unwrap().local; + let local = arg.node.place().unwrap().local; - let arg = Rvalue::Use(arg); + let arg = Rvalue::Use(arg.node); let assign = Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new((destination, arg))), @@ -1865,7 +1865,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { self.check_assigned_place(*destination, |this| { this.visit_operand(func, location); for arg in args { - this.visit_operand(arg, location); + this.visit_operand(&arg.node, location); } }); } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 263bfdaaaba..c6badbe78a4 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -1,9 +1,10 @@ use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_index::bit_set::BitSet; -use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; +use rustc_index::IndexVec; +use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; use std::cmp::Ordering; use std::collections::VecDeque; @@ -30,23 +31,16 @@ impl CoverageGraph { // `SwitchInt` to have multiple targets to the same destination `BasicBlock`, so // de-duplication is required. This is done without reordering the successors. - let mut seen = IndexVec::from_elem(false, &bcbs); let successors = IndexVec::from_fn_n( |bcb| { - for b in seen.iter_mut() { - *b = false; - } - let bcb_data = &bcbs[bcb]; - let mut bcb_successors = Vec::new(); - for successor in bcb_filtered_successors(mir_body, bcb_data.last_bb()) + let mut seen_bcbs = FxHashSet::default(); + let terminator = mir_body[bcbs[bcb].last_bb()].terminator(); + bcb_filtered_successors(terminator) + .into_iter() .filter_map(|successor_bb| bb_to_bcb[successor_bb]) - { - if !seen[successor] { - seen[successor] = true; - bcb_successors.push(successor); - } - } - bcb_successors + // Remove duplicate successor BCBs, keeping only the first. + .filter(|&successor_bcb| seen_bcbs.insert(successor_bcb)) + .collect::<Vec<_>>() }, bcbs.len(), ); @@ -80,9 +74,23 @@ impl CoverageGraph { IndexVec<BasicBlock, Option<BasicCoverageBlock>>, ) { let num_basic_blocks = mir_body.basic_blocks.len(); - let mut bcbs = IndexVec::with_capacity(num_basic_blocks); + let mut bcbs = IndexVec::<BasicCoverageBlock, _>::with_capacity(num_basic_blocks); let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); + let mut add_basic_coverage_block = |basic_blocks: &mut Vec<BasicBlock>| { + // Take the accumulated list of blocks, leaving the vector empty + // to be used by subsequent BCBs. + let basic_blocks = std::mem::take(basic_blocks); + + let bcb = bcbs.next_index(); + for &bb in basic_blocks.iter() { + bb_to_bcb[bb] = Some(bcb); + } + let bcb_data = BasicCoverageBlockData::from(basic_blocks); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + bcbs.push(bcb_data); + }; + // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows // each block terminator's `successors()`. Coverage spans must map to actual source code, // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal @@ -90,102 +98,42 @@ impl CoverageGraph { // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and // `catch_unwind()` handlers. + // Accumulates a chain of blocks that will be combined into one BCB. let mut basic_blocks = Vec::new(); - for bb in short_circuit_preorder(mir_body, bcb_filtered_successors) { - if let Some(last) = basic_blocks.last() { - let predecessors = &mir_body.basic_blocks.predecessors()[bb]; - if predecessors.len() > 1 || !predecessors.contains(last) { - // The `bb` has more than one _incoming_ edge, and should start its own - // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet - // include `bb`; it contains a sequence of one or more sequential basic_blocks - // with no intermediate branches in or out. Save these as a new - // `BasicCoverageBlockData` before starting the new one.) - Self::add_basic_coverage_block( - &mut bcbs, - &mut bb_to_bcb, - basic_blocks.split_off(0), - ); - debug!( - " because {}", - if predecessors.len() > 1 { - "predecessors.len() > 1".to_owned() - } else { - format!("bb {} is not in predecessors: {:?}", bb.index(), predecessors) - } - ); - } - } - basic_blocks.push(bb); - let term = mir_body[bb].terminator(); - - match term.kind { - TerminatorKind::Return { .. } - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Yield { .. } - | TerminatorKind::SwitchInt { .. } => { - // The `bb` has more than one _outgoing_ edge, or exits the function. Save the - // current sequence of `basic_blocks` gathered to this point, as a new - // `BasicCoverageBlockData`. - Self::add_basic_coverage_block( - &mut bcbs, - &mut bb_to_bcb, - basic_blocks.split_off(0), - ); - debug!(" because term.kind = {:?}", term.kind); - // Note that this condition is based on `TerminatorKind`, even though it - // theoretically boils down to `successors().len() != 1`; that is, either zero - // (e.g., `Return`, `Terminate`) or multiple successors (e.g., `SwitchInt`), but - // since the BCB CFG ignores things like unwind branches (which exist in the - // `Terminator`s `successors()` list) checking the number of successors won't - // work. - } - - // The following `TerminatorKind`s are either not expected outside an unwind branch, - // or they should not (under normal circumstances) branch. Coverage graphs are - // simplified by assuring coverage results are accurate for program executions that - // don't panic. - // - // Programs that panic and unwind may record slightly inaccurate coverage results - // for a coverage region containing the `Terminator` that began the panic. This - // is as intended. (See Issue #78544 for a possible future option to support - // coverage in test programs that panic.) - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::Assert { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} + let filtered_successors = |bb| bcb_filtered_successors(mir_body[bb].terminator()); + for bb in short_circuit_preorder(mir_body, filtered_successors) + .filter(|&bb| mir_body[bb].terminator().kind != TerminatorKind::Unreachable) + { + // If the previous block can't be chained into `bb`, flush the accumulated + // blocks into a new BCB, then start building the next chain. + if let Some(&prev) = basic_blocks.last() + && (!filtered_successors(prev).is_chainable() || { + // If `bb` has multiple predecessor blocks, or `prev` isn't + // one of its predecessors, we can't chain and must flush. + let predecessors = &mir_body.basic_blocks.predecessors()[bb]; + predecessors.len() > 1 || !predecessors.contains(&prev) + }) + { + debug!( + terminator_kind = ?mir_body[prev].terminator().kind, + predecessors = ?&mir_body.basic_blocks.predecessors()[bb], + "can't chain from {prev:?} to {bb:?}" + ); + add_basic_coverage_block(&mut basic_blocks); } + + basic_blocks.push(bb); } if !basic_blocks.is_empty() { - // process any remaining basic_blocks into a final `BasicCoverageBlockData` - Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); - debug!(" because the end of the MIR CFG was reached while traversing"); + debug!("flushing accumulated blocks into one last BCB"); + add_basic_coverage_block(&mut basic_blocks); } (bcbs, bb_to_bcb) } - fn add_basic_coverage_block( - bcbs: &mut IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, - bb_to_bcb: &mut IndexSlice<BasicBlock, Option<BasicCoverageBlock>>, - basic_blocks: Vec<BasicBlock>, - ) { - let bcb = bcbs.next_index(); - for &bb in basic_blocks.iter() { - bb_to_bcb[bb] = Some(bcb); - } - let bcb_data = BasicCoverageBlockData::from(basic_blocks); - debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); - bcbs.push(bcb_data); - } - #[inline(always)] pub fn iter_enumerated( &self, @@ -346,28 +294,76 @@ impl BasicCoverageBlockData { } } +/// Holds the coverage-relevant successors of a basic block's terminator, and +/// indicates whether that block can potentially be combined into the same BCB +/// as its sole successor. +#[derive(Clone, Copy, Debug)] +enum CoverageSuccessors<'a> { + /// The terminator has exactly one straight-line successor, so its block can + /// potentially be combined into the same BCB as that successor. + Chainable(BasicBlock), + /// The block cannot be combined into the same BCB as its successor(s). + NotChainable(&'a [BasicBlock]), +} + +impl CoverageSuccessors<'_> { + fn is_chainable(&self) -> bool { + match self { + Self::Chainable(_) => true, + Self::NotChainable(_) => false, + } + } +} + +impl IntoIterator for CoverageSuccessors<'_> { + type Item = BasicBlock; + type IntoIter = impl DoubleEndedIterator<Item = Self::Item>; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Chainable(bb) => Some(bb).into_iter().chain((&[]).iter().copied()), + Self::NotChainable(bbs) => None.into_iter().chain(bbs.iter().copied()), + } + } +} + // Returns the subset of a block's successors that are relevant to the coverage -// graph, i.e. those that do not represent unwinds or unreachable branches. +// graph, i.e. those that do not represent unwinds or false edges. // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and // `catch_unwind()` handlers. -fn bcb_filtered_successors<'a, 'tcx>( - body: &'a mir::Body<'tcx>, - bb: BasicBlock, -) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx> { - let terminator = body[bb].terminator(); - - let take_n_successors = match terminator.kind { - // SwitchInt successors are never unwinds, so all of them should be traversed. - TerminatorKind::SwitchInt { .. } => usize::MAX, - // For all other kinds, return only the first successor (if any), ignoring any - // unwind successors. - _ => 1, - }; - - terminator - .successors() - .take(take_n_successors) - .filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) +fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> CoverageSuccessors<'a> { + use TerminatorKind::*; + match terminator.kind { + // A switch terminator can have many coverage-relevant successors. + // (If there is exactly one successor, we still treat it as not chainable.) + SwitchInt { ref targets, .. } => CoverageSuccessors::NotChainable(targets.all_targets()), + + // A yield terminator has exactly 1 successor, but should not be chained, + // because its resume edge has a different execution count. + Yield { ref resume, .. } => CoverageSuccessors::NotChainable(std::slice::from_ref(resume)), + + // These terminators have exactly one coverage-relevant successor, + // and can be chained into it. + Assert { target, .. } + | Drop { target, .. } + | FalseEdge { real_target: target, .. } + | FalseUnwind { real_target: target, .. } + | Goto { target } => CoverageSuccessors::Chainable(target), + + // These terminators can normally be chained, except when they have no + // successor because they are known to diverge. + Call { target: maybe_target, .. } | InlineAsm { destination: maybe_target, .. } => { + match maybe_target { + Some(target) => CoverageSuccessors::Chainable(target), + None => CoverageSuccessors::NotChainable(&[]), + } + } + + // These terminators have no coverage-relevant successors. + CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => { + CoverageSuccessors::NotChainable(&[]) + } + } } /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the @@ -544,8 +540,8 @@ fn short_circuit_preorder<'a, 'tcx, F, Iter>( filtered_successors: F, ) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx> where - F: Fn(&'a mir::Body<'tcx>, BasicBlock) -> Iter, - Iter: Iterator<Item = BasicBlock>, + F: Fn(BasicBlock) -> Iter, + Iter: IntoIterator<Item = BasicBlock>, { let mut visited = BitSet::new_empty(body.basic_blocks.len()); let mut worklist = vec![mir::START_BLOCK]; @@ -556,7 +552,7 @@ where continue; } - worklist.extend(filtered_successors(body, bb)); + worklist.extend(filtered_successors(bb)); return Some(bb); } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 3d74ef7e327..e6317e5469c 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -52,7 +52,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { live.seek_to_block_end(bb); let mut state = live.get().clone(); - for (index, arg) in args.iter().enumerate().rev() { + for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() { if let Operand::Copy(place) = *arg && !place.is_indirect() // Do not skip the transformation if the local is in debuginfo, as we do @@ -119,17 +119,25 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { bug!() }; - let arg = &mut args[argument_index]; + let arg = &mut args[argument_index].node; let Operand::Copy(place) = *arg else { bug!() }; *arg = Operand::Move(place); } - - crate::simplify::simplify_locals(body, tcx) } -pub struct DeadStoreElimination; +pub enum DeadStoreElimination { + Initial, + Final, +} impl<'tcx> MirPass<'tcx> for DeadStoreElimination { + fn name(&self) -> &'static str { + match self { + DeadStoreElimination::Initial => "DeadStoreElimination-initial", + DeadStoreElimination::Final => "DeadStoreElimination-final", + } + } + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() >= 2 } diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 990cfb05e60..a6750911394 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -111,7 +111,7 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { if let TerminatorKind::Call { ref args, .. } = terminator.kind { for arg in args { - if let Operand::Move(place) = *arg { + if let Operand::Move(place) = arg.node { let local = place.local; if place.is_indirect() || local == RETURN_PLACE diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 49b0edc0db8..0ac4ab61d40 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -134,6 +134,7 @@ use crate::MirPass; use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry}; use rustc_index::bit_set::BitSet; +use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::HasLocalDecls; use rustc_middle::mir::{dump_mir, PassWhere}; @@ -143,7 +144,8 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::MaybeLiveLocals; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_mir_dataflow::points::{save_as_intervals, DenseLocationMap, PointIndex}; +use rustc_mir_dataflow::Analysis; pub struct DestinationPropagation; @@ -167,6 +169,13 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); + let live = MaybeLiveLocals + .into_engine(tcx, body) + .pass_name("MaybeLiveLocals-DestinationPropagation") + .iterate_to_fixpoint(); + let points = DenseLocationMap::new(body); + let mut live = save_as_intervals(&points, body, live); + // In order to avoid having to collect data for every single pair of locals in the body, we // do not allow doing more than one merge for places that are derived from the same local at // once. To avoid missed opportunities, we instead iterate to a fixed point - we'll refer to @@ -190,22 +199,19 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { &mut allocations.candidates_reverse, ); trace!(?candidates); - let mut live = MaybeLiveLocals - .into_engine(tcx, body) - .iterate_to_fixpoint() - .into_results_cursor(body); - dest_prop_mir_dump(tcx, body, &mut live, round_count); + dest_prop_mir_dump(tcx, body, &points, &live, round_count); FilterInformation::filter_liveness( &mut candidates, - &mut live, + &points, + &live, &mut allocations.write_info, body, ); - // Because we do not update liveness information, it is unsound to use a local for more - // than one merge operation within a single round of optimizations. We store here which - // ones we have already used. + // Because we only filter once per round, it is unsound to use a local for more than + // one merge operation within a single round of optimizations. We store here which ones + // we have already used. let mut merged_locals: BitSet<Local> = BitSet::new_empty(body.local_decls.len()); // This is the set of merges we will apply this round. It is a subset of the candidates. @@ -224,9 +230,15 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { }) { break; } + + // Replace `src` by `dest` everywhere. merges.insert(*src, *dest); merged_locals.insert(*src); merged_locals.insert(*dest); + + // Update liveness information based on the merge we just performed. + // Every location where `src` was live, `dest` will be live. + live.union_rows(*src, *dest); } trace!(merging = ?merges); @@ -349,7 +361,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> { struct FilterInformation<'a, 'body, 'alloc, 'tcx> { body: &'body Body<'tcx>, - live: &'a mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>, + points: &'a DenseLocationMap, + live: &'a SparseIntervalMatrix<Local, PointIndex>, candidates: &'a mut Candidates<'alloc>, write_info: &'alloc mut WriteInfo, at: Location, @@ -452,12 +465,14 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { /// locals as also being read from. fn filter_liveness<'b>( candidates: &mut Candidates<'alloc>, - live: &mut ResultsCursor<'b, 'tcx, MaybeLiveLocals>, + points: &DenseLocationMap, + live: &SparseIntervalMatrix<Local, PointIndex>, write_info_alloc: &'alloc mut WriteInfo, body: &'b Body<'tcx>, ) { let mut this = FilterInformation { body, + points, live, candidates, // We don't actually store anything at this scope, we just keep things here to be able @@ -472,13 +487,11 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { fn internal_filter_liveness(&mut self) { for (block, data) in traversal::preorder(self.body) { self.at = Location { block, statement_index: data.statements.len() }; - self.live.seek_after_primary_effect(self.at); self.write_info.for_terminator(&data.terminator().kind); self.apply_conflicts(); for (i, statement) in data.statements.iter().enumerate().rev() { self.at = Location { block, statement_index: i }; - self.live.seek_after_primary_effect(self.at); self.write_info.for_statement(&statement.kind, self.body); self.apply_conflicts(); } @@ -497,6 +510,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { None } }); + let at = self.points.point_from_location(self.at); self.candidates.filter_candidates_by( *p, |q| { @@ -508,7 +522,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { // calls or inline asm. Because of this, we also mark locals as // conflicting when both of them are written to in the same // statement. - if self.live.contains(q) || writes.contains(&q) { + if self.live.contains(q, at) || writes.contains(&q) { CandidateFilter::Remove } else { CandidateFilter::Keep @@ -609,7 +623,7 @@ impl WriteInfo { self.add_place(*destination); self.add_operand(func); for arg in args { - self.add_operand(arg); + self.add_operand(&arg.node); } } TerminatorKind::InlineAsm { operands, .. } => { @@ -801,38 +815,17 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool { fn dest_prop_mir_dump<'body, 'tcx>( tcx: TyCtxt<'tcx>, body: &'body Body<'tcx>, - live: &mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>, + points: &DenseLocationMap, + live: &SparseIntervalMatrix<Local, PointIndex>, round: usize, ) { - let mut reachable = None; + let locals_live_at = |location| { + let location = points.point_from_location(location); + live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>() + }; dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| { - let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); - - match pass_where { - PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { - live.seek_after_primary_effect(loc); - writeln!(w, " // live: {:?}", live.get())?; - } - PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { - let loc = body.terminator_loc(bb); - live.seek_before_primary_effect(loc); - writeln!(w, " // live: {:?}", live.get())?; - } - - PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { - live.seek_to_block_start(bb); - writeln!(w, " // live: {:?}", live.get())?; - } - - PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} - - PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { - writeln!(w, " // live: <unreachable>")?; - } - - PassWhere::BeforeBlock(_) => { - writeln!(w, " // live: <unreachable>")?; - } + if let PassWhere::BeforeLocation(loc) = pass_where { + writeln!(w, " // live: {:?}", locals_live_at(loc))?; } Ok(()) diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 446f13feff0..2ee660ddc9b 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -66,7 +66,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for RequiresUnsafe { #[track_caller] fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { let mut diag = DiagnosticBuilder::new(dcx, level, fluent::mir_transform_requires_unsafe); - diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string())); + diag.code("E0133".to_string()); diag.span(self.span); diag.span_label(self.span, self.details.label()); let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index 340bb1948eb..61d99f1f018 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; +use rustc_span::source_map::Spanned; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; @@ -43,7 +44,7 @@ impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> { if let ty::FnDef(def_id, args_ref) = *func_ty.kind() { // Handle calls to `transmute` if self.tcx.is_diagnostic_item(sym::transmute, def_id) { - let arg_ty = args[0].ty(self.body, self.tcx); + let arg_ty = args[0].node.ty(self.body, self.tcx); for inner_ty in arg_ty.walk().filter_map(|arg| arg.as_type()) { if let Some((fn_id, fn_args)) = FunctionItemRefChecker::is_fn_ref(inner_ty) { @@ -67,7 +68,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { &self, def_id: DefId, args_ref: GenericArgsRef<'tcx>, - args: &[Operand<'tcx>], + args: &[Spanned<Operand<'tcx>>], source_info: SourceInfo, ) { let param_env = self.tcx.param_env(def_id); @@ -134,8 +135,8 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { .unwrap_or(None) } - fn nth_arg_span(&self, args: &[Operand<'tcx>], n: usize) -> Span { - match &args[n] { + fn nth_arg_span(&self, args: &[Spanned<Operand<'tcx>>], n: usize) -> Span { + match &args[n].node { Operand::Copy(place) | Operand::Move(place) => { self.body.local_decls[place.local].source_info.span } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 2551c8aca88..390ec3e1a36 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -84,7 +84,7 @@ use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; @@ -99,6 +99,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut}; use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; +use smallvec::SmallVec; use std::borrow::Cow; use crate::dataflow_const_prop::DummyMachine; @@ -238,14 +239,18 @@ struct VnState<'body, 'tcx> { local_decls: &'body LocalDecls<'tcx>, /// Value stored in each local. locals: IndexVec<Local, Option<VnIndex>>, - /// First local to be assigned that value. - rev_locals: FxHashMap<VnIndex, Vec<Local>>, + /// Locals that are assigned that value. + // This vector does not hold all the values of `VnIndex` that we create. + // It stops at the largest value created in the first phase of collecting assignments. + rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>, values: FxIndexSet<Value<'tcx>>, /// Values evaluated as constants if possible. evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>, /// Counter to generate different values. /// This is an option to stop creating opaques during replacement. next_opaque: Option<usize>, + /// Cache the value of the `unsized_locals` features, to avoid fetching it repeatedly in a loop. + feature_unsized_locals: bool, ssa: &'body SsaLocals, dominators: &'body Dominators<BasicBlock>, reused_locals: BitSet<Local>, @@ -265,10 +270,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { param_env, local_decls, locals: IndexVec::from_elem(None, local_decls), - rev_locals: FxHashMap::default(), + rev_locals: IndexVec::default(), values: FxIndexSet::default(), evaluated: IndexVec::new(), next_opaque: Some(0), + feature_unsized_locals: tcx.features().unsized_locals, ssa, dominators, reused_locals: BitSet::new_empty(local_decls.len()), @@ -316,10 +322,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.locals[local] = Some(value); // Only register the value if its type is `Sized`, as we will emit copies of it. - let is_sized = !self.tcx.features().unsized_locals + let is_sized = !self.feature_unsized_locals || self.local_decls[local].ty.is_sized(self.tcx, self.param_env); if is_sized { - self.rev_locals.entry(value).or_default().push(local); + self.rev_locals.ensure_contains_elem(value, SmallVec::new); + self.rev_locals[value].push(local); } } @@ -338,11 +345,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Some(self.insert(Value::Constant { value, disambiguator })) } + fn insert_bool(&mut self, flag: bool) -> VnIndex { + // Booleans are deterministic. + self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 }) + } + fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex { self.insert_constant(Const::from_scalar(self.tcx, scalar, ty)) .expect("scalars are deterministic") } + fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex { + self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values)) + } + #[instrument(level = "trace", skip(self), ret)] fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> { use Value::*; @@ -630,6 +646,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if place.is_indirect() && let Some(base) = self.locals[place.local] && let Some(new_local) = self.try_as_local(base, location) + && place.local != new_local { place.local = new_local; self.reused_locals.insert(new_local); @@ -639,8 +656,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { for i in 0..projection.len() { let elem = projection[i]; - if let ProjectionElem::Index(idx) = elem - && let Some(idx) = self.locals[idx] + if let ProjectionElem::Index(idx_local) = elem + && let Some(idx) = self.locals[idx_local] { if let Some(offset) = self.evaluated[idx].as_ref() && let Ok(offset) = self.ecx.read_target_usize(offset) @@ -648,9 +665,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { projection.to_mut()[i] = ProjectionElem::ConstantIndex { offset, min_length, from_end: false }; - } else if let Some(new_idx) = self.try_as_local(idx, location) { - projection.to_mut()[i] = ProjectionElem::Index(new_idx); - self.reused_locals.insert(new_idx); + } else if let Some(new_idx_local) = self.try_as_local(idx, location) + && idx_local != new_idx_local + { + projection.to_mut()[i] = ProjectionElem::Index(new_idx_local); + self.reused_locals.insert(new_idx_local); } } } @@ -757,10 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Operations. - Rvalue::Len(ref mut place) => { - let place = self.simplify_place_value(place, location)?; - Value::Len(place) - } + Rvalue::Len(ref mut place) => return self.simplify_len(place, location), Rvalue::Cast(kind, ref mut value, to) => { let from = value.ty(self.local_decls, self.tcx); let value = self.simplify_operand(value, location)?; @@ -775,17 +791,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Cast { kind, value, from, to } } Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => { + let ty = lhs.ty(self.local_decls, self.tcx); let lhs = self.simplify_operand(lhs, location); let rhs = self.simplify_operand(rhs, location); - Value::BinaryOp(op, lhs?, rhs?) + // Only short-circuit options after we called `simplify_operand` + // on both operands for side effect. + let lhs = lhs?; + let rhs = rhs?; + if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) { + return Some(value); + } + Value::BinaryOp(op, lhs, rhs) } Rvalue::CheckedBinaryOp(op, box (ref mut lhs, ref mut rhs)) => { + let ty = lhs.ty(self.local_decls, self.tcx); let lhs = self.simplify_operand(lhs, location); let rhs = self.simplify_operand(rhs, location); - Value::CheckedBinaryOp(op, lhs?, rhs?) + // Only short-circuit options after we called `simplify_operand` + // on both operands for side effect. + let lhs = lhs?; + let rhs = rhs?; + if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) { + return Some(value); + } + Value::CheckedBinaryOp(op, lhs, rhs) } Rvalue::UnaryOp(op, ref mut arg) => { let arg = self.simplify_operand(arg, location)?; + if let Some(value) = self.simplify_unary(op, arg) { + return Some(value); + } Value::UnaryOp(op, arg) } Rvalue::Discriminant(ref mut place) => { @@ -884,6 +919,150 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Some(self.insert(Value::Aggregate(ty, variant_index, fields))) } + + #[instrument(level = "trace", skip(self), ret)] + fn simplify_unary(&mut self, op: UnOp, value: VnIndex) -> Option<VnIndex> { + let value = match (op, self.get(value)) { + (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner), + (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner), + (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => { + Value::BinaryOp(BinOp::Ne, *lhs, *rhs) + } + (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { + Value::BinaryOp(BinOp::Eq, *lhs, *rhs) + } + _ => return None, + }; + + Some(self.insert(value)) + } + + #[instrument(level = "trace", skip(self), ret)] + fn simplify_binary( + &mut self, + op: BinOp, + checked: bool, + lhs_ty: Ty<'tcx>, + lhs: VnIndex, + rhs: VnIndex, + ) -> Option<VnIndex> { + // Floats are weird enough that none of the logic below applies. + let reasonable_ty = + lhs_ty.is_integral() || lhs_ty.is_bool() || lhs_ty.is_char() || lhs_ty.is_any_ptr(); + if !reasonable_ty { + return None; + } + + let layout = self.ecx.layout_of(lhs_ty).ok()?; + + let as_bits = |value| { + let constant = self.evaluated[value].as_ref()?; + if layout.abi.is_scalar() { + let scalar = self.ecx.read_scalar(constant).ok()?; + scalar.to_bits(constant.layout.size).ok() + } else { + // `constant` is a wide pointer. Do not evaluate to bits. + None + } + }; + + // Represent the values as `Left(bits)` or `Right(VnIndex)`. + use Either::{Left, Right}; + let a = as_bits(lhs).map_or(Right(lhs), Left); + let b = as_bits(rhs).map_or(Right(rhs), Left); + let result = match (op, a, b) { + // Neutral elements. + (BinOp::Add | BinOp::BitOr | BinOp::BitXor, Left(0), Right(p)) + | ( + BinOp::Add + | BinOp::BitOr + | BinOp::BitXor + | BinOp::Sub + | BinOp::Offset + | BinOp::Shl + | BinOp::Shr, + Right(p), + Left(0), + ) + | (BinOp::Mul, Left(1), Right(p)) + | (BinOp::Mul | BinOp::Div, Right(p), Left(1)) => p, + // Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size. + (BinOp::BitAnd, Right(p), Left(ones)) | (BinOp::BitAnd, Left(ones), Right(p)) + if ones == layout.size.truncate(u128::MAX) + || (layout.ty.is_bool() && ones == 1) => + { + p + } + // Absorbing elements. + (BinOp::Mul | BinOp::BitAnd, _, Left(0)) + | (BinOp::Rem, _, Left(1)) + | ( + BinOp::Mul | BinOp::Div | BinOp::Rem | BinOp::BitAnd | BinOp::Shl | BinOp::Shr, + Left(0), + _, + ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty), + // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`. + (BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _) + if ones == layout.size.truncate(u128::MAX) + || (layout.ty.is_bool() && ones == 1) => + { + self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty) + } + // Sub/Xor with itself. + (BinOp::Sub | BinOp::BitXor, a, b) if a == b => { + self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty) + } + // Comparison: + // - if both operands can be computed as bits, just compare the bits; + // - if we proved that both operands have the same value, we can insert true/false; + // - otherwise, do nothing, as we do not try to prove inequality. + (BinOp::Eq, Left(a), Left(b)) => self.insert_bool(a == b), + (BinOp::Eq, a, b) if a == b => self.insert_bool(true), + (BinOp::Ne, Left(a), Left(b)) => self.insert_bool(a != b), + (BinOp::Ne, a, b) if a == b => self.insert_bool(false), + _ => return None, + }; + + if checked { + let false_val = self.insert_bool(false); + Some(self.insert_tuple(vec![result, false_val])) + } else { + Some(result) + } + } + + fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> { + // Trivial case: we are fetching a statically known length. + let place_ty = place.ty(self.local_decls, self.tcx).ty; + if let ty::Array(_, len) = place_ty.kind() { + return self.insert_constant(Const::from_ty_const(*len, self.tcx)); + } + + let mut inner = self.simplify_place_value(place, location)?; + + // The length information is stored in the fat pointer. + // Reborrowing copies length information from one pointer to the other. + while let Value::Address { place: borrowed, .. } = self.get(inner) + && let [PlaceElem::Deref] = borrowed.projection[..] + && let Some(borrowed) = self.locals[borrowed.local] + { + inner = borrowed; + } + + // We have an unsizing cast, which assigns the length to fat pointer metadata. + if let Value::Cast { kind, from, to, .. } = self.get(inner) + && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize) = kind + && let Some(from) = from.builtin_deref(true) + && let ty::Array(_, len) = from.ty.kind() + && let Some(to) = to.builtin_deref(true) + && let ty::Slice(..) = to.ty.kind() + { + return self.insert_constant(Const::from_ty_const(*len, self.tcx)); + } + + // Fallback: a symbolic `Len`. + Some(self.insert(Value::Len(inner))) + } } fn op_to_prop_const<'tcx>( @@ -986,11 +1165,11 @@ impl<'tcx> VnState<'_, 'tcx> { /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`, /// return it. fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> { - let other = self.rev_locals.get(&index)?; + let other = self.rev_locals.get(index)?; other .iter() + .find(|&&other| self.ssa.assignment_dominates(self.dominators, other, loc)) .copied() - .find(|&other| self.ssa.assignment_dominates(self.dominators, other, loc)) } } @@ -1008,23 +1187,32 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { } fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { - if let StatementKind::Assign(box (_, ref mut rvalue)) = stmt.kind + if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { + self.simplify_place_projection(lhs, location); + // Do not try to simplify a constant, it's already in canonical shape. - && !matches!(rvalue, Rvalue::Use(Operand::Constant(_))) - { - if let Some(value) = self.simplify_rvalue(rvalue, location) { - if let Some(const_) = self.try_as_constant(value) { - *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); - } else if let Some(local) = self.try_as_local(value, location) - && *rvalue != Rvalue::Use(Operand::Move(local.into())) - { - *rvalue = Rvalue::Use(Operand::Copy(local.into())); - self.reused_locals.insert(local); - } + if matches!(rvalue, Rvalue::Use(Operand::Constant(_))) { + return; } - } else { - self.super_statement(stmt, location); + + let value = lhs + .as_local() + .and_then(|local| self.locals[local]) + .or_else(|| self.simplify_rvalue(rvalue, location)); + let Some(value) = value else { return }; + + if let Some(const_) = self.try_as_constant(value) { + *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); + } else if let Some(local) = self.try_as_local(value, location) + && *rvalue != Rvalue::Use(Operand::Move(local.into())) + { + *rvalue = Rvalue::Use(Operand::Copy(local.into())); + self.reused_locals.insert(local); + } + + return; } + self.super_statement(stmt, location); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index ebefc3b47cc..67668a216de 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -11,6 +11,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_session::config::OptLevel; +use rustc_span::source_map::Spanned; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; @@ -172,7 +173,7 @@ impl<'tcx> Inliner<'tcx> { let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; for arg in args { - if !arg.ty(&caller_body.local_decls, self.tcx).is_sized(self.tcx, self.param_env) { + if !arg.node.ty(&caller_body.local_decls, self.tcx).is_sized(self.tcx, self.param_env) { // We do not allow inlining functions with unsized params. Inlining these functions // could create unsized locals, which are unsound and being phased out. return Err("Call has unsized argument"); @@ -238,9 +239,9 @@ impl<'tcx> Inliner<'tcx> { }; let self_arg_ty = - self_arg.map(|self_arg| self_arg.ty(&caller_body.local_decls, self.tcx)); + self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, self.tcx)); - let arg_tuple_ty = arg_tuple.ty(&caller_body.local_decls, self.tcx); + let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, self.tcx); let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { bug!("Closure arguments are not passed as a tuple"); }; @@ -263,7 +264,7 @@ impl<'tcx> Inliner<'tcx> { } else { for (arg, input) in args.iter().zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; - let arg_ty = arg.ty(&caller_body.local_decls, self.tcx); + let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); if !util::relate_types( self.tcx, self.param_env, @@ -694,7 +695,7 @@ impl<'tcx> Inliner<'tcx> { fn make_call_args( &self, - args: Vec<Operand<'tcx>>, + args: Vec<Spanned<Operand<'tcx>>>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, callee_body: &Body<'tcx>, @@ -728,13 +729,13 @@ impl<'tcx> Inliner<'tcx> { if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() { let mut args = args.into_iter(); let self_ = self.create_temp_if_necessary( - args.next().unwrap(), + args.next().unwrap().node, callsite, caller_body, return_block, ); let tuple = self.create_temp_if_necessary( - args.next().unwrap(), + args.next().unwrap().node, callsite, caller_body, return_block, @@ -761,7 +762,7 @@ impl<'tcx> Inliner<'tcx> { closure_ref_arg.chain(tuple_tmp_args).collect() } else { args.into_iter() - .map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block)) + .map(|a| self.create_temp_if_necessary(a.node, callsite, caller_body, return_block)) .collect() } } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 4f0f63d22a4..a28db0defc9 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -215,7 +215,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { // These types are easily available from locals, so check that before // doing DefId lookups to figure out what we're actually calling. - let arg_ty = args[0].ty(self.local_decls, self.tcx); + let arg_ty = args[0].node.ty(self.local_decls, self.tcx); let ty::Ref(_region, inner_ty, Mutability::Not) = *arg_ty.kind() else { return }; @@ -238,7 +238,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { return; } - let Some(arg_place) = args.pop().unwrap().place() else { return }; + let Some(arg_place) = args.pop().unwrap().node.place() else { return }; statements.push(Statement { source_info: terminator.source_info, diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index dcab124505e..e87f68a0905 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -36,16 +36,21 @@ //! cost by `MAX_COST`. use rustc_arena::DroplessArena; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, ScalarInt, TyCtxt}; use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; +use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; use crate::cost_checker::CostChecker; +use crate::dataflow_const_prop::DummyMachine; pub struct JumpThreading; @@ -71,6 +76,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading { let mut finder = TOFinder { tcx, param_env, + ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine), body, arena: &arena, map: &map, @@ -88,7 +94,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading { debug!(?discr, ?bb); let discr_ty = discr.ty(body, tcx).ty; - let Ok(discr_layout) = tcx.layout_of(param_env.and(discr_ty)) else { continue }; + 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); @@ -142,6 +148,7 @@ struct ThreadingOpportunity { struct TOFinder<'tcx, 'a> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + ecx: InterpCx<'tcx, 'tcx, DummyMachine>, body: &'a Body<'tcx>, map: &'a Map, loop_headers: &'a BitSet<BasicBlock>, @@ -329,11 +336,11 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { } #[instrument(level = "trace", skip(self))] - fn process_operand( + fn process_immediate( &mut self, bb: BasicBlock, lhs: PlaceIndex, - rhs: &Operand<'tcx>, + rhs: ImmTy<'tcx>, state: &mut State<ConditionSet<'a>>, ) -> Option<!> { let register_opportunity = |c: Condition| { @@ -341,13 +348,70 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) }; + let conditions = state.try_get_idx(lhs, self.map)?; + if let Immediate::Scalar(Scalar::Int(int)) = *rhs { + conditions.iter_matches(int).for_each(register_opportunity); + } + + None + } + + /// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. + #[instrument(level = "trace", skip(self))] + fn process_constant( + &mut self, + bb: BasicBlock, + lhs: PlaceIndex, + constant: OpTy<'tcx>, + state: &mut State<ConditionSet<'a>>, + ) { + self.map.for_each_projection_value( + lhs, + constant, + &mut |elem, op| match elem { + TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).ok(), + TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).ok(), + TrackElem::Discriminant => { + let variant = self.ecx.read_discriminant(op).ok()?; + let discr_value = + self.ecx.discriminant_for_variant(op.layout.ty, variant).ok()?; + Some(discr_value.into()) + } + TrackElem::DerefLen => { + let op: OpTy<'_> = self.ecx.deref_pointer(op).ok()?.into(); + let len_usize = op.len(&self.ecx).ok()?; + let layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); + Some(ImmTy::from_uint(len_usize, layout).into()) + } + }, + &mut |place, op| { + if let Some(conditions) = state.try_get_idx(place, self.map) + && let Ok(imm) = self.ecx.read_immediate_raw(op) + && let Some(imm) = imm.right() + && let Immediate::Scalar(Scalar::Int(int)) = *imm + { + conditions.iter_matches(int).for_each(|c: Condition| { + self.opportunities + .push(ThreadingOpportunity { chain: vec![bb], target: c.target }) + }) + } + }, + ); + } + + #[instrument(level = "trace", skip(self))] + fn process_operand( + &mut self, + bb: BasicBlock, + lhs: PlaceIndex, + rhs: &Operand<'tcx>, + state: &mut State<ConditionSet<'a>>, + ) -> Option<!> { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let conditions = state.try_get_idx(lhs, self.map)?; - let constant = - constant.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?; - conditions.iter_matches(constant).for_each(register_opportunity); + let constant = self.ecx.eval_mir_constant(&constant.const_, None, None).ok()?; + self.process_constant(bb, lhs, constant, state); } // Transfer the conditions on the copied rhs. Operand::Move(rhs) | Operand::Copy(rhs) => { @@ -360,6 +424,84 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { } #[instrument(level = "trace", skip(self))] + fn process_assign( + &mut self, + bb: BasicBlock, + lhs_place: &Place<'tcx>, + rhs: &Rvalue<'tcx>, + state: &mut State<ConditionSet<'a>>, + ) -> Option<!> { + let lhs = self.map.find(lhs_place.as_ref())?; + match rhs { + Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?, + // Transfer the conditions on the copy rhs. + Rvalue::CopyForDeref(rhs) => { + self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)? + } + Rvalue::Discriminant(rhs) => { + let rhs = self.map.find_discr(rhs.as_ref())?; + state.insert_place_idx(rhs, lhs, self.map); + } + // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. + Rvalue::Aggregate(box ref kind, ref operands) => { + let agg_ty = lhs_place.ty(self.body, self.tcx).ty; + let lhs = match kind { + // Do not support unions. + AggregateKind::Adt(.., Some(_)) => return None, + AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { + if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) + && let Ok(discr_value) = + self.ecx.discriminant_for_variant(agg_ty, *variant_index) + { + self.process_immediate(bb, discr_target, discr_value, state); + } + self.map.apply(lhs, TrackElem::Variant(*variant_index))? + } + _ => lhs, + }; + for (field_index, operand) in operands.iter_enumerated() { + if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) { + self.process_operand(bb, field, operand, state); + } + } + } + // Transfer the conditions on the copy rhs, after inversing polarity. + Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { + let conditions = state.try_get_idx(lhs, self.map)?; + let place = self.map.find(place.as_ref())?; + let conds = conditions.map(self.arena, Condition::inv); + state.insert_value_idx(place, conds, self.map); + } + // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. + // Create a condition on `rhs ?= B`. + Rvalue::BinaryOp( + op, + box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value)) + | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)), + ) => { + let conditions = state.try_get_idx(lhs, self.map)?; + let place = self.map.find(place.as_ref())?; + let equals = match op { + BinOp::Eq => ScalarInt::TRUE, + BinOp::Ne => ScalarInt::FALSE, + _ => return None, + }; + let value = value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?; + let conds = conditions.map(self.arena, |c| Condition { + value, + polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, + ..c + }); + state.insert_value_idx(place, conds, self.map); + } + + _ => {} + } + + None + } + + #[instrument(level = "trace", skip(self))] fn process_statement( &mut self, bb: BasicBlock, @@ -374,18 +516,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // Below, `lhs` is the return value of `mutated_statement`, // the place to which `conditions` apply. - let discriminant_for_variant = |enum_ty: Ty<'tcx>, variant_index| { - let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?; - let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?; - let scalar = ScalarInt::try_from_uint(discr.val, discr_layout.size)?; - Some(Operand::const_from_scalar( - self.tcx, - discr.ty, - scalar.into(), - rustc_span::DUMMY_SP, - )) - }; - match &stmt.kind { // If we expect `discriminant(place) ?= A`, // we have an opportunity if `variant_index ?= A`. @@ -395,7 +525,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant // of a niche encoding. If we cannot ensure that we write to the discriminant, do // nothing. - let enum_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?; + let enum_layout = self.ecx.layout_of(enum_ty).ok()?; let writes_discriminant = match enum_layout.variants { Variants::Single { index } => { assert_eq!(index, *variant_index); @@ -408,8 +538,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { } => *variant_index != untagged_variant, }; if writes_discriminant { - let discr = discriminant_for_variant(enum_ty, *variant_index)?; - self.process_operand(bb, discr_target, &discr, state)?; + let discr = self.ecx.discriminant_for_variant(enum_ty, *variant_index).ok()?; + self.process_immediate(bb, discr_target, discr, state)?; } } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. @@ -420,89 +550,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); } StatementKind::Assign(box (lhs_place, rhs)) => { - if let Some(lhs) = self.map.find(lhs_place.as_ref()) { - match rhs { - Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?, - // Transfer the conditions on the copy rhs. - Rvalue::CopyForDeref(rhs) => { - self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)? - } - Rvalue::Discriminant(rhs) => { - let rhs = self.map.find_discr(rhs.as_ref())?; - state.insert_place_idx(rhs, lhs, self.map); - } - // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. - Rvalue::Aggregate(box ref kind, ref operands) => { - let agg_ty = lhs_place.ty(self.body, self.tcx).ty; - let lhs = match kind { - // Do not support unions. - AggregateKind::Adt(.., Some(_)) => return None, - AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { - if let Some(discr_target) = - self.map.apply(lhs, TrackElem::Discriminant) - && let Some(discr_value) = - discriminant_for_variant(agg_ty, *variant_index) - { - self.process_operand(bb, discr_target, &discr_value, state); - } - self.map.apply(lhs, TrackElem::Variant(*variant_index))? - } - _ => lhs, - }; - for (field_index, operand) in operands.iter_enumerated() { - if let Some(field) = - self.map.apply(lhs, TrackElem::Field(field_index)) - { - self.process_operand(bb, field, operand, state); - } - } - } - // Transfer the conditions on the copy rhs, after inversing polarity. - Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { - let conditions = state.try_get_idx(lhs, self.map)?; - let place = self.map.find(place.as_ref())?; - let conds = conditions.map(self.arena, Condition::inv); - state.insert_value_idx(place, conds, self.map); - } - // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. - // Create a condition on `rhs ?= B`. - Rvalue::BinaryOp( - op, - box ( - Operand::Move(place) | Operand::Copy(place), - Operand::Constant(value), - ) - | box ( - Operand::Constant(value), - Operand::Move(place) | Operand::Copy(place), - ), - ) => { - let conditions = state.try_get_idx(lhs, self.map)?; - let place = self.map.find(place.as_ref())?; - let equals = match op { - BinOp::Eq => ScalarInt::TRUE, - BinOp::Ne => ScalarInt::FALSE, - _ => return None, - }; - let value = value - .const_ - .normalize(self.tcx, self.param_env) - .try_to_scalar_int()?; - let conds = conditions.map(self.arena, |c| Condition { - value, - polarity: if c.matches(equals) { - Polarity::Eq - } else { - Polarity::Ne - }, - ..c - }); - state.insert_value_idx(place, conds, self.map); - } - - _ => {} - } - } + self.process_assign(bb, lhs_place, rhs, state)?; } _ => {} } @@ -577,7 +625,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { let discr = discr.place()?; let discr_ty = discr.ty(self.body, self.tcx).ty; - let discr_layout = self.tcx.layout_of(self.param_env.and(discr_ty)).ok()?; + let discr_layout = self.ecx.layout_of(discr_ty).ok()?; let conditions = state.try_get(discr.as_ref(), self.map)?; if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index ce9043ec287..19bfed4333c 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -4,6 +4,7 @@ #![feature(box_patterns)] #![feature(cow_is_borrowed)] #![feature(decl_macro)] +#![feature(impl_trait_in_assoc_type)] #![feature(is_sorted)] #![feature(let_chains)] #![feature(map_try_insert)] @@ -40,7 +41,7 @@ use rustc_middle::mir::{ }; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::sym; +use rustc_span::{source_map::Spanned, sym, DUMMY_SP}; use rustc_trait_selection::traits; #[macro_use] @@ -169,13 +170,13 @@ fn remap_mir_for_const_eval_select<'tcx>( { let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap(); - let ty = tupled_args.ty(&body.local_decls, tcx); + let ty = tupled_args.node.ty(&body.local_decls, tcx); let fields = ty.tuple_fields(); let num_args = fields.len(); let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt }; let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = - match tupled_args { + match tupled_args.node { Operand::Constant(_) => { // there is no good way of extracting a tuple arg from a constant (const generic stuff) // so we just create a temporary and deconstruct that. @@ -184,7 +185,7 @@ fn remap_mir_for_const_eval_select<'tcx>( source_info: SourceInfo::outermost(fn_span), kind: StatementKind::Assign(Box::new(( local.into(), - Rvalue::Use(tupled_args.clone()), + Rvalue::Use(tupled_args.node.clone()), ))), }); (Operand::Move, local.into()) @@ -199,11 +200,11 @@ fn remap_mir_for_const_eval_select<'tcx>( place_elems.push(ProjectionElem::Field(x.into(), fields[x])); let projection = tcx.mk_place_elems(&place_elems); let place = Place { local: place.local, projection }; - method(place) + Spanned { node: method(place), span: DUMMY_SP } }) .collect(); terminator.kind = TerminatorKind::Call { - func, + func: func.node, args: arguments, destination, target, @@ -595,24 +596,25 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &multiple_return_terminators::MultipleReturnTerminators, &instsimplify::InstSimplify, &simplify::SimplifyLocals::BeforeConstProp, - ©_prop::CopyProp, + &dead_store_elimination::DeadStoreElimination::Initial, + &gvn::GVN, + &simplify::SimplifyLocals::AfterGVN, // Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may // destroy the SSA property. It should still happen before const-propagation, so the // latter pass will leverage the created opportunities. &separate_const_switch::SeparateConstSwitch, - &gvn::GVN, - &simplify::SimplifyLocals::AfterGVN, &dataflow_const_prop::DataflowConstProp, &const_debuginfo::ConstDebugInfo, &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, - &dead_store_elimination::DeadStoreElimination, &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::Final), &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::Final), + ©_prop::CopyProp, + &dead_store_elimination::DeadStoreElimination::Final, &nrvo::RenameReturnPlace, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index c0c0a3f5ee6..5b269185e87 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -131,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { self.places.insert(destination.as_ref()); let mut has_duplicates = false; for arg in args { - if let Operand::Move(place) = arg { + if let Operand::Move(place) = &arg.node { has_duplicates |= !self.places.insert(place.as_ref()); } } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 18f588dccf6..897375e0e16 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -45,9 +45,9 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { kind: StatementKind::Intrinsic(Box::new( NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { - src: args.next().unwrap(), - dst: args.next().unwrap(), - count: args.next().unwrap(), + src: args.next().unwrap().node, + dst: args.next().unwrap().node, + count: args.next().unwrap().node, }, ), )), @@ -66,7 +66,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::Assume(args.next().unwrap()), + NonDivergingIntrinsic::Assume(args.next().unwrap().node), )), }); assert_eq!( @@ -112,7 +112,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), + Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), }); terminator.kind = TerminatorKind::Goto { target }; @@ -136,7 +136,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs, rhs))), + Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), }); terminator.kind = TerminatorKind::Goto { target }; @@ -164,7 +164,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { let [arg] = args.as_slice() else { span_bug!(terminator.source_info.span, "Wrong number of arguments"); }; - let derefed_place = if let Some(place) = arg.place() + let derefed_place = if let Some(place) = arg.node.place() && let Some(local) = place.as_local() { tcx.mk_place_deref(local.into()) @@ -200,7 +200,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { "Wrong number of arguments for write_via_move intrinsic", ); }; - let derefed_place = if let Some(place) = ptr.place() + let derefed_place = if let Some(place) = ptr.node.place() && let Some(local) = place.as_local() { tcx.mk_place_deref(local.into()) @@ -214,13 +214,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( derefed_place, - Rvalue::Use(val), + Rvalue::Use(val.node), ))), }); terminator.kind = TerminatorKind::Goto { target }; } sym::discriminant_value => { - if let (Some(target), Some(arg)) = (*target, args[0].place()) { + if let (Some(target), Some(arg)) = (*target, args[0].node.place()) { let arg = tcx.mk_place_deref(arg); block.statements.push(Statement { source_info: terminator.source_info, @@ -244,7 +244,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, delta))), + Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr.node, delta.node))), ))), }); terminator.kind = TerminatorKind::Goto { target }; @@ -265,7 +265,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::Cast(CastKind::Transmute, arg, dst_ty), + Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty), ))), }); diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index daeb56666f4..8137525a332 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -50,7 +50,7 @@ fn lower_slice_len_call<'tcx>( } = &terminator.kind // some heuristics for fast rejection && let [arg] = &args[..] - && let Some(arg) = arg.place() + && let Some(arg) = arg.node.place() && let ty::FnDef(fn_def_id, _) = func.ty(local_decls, tcx).kind() && *fn_def_id == slice_len_fn_item_def_id { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 841b86fed45..c00093ea27e 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::{self, List, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_span::source_map::Spanned; use std::assert_matches::assert_matches; use std::cell::Cell; @@ -565,7 +566,7 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_call( &mut self, callee: &Operand<'tcx>, - args: &[Operand<'tcx>], + args: &[Spanned<Operand<'tcx>>], ) -> Result<(), Unpromotable> { let fn_ty = callee.ty(self.body, self.tcx); @@ -595,7 +596,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(callee)?; for arg in args { - self.validate_operand(arg)?; + self.validate_operand(&arg.node)?; } Ok(()) @@ -731,7 +732,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } => { self.visit_operand(&mut func, loc); for arg in &mut args { - self.visit_operand(arg, loc); + self.visit_operand(&mut arg.node, loc); } let last = self.promoted.basic_blocks.last_index().unwrap(); diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f6b820bfcd0..89414ce940e 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -9,7 +9,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_index::{Idx, IndexVec}; -use rustc_span::Span; +use rustc_span::{source_map::Spanned, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use std::fmt; @@ -526,7 +526,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { vec![statement], TerminatorKind::Call { func, - args: vec![Operand::Move(ref_loc)], + args: vec![Spanned { node: Operand::Move(ref_loc), span: DUMMY_SP }], destination: dest, target: Some(next), unwind: UnwindAction::Cleanup(cleanup), @@ -811,6 +811,7 @@ fn build_call_shim<'tcx>( }; // BB #0 + let args = args.into_iter().map(|a| Spanned { node: a, span: DUMMY_SP }).collect::<Vec<_>>(); block( &mut blocks, statements, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 92f001cc321..20e70f87b75 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -614,8 +614,8 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { } fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - let limit = self.tcx.move_size_limit().0; - if limit == 0 { + let limit = self.tcx.move_size_limit(); + if limit.0 == 0 { return; } @@ -627,48 +627,19 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { return; } - let limit = Size::from_bytes(limit); - let ty = operand.ty(self.body, self.tcx); - let ty = self.monomorphize(ty); - let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { return }; - if layout.size <= limit { - return; - } - debug!(?layout); let source_info = self.body.source_info(location); debug!(?source_info); - for span in &self.move_size_spans { - if span.overlaps(source_info.span) { - return; - } - } - let lint_root = source_info.scope.lint_root(&self.body.source_scopes); - debug!(?lint_root); - let Some(lint_root) = lint_root else { - // This happens when the issue is in a function from a foreign crate that - // we monomorphized in the current crate. We can't get a `HirId` for things - // in other crates. - // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root - // but correct span? This would make the lint at least accept crate-level lint attributes. - return; + + if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { + self.lint_large_assignment(limit.0, too_large_size, location, source_info.span); }; - self.tcx.emit_spanned_lint( - LARGE_ASSIGNMENTS, - lint_root, - source_info.span, - LargeAssignmentsLint { - span: source_info.span, - size: layout.size.bytes(), - limit: limit.bytes(), - }, - ); - self.move_size_spans.push(source_info.span); } fn check_fn_args_move_size( &mut self, callee_ty: Ty<'tcx>, - args: &[mir::Operand<'tcx>], + args: &[Spanned<mir::Operand<'tcx>>], + fn_span: Span, location: Location, ) { let limit = self.tcx.move_size_limit(); @@ -692,10 +663,65 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { return; } + debug!(?def_id, ?fn_span); + for arg in args { - self.check_operand_move_size(arg, location); + if let Some(too_large_size) = self.operand_size_if_too_large(limit, &arg.node) { + self.lint_large_assignment(limit.0, too_large_size, location, arg.span); + }; + } + } + + fn operand_size_if_too_large( + &mut self, + limit: Limit, + operand: &mir::Operand<'tcx>, + ) -> Option<Size> { + let ty = operand.ty(self.body, self.tcx); + let ty = self.monomorphize(ty); + let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { + return None; + }; + if layout.size.bytes_usize() > limit.0 { + debug!(?layout); + Some(layout.size) + } else { + None } } + + fn lint_large_assignment( + &mut self, + limit: usize, + too_large_size: Size, + location: Location, + span: Span, + ) { + let source_info = self.body.source_info(location); + debug!(?source_info); + for reported_span in &self.move_size_spans { + if reported_span.overlaps(span) { + return; + } + } + let lint_root = source_info.scope.lint_root(&self.body.source_scopes); + debug!(?lint_root); + let Some(lint_root) = lint_root else { + // This happens when the issue is in a function from a foreign crate that + // we monomorphized in the current crate. We can't get a `HirId` for things + // in other crates. + // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root + // but correct span? This would make the lint at least accept crate-level lint attributes. + return; + }; + self.tcx.emit_spanned_lint( + LARGE_ASSIGNMENTS, + lint_root, + span, + LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 }, + ); + self.move_size_spans.push(span); + } } impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { @@ -813,11 +839,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, .. } => { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); - self.check_fn_args_move_size(callee_ty, args, location); - visit_fn_use(self.tcx, callee_ty, true, source, self.output) + self.check_fn_args_move_size(callee_ty, args, *fn_span, location); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; @@ -1407,7 +1433,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { - for impl_def_id in tcx.inherent_impls(def_id) { + for impl_def_id in tcx.inherent_impls(def_id).ok()? { if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( tcx, fn_ident, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 2c40cd4d8f2..7ff182381b8 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -182,7 +182,7 @@ where } // Ensure CGUs are sorted by name, so that we get deterministic results. - if !codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str()))) { + if !codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str()) { let mut names = String::new(); for cgu in codegen_units.iter() { names += &format!("- {}\n", cgu.name()); @@ -317,7 +317,7 @@ fn merge_codegen_units<'tcx>( assert!(cx.tcx.sess.codegen_units().as_usize() >= 1); // A sorted order here ensures merging is deterministic. - assert!(codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str())))); + assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str())); // This map keeps track of what got merged into what. let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> = diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index c11a6fab7e5..f904e0c44ea 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -407,6 +407,9 @@ parse_invalid_logical_operator = `{$incorrect}` is not a logical operator parse_invalid_meta_item = expected unsuffixed literal or identifier, found `{$token}` +parse_invalid_meta_item_unquoted_ident = expected unsuffixed literal, found `{$token}` + .suggestion = surround the identifier with quotation marks to parse it as a string + parse_invalid_offset_of = offset_of expects dot-separated field and variant names parse_invalid_unicode_escape = invalid unicode character escape @@ -613,6 +616,8 @@ parse_note_mut_pattern_usage = `mut` may be followed by `variable` and `variable parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns are separated with `|`, not `||` +parse_nul_in_c_str = null characters in C string literals are not supported + parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings parse_out_of_range_hex_escape = out of range hex escape @@ -719,6 +724,8 @@ parse_sugg_turbofish_syntax = use `::<...>` instead of `<...>` to specify lifeti parse_sugg_wrap_expression_in_parentheses = wrap the expression in parentheses +parse_sugg_wrap_macro_in_parentheses = use parentheses instead of braces for this macro + parse_sugg_wrap_pattern_in_parens = wrap the pattern in parentheses parse_switch_mut_let_order = diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0de252707bd..40852048293 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -722,19 +722,32 @@ pub(crate) struct LabeledLoopInBreak { #[primary_span] pub span: Span, #[subdiagnostic] - pub sub: WrapExpressionInParentheses, + pub sub: WrapInParentheses, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_sugg_wrap_expression_in_parentheses, - applicability = "machine-applicable" -)] -pub(crate) struct WrapExpressionInParentheses { - #[suggestion_part(code = "(")] - pub left: Span, - #[suggestion_part(code = ")")] - pub right: Span, + +pub(crate) enum WrapInParentheses { + #[multipart_suggestion( + parse_sugg_wrap_expression_in_parentheses, + applicability = "machine-applicable" + )] + Expression { + #[suggestion_part(code = "(")] + left: Span, + #[suggestion_part(code = ")")] + right: Span, + }, + #[multipart_suggestion( + parse_sugg_wrap_macro_in_parentheses, + applicability = "machine-applicable" + )] + MacroArgs { + #[suggestion_part(code = "(")] + left: Span, + #[suggestion_part(code = ")")] + right: Span, + }, } #[derive(Diagnostic)] @@ -936,7 +949,7 @@ pub(crate) struct InvalidExpressionInLetElse { pub span: Span, pub operator: &'static str, #[subdiagnostic] - pub sugg: WrapExpressionInParentheses, + pub sugg: WrapInParentheses, } #[derive(Diagnostic)] @@ -945,7 +958,7 @@ pub(crate) struct InvalidCurlyInLetElse { #[primary_span] pub span: Span, #[subdiagnostic] - pub sugg: WrapExpressionInParentheses, + pub sugg: WrapInParentheses, } #[derive(Diagnostic)] @@ -973,6 +986,25 @@ pub(crate) struct InvalidMetaItem { pub token: Token, } +#[derive(Diagnostic)] +#[diag(parse_invalid_meta_item_unquoted_ident)] +pub(crate) struct InvalidMetaItemUnquotedIdent { + #[primary_span] + pub span: Span, + pub token: Token, + #[subdiagnostic] + pub sugg: InvalidMetaItemSuggQuoteIdent, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct InvalidMetaItemSuggQuoteIdent { + #[suggestion_part(code = "\"")] + pub before: Span, + #[suggestion_part(code = "\"")] + pub after: Span, +} + #[derive(Subdiagnostic)] #[suggestion( parse_sugg_escape_identifier, @@ -2144,6 +2176,11 @@ pub enum UnescapeError { #[subdiagnostic] suggestion: MoreThanOneCharSugg, }, + #[diag(parse_nul_in_c_str)] + NulInCStr { + #[primary_span] + span: Span, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index fbc77f28780..3238f8e23bb 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -262,6 +262,9 @@ pub(crate) fn emit_unescape_error( EscapeError::LoneSlash => { dcx.emit_err(UnescapeError::LoneSlash(err_span)); } + EscapeError::NulInCStr => { + dcx.emit_err(UnescapeError::NulInCStr { span: err_span }); + } EscapeError::UnskippedWhitespaceWarning => { let (c, char_span) = last_char(); dcx.emit_warn(UnescapeError::UnskippedWhitespace { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 02dab95233a..a92609c2c2f 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,4 +1,7 @@ -use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute}; +use crate::errors::{ + InvalidMetaItem, InvalidMetaItemSuggQuoteIdent, InvalidMetaItemUnquotedIdent, + SuffixedLiteralInAttribute, +}; use crate::fluent_generated as fluent; use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; @@ -417,9 +420,26 @@ impl<'a> Parser<'a> { Err(err) => err.cancel(), } - Err(self - .dcx() - .create_err(InvalidMetaItem { span: self.token.span, token: self.token.clone() })) + let token = self.token.clone(); + + // Check for unquoted idents in meta items, e.g.: #[cfg(key = foo)] + // `from_expansion()` ensures we don't suggest for cases such as + // `#[cfg(feature = $expr)]` in macros + if self.prev_token == token::Eq && !self.token.span.from_expansion() { + let before = self.token.span.shrink_to_lo(); + while matches!(self.token.kind, token::Ident(..)) { + self.bump(); + } + let after = self.prev_token.span.shrink_to_hi(); + let sugg = InvalidMetaItemSuggQuoteIdent { before, after }; + return Err(self.dcx().create_err(InvalidMetaItemUnquotedIdent { + span: token.span, + token, + sugg, + })); + } + + Err(self.dcx().create_err(InvalidMetaItem { span: token.span, token })) } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8ca02452342..ae3661a530b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -33,7 +33,6 @@ use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Spanned}; -use rustc_span::symbol::kw::PathRoot; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos, Span}; use thin_vec::{thin_vec, ThinVec}; @@ -642,26 +641,13 @@ impl<'a> Parser<'a> { } /// Parse `box expr` - this syntax has been removed, but we still parse this - /// for now to provide an automated way to fix usages of it - fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - let (span, expr) = self.parse_expr_prefix_common(lo)?; - let code = self.sess.source_map().span_to_snippet(span.with_lo(lo.hi())).unwrap(); - self.dcx().emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() }); - // So typechecking works, parse `box <expr>` as `::std::boxed::Box::new(expr)` - let path = Path { - span, - segments: [ - PathSegment::from_ident(Ident::with_dummy_span(PathRoot)), - PathSegment::from_ident(Ident::with_dummy_span(sym::std)), - PathSegment::from_ident(Ident::from_str("boxed")), - PathSegment::from_ident(Ident::from_str("Box")), - PathSegment::from_ident(Ident::with_dummy_span(sym::new)), - ] - .into(), - tokens: None, - }; - let path = self.mk_expr(span, ExprKind::Path(None, path)); - Ok((span, self.mk_call(path, ThinVec::from([expr])))) + /// for now to provide a more useful error + fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> { + let (span, _) = self.parse_expr_prefix_common(box_kw)?; + let inner_span = span.with_lo(box_kw.hi()); + let code = self.sess.source_map().span_to_snippet(inner_span).unwrap(); + self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() }); + Ok((span, ExprKind::Err)) } fn is_mistaken_not_ident_negation(&self) -> bool { @@ -1844,7 +1830,7 @@ impl<'a> Parser<'a> { let lexpr = self.parse_expr_labeled(label, true)?; self.dcx().emit_err(errors::LabeledLoopInBreak { span: lexpr.span, - sub: errors::WrapExpressionInParentheses { + sub: errors::WrapInParentheses::Expression { left: lexpr.span.shrink_to_lo(), right: lexpr.span.shrink_to_hi(), }, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1a8bbf0a157..1bae5b32240 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -389,7 +389,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::InvalidExpressionInLetElse { span: init.span, operator: op.node.as_str(), - sugg: errors::WrapExpressionInParentheses { + sugg: errors::WrapInParentheses::Expression { left: init.span.shrink_to_lo(), right: init.span.shrink_to_hi(), }, @@ -400,12 +400,19 @@ impl<'a> Parser<'a> { fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { if let Some(trailing) = classify::expr_trailing_brace(init) { - self.dcx().emit_err(errors::InvalidCurlyInLetElse { - span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)), - sugg: errors::WrapExpressionInParentheses { + let sugg = match &trailing.kind { + ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs { + left: mac.args.dspan.open, + right: mac.args.dspan.close, + }, + _ => errors::WrapInParentheses::Expression { left: trailing.span.shrink_to_lo(), right: trailing.span.shrink_to_hi(), }, + }; + self.dcx().emit_err(errors::InvalidCurlyInLetElse { + span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)), + sugg, }); } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index ac2ca23ad41..d7f17ac4547 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -57,7 +57,7 @@ struct MarkSymbolVisitor<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, live_symbols: LocalDefIdSet, - repr_has_repr_c: bool, + repr_unconditionally_treats_fields_as_live: bool, repr_has_repr_simd: bool, in_pat: bool, ignore_variant_stack: Vec<DefId>, @@ -128,7 +128,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(def_id) = self.typeck_results().type_dependent_def_id(id) { self.check_def_id(def_id); } else { - bug!("no type-dependent def for method"); + assert!( + self.typeck_results().tainted_by_errors.is_some(), + "no type-dependent def for method" + ); } } @@ -362,15 +365,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return; } - let had_repr_c = self.repr_has_repr_c; + let unconditionally_treated_fields_as_live = + self.repr_unconditionally_treats_fields_as_live; let had_repr_simd = self.repr_has_repr_simd; - self.repr_has_repr_c = false; + self.repr_unconditionally_treats_fields_as_live = false; self.repr_has_repr_simd = false; match node { Node::Item(item) => match item.kind { hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => { let def = self.tcx.adt_def(item.owner_id); - self.repr_has_repr_c = def.repr().c(); + self.repr_unconditionally_treats_fields_as_live = + def.repr().c() || def.repr().transparent(); self.repr_has_repr_simd = def.repr().simd(); intravisit::walk_item(self, item) @@ -408,7 +413,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { _ => {} } self.repr_has_repr_simd = had_repr_simd; - self.repr_has_repr_c = had_repr_c; + self.repr_unconditionally_treats_fields_as_live = unconditionally_treated_fields_as_live; } fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) { @@ -432,11 +437,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) { let tcx = self.tcx; - let has_repr_c = self.repr_has_repr_c; + let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live; let has_repr_simd = self.repr_has_repr_simd; let live_fields = def.fields().iter().filter_map(|f| { let def_id = f.def_id; - if has_repr_c || (f.is_positional() && has_repr_simd) { + if unconditionally_treat_fields_as_live || (f.is_positional() && has_repr_simd) { return Some(def_id); } if !tcx.visibility(f.hir_id.owner.def_id).is_public() { @@ -738,7 +743,7 @@ fn live_symbols_and_ignored_derived_traits( tcx, maybe_typeck_results: None, live_symbols: Default::default(), - repr_has_repr_c: false, + repr_unconditionally_treats_fields_as_live: false, repr_has_repr_simd: false, in_pat: false, ignore_variant_stack: vec![], diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index a9d0e0d2dd3..25ca685cbfa 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -180,8 +180,8 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { Default::default() }); let main_def_opt = tcx.resolutions(()).main_def; - let diagnostic_id = error_code!(E0601); - let add_teach_note = tcx.sess.teach(&diagnostic_id); + let code = error_code!(E0601); + let add_teach_note = tcx.sess.teach(&code); // The file may be empty, which leads to the diagnostic machinery not emitting this // note. This is a relatively simple way to detect that case and emit a span-less // note instead. diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index ff29fc5929c..528a52f4225 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -303,7 +303,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Ref, Lit, Range, - Slice + Slice, + Err ] ); hir_visit::walk_pat(self, p) @@ -576,7 +577,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Rest, Never, Paren, - MacCall + MacCall, + Err ] ); ast_visit::walk_pat(self, p) diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 2152f9fda00..1d0e1cb7e6a 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start derivative = "2.2.0" +rustc-hash = "1.1.0" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } @@ -20,13 +21,10 @@ rustc_span = { path = "../rustc_span", optional = true } rustc_target = { path = "../rustc_target", optional = true } smallvec = { version = "1.8.1", features = ["union"] } tracing = "0.1" -typed-arena = { version = "2.0.2", optional = true } # tidy-alphabetical-end [features] default = ["rustc"] -# It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so -# we use another feature instead. The crate won't compile if one of these isn't enabled. rustc = [ "dep:rustc_arena", "dep:rustc_data_structures", @@ -41,6 +39,3 @@ rustc = [ "smallvec/may_dangle", "rustc_index/nightly", ] -stable = [ - "dep:typed-arena", -] diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 15ff4ceb5b3..eba71a23435 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -155,13 +155,13 @@ use std::iter::once; use smallvec::SmallVec; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; -use rustc_index::bit_set::{BitSet, GrowableBitSet}; -use rustc_index::IndexVec; +use rustc_index::bit_set::GrowableBitSet; use self::Constructor::*; use self::MaybeInfiniteInt::*; use self::SliceKind::*; +use crate::index; use crate::usefulness::PlaceCtxt; use crate::TypeCx; @@ -718,7 +718,7 @@ impl<Cx: TypeCx> Constructor<Cx> { /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. - pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize { + pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, Cx>) -> usize { pcx.ctor_arity(self) } @@ -727,7 +727,7 @@ impl<Cx: TypeCx> Constructor<Cx> { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool { + pub(crate) fn is_covered_by(&self, pcx: &PlaceCtxt<'_, Cx>, other: &Self) -> bool { match (self, other) { (Wildcard, _) => pcx .mcx @@ -804,7 +804,10 @@ pub enum ConstructorSet<Cx: TypeCx> { Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. - Variants { variants: IndexVec<Cx::VariantIdx, VariantVisibility>, non_exhaustive: bool }, + Variants { + variants: index::IdxContainer<Cx::VariantIdx, VariantVisibility>, + non_exhaustive: bool, + }, /// The type is `&T`. Ref, /// The type is a union. @@ -858,12 +861,14 @@ impl<Cx: TypeCx> ConstructorSet<Cx> { /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation /// and its invariants. - #[instrument(level = "debug", skip(self, pcx, ctors), ret)] + #[instrument(level = "debug", skip(self, ctors), ret)] pub(crate) fn split<'a>( &self, - pcx: &PlaceCtxt<'a, '_, Cx>, ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone, - ) -> SplitConstructorSet<Cx> { + ) -> SplitConstructorSet<Cx> + where + Cx: 'a, + { let mut present: SmallVec<[_; 1]> = SmallVec::new(); // Empty constructors found missing. let mut missing_empty = Vec::new(); @@ -904,7 +909,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set: BitSet<_> = BitSet::new_empty(variants.len()); + let mut seen_set = index::IdxSet::new_empty(variants.len()); for idx in seen.iter().map(|c| c.as_variant().unwrap()) { seen_set.insert(idx); } @@ -1003,17 +1008,6 @@ impl<Cx: TypeCx> ConstructorSet<Cx> { } } - // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty. - // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty - // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. - if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on() - && !(pcx.is_scrutinee && matches!(self, Self::NoConstructors)) - { - // Treat all missing constructors as nonempty. - // This clears `missing_empty`. - missing.append(&mut missing_empty); - } - SplitConstructorSet { present, missing, missing_empty } } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 374914055d8..4fd01b5e638 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -21,30 +21,59 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } use std::fmt; -use rustc_index::Idx; +#[cfg(feature = "rustc")] +pub mod index { + // Faster version when the indices of variants are `0..variants.len()`. + pub use rustc_index::bit_set::BitSet as IdxSet; + pub use rustc_index::Idx; + pub use rustc_index::IndexVec as IdxContainer; +} +#[cfg(not(feature = "rustc"))] +pub mod index { + // Slower version when the indices of variants are something else. + pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {} + impl<T: Copy + PartialEq + Eq + std::hash::Hash> Idx for T {} + + #[derive(Debug)] + pub struct IdxContainer<K, V>(pub rustc_hash::FxHashMap<K, V>); + impl<K: Idx, V> IdxContainer<K, V> { + pub fn len(&self) -> usize { + self.0.len() + } + pub fn iter_enumerated(&self) -> impl Iterator<Item = (K, &V)> { + self.0.iter().map(|(k, v)| (*k, v)) + } + } + + #[derive(Debug)] + pub struct IdxSet<T>(pub rustc_hash::FxHashSet<T>); + impl<T: Idx> IdxSet<T> { + pub fn new_empty(_len: usize) -> Self { + Self(Default::default()) + } + pub fn contains(&self, elem: T) -> bool { + self.0.contains(&elem) + } + pub fn insert(&mut self, elem: T) { + self.0.insert(elem); + } + } +} + #[cfg(feature = "rustc")] use rustc_middle::ty::Ty; #[cfg(feature = "rustc")] use rustc_span::ErrorGuaranteed; -use crate::constructor::{Constructor, ConstructorSet}; +use crate::constructor::{Constructor, ConstructorSet, IntRange}; #[cfg(feature = "rustc")] -use crate::lints::{ - lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn, -}; +use crate::lints::{lint_nonexhaustive_missing_variants, PatternColumn}; use crate::pat::DeconstructedPat; #[cfg(feature = "rustc")] use crate::rustc::RustcMatchCheckCtxt; #[cfg(feature = "rustc")] use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; -// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so -// we use another feature instead. The crate won't compile if one of these isn't enabled. -#[cfg(feature = "rustc")] -pub(crate) use rustc_arena::TypedArena; -#[cfg(feature = "stable")] -pub(crate) use typed_arena::Arena as TypedArena; - pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} @@ -53,11 +82,11 @@ impl<'a, T: ?Sized> Captures<'a> for T {} /// Most of the crate is parameterized on a type that implements this trait. pub trait TypeCx: Sized + fmt::Debug { /// The type of a pattern. - type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy + type Ty: Clone + fmt::Debug; /// Errors that can abort analysis. type Error: fmt::Debug; /// The index of an enum variant. - type VariantIdx: Clone + Idx; + type VariantIdx: Clone + index::Idx + fmt::Debug; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store in a match arm. @@ -68,32 +97,41 @@ pub trait TypeCx: Sized + fmt::Debug { fn is_exhaustive_patterns_feature_on(&self) -> bool; /// The number of fields for this constructor. - fn ctor_arity(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> usize; + fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize; /// The types of the fields for this constructor. The result must have a length of /// `ctor_arity()`. - fn ctor_sub_tys(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> &[Self::Ty]; + fn ctor_sub_tys(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> &[Self::Ty]; /// The set of all the constructors for `ty`. /// /// This must follow the invariants of `ConstructorSet` - fn ctors_for_ty(&self, ty: Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>; + fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>; /// Best-effort `Debug` implementation. fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result; /// Raise a bug. fn bug(&self, fmt: fmt::Arguments<'_>) -> !; + + /// Lint that the range `pat` overlapped with all the ranges in `overlaps_with`, where the range + /// they overlapped over is `overlaps_on`. We only detect singleton overlaps. + /// The default implementation does nothing. + fn lint_overlapping_range_endpoints( + &self, + _pat: &DeconstructedPat<'_, Self>, + _overlaps_on: IntRange, + _overlaps_with: &[&DeconstructedPat<'_, Self>], + ) { + } } /// Context that provides information global to a match. #[derive(derivative::Derivative)] #[derivative(Clone(bound = ""), Copy(bound = ""))] -pub struct MatchCtxt<'a, 'p, Cx: TypeCx> { +pub struct MatchCtxt<'a, Cx: TypeCx> { /// The context for type information. pub tycx: &'a Cx, - /// An arena to store the wildcards we produce during analysis. - pub wildcard_arena: &'p TypedArena<DeconstructedPat<'p, Cx>>, } /// The arm of a match expression. @@ -114,24 +152,16 @@ pub fn analyze_match<'p, 'tcx>( arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { - // Arena to store the extra wildcards we construct during analysis. - let wildcard_arena = tycx.pattern_arena; let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); - let cx = MatchCtxt { tycx, wildcard_arena }; + let cx = MatchCtxt { tycx }; let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?; - let pat_column = PatternColumn::new(arms); - - // Lint ranges that overlap on their endpoints, which is likely a mistake. - if !report.overlapping_range_endpoints.is_empty() { - lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints); - } - // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { + let pat_column = PatternColumn::new(arms); lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?; } diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index cfe4ca3ce93..f9f065fbe8b 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,14 +1,11 @@ -use rustc_session::lint; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::ErrorGuaranteed; -use crate::errors::{ - self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered, -}; +use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered}; use crate::pat::PatOrWild; use crate::rustc::{ - self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, - RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat, + Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt, + SplitConstructorSet, WitnessPat, }; /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that @@ -49,7 +46,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { } fn head_ty(&self) -> Option<RevealedTy<'tcx>> { - self.patterns.first().map(|pat| pat.ty()) + self.patterns.first().map(|pat| *pat.ty()) } /// Do constructor splitting on the constructors of the column. @@ -59,7 +56,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { ) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); let ctors_for_ty = &pcx.ctors_for_ty()?; - Ok(ctors_for_ty.split(pcx, column_ctors)) + Ok(ctors_for_ty.split(column_ctors)) } /// Does specialization: given a constructor, this takes the patterns from the column that match @@ -104,7 +101,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let Some(ty) = column.head_ty() else { return Ok(Vec::new()); }; - let pcx = &PlaceCtxt::new_dummy(cx, ty); + let pcx = &PlaceCtxt::new_dummy(cx, &ty); let set = column.analyze_ctors(pcx)?; if set.present.is_empty() { @@ -196,26 +193,3 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } Ok(()) } - -pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( - cx: MatchCtxt<'a, 'p, 'tcx>, - overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>], -) { - let rcx = cx.tycx; - for overlap in overlapping_range_endpoints { - let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty()); - let overlaps: Vec<_> = overlap - .overlaps_with - .iter() - .map(|pat| pat.data().unwrap().span) - .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) - .collect(); - let pat_span = overlap.pat.data().unwrap().span; - rcx.tcx.emit_spanned_lint( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - rcx.match_lint_level, - pat_span, - errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span }, - ); - } -} diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 2f5dc241cb7..8cd0ecb073c 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -54,8 +54,8 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { pub fn ctor(&self) -> &Constructor<Cx> { &self.ctor } - pub fn ty(&self) -> Cx::Ty { - self.ty + pub fn ty(&self) -> &Cx::Ty { + &self.ty } /// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that /// does not correspond to a user-supplied pattern. @@ -240,17 +240,17 @@ impl<Cx: TypeCx> WitnessPat<Cx> { /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. - pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor<Cx>) -> Self { + pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, Cx>, ctor: Constructor<Cx>) -> Self { let field_tys = pcx.ctor_sub_tys(&ctor); - let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); - Self::new(ctor, fields, pcx.ty) + let fields = field_tys.iter().cloned().map(|ty| Self::wildcard(ty)).collect(); + Self::new(ctor, fields, pcx.ty.clone()) } pub fn ctor(&self) -> &Constructor<Cx> { &self.ctor } - pub fn ty(&self) -> Cx::Ty { - self.ty + pub fn ty(&self) -> &Cx::Ty { + &self.ty } pub fn iter_fields(&self) -> impl Iterator<Item = &WitnessPat<Cx>> { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index a17cd2c81b9..27b25802427 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1,3 +1,4 @@ +use smallvec::SmallVec; use std::fmt; use std::iter::once; @@ -5,24 +6,21 @@ use rustc_arena::{DroplessArena, TypedArena}; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_hir::HirId; -use rustc_index::Idx; -use rustc_index::IndexVec; +use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef}; -use rustc_span::ErrorGuaranteed; -use rustc_span::{Span, DUMMY_SP}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; +use rustc_session::lint; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; -use smallvec::SmallVec; use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; -use crate::TypeCx; +use crate::{errors, TypeCx}; use crate::constructor::Constructor::*; @@ -33,11 +31,9 @@ pub type ConstructorSet<'p, 'tcx> = pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>; -pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; -pub type OverlappingRanges<'p, 'tcx> = - crate::usefulness::OverlappingRanges<'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type PlaceCtxt<'a, 'p, 'tcx> = - crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; + crate::usefulness::PlaceCtxt<'a, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = crate::constructor::SplitConstructorSet<RustcMatchCheckCtxt<'p, 'tcx>>; pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcMatchCheckCtxt<'p, 'tcx>>; @@ -80,7 +76,9 @@ pub struct RustcMatchCheckCtxt<'p, 'tcx> { /// outside its module and should not be matchable with an empty match statement. pub module: DefId, pub param_env: ty::ParamEnv<'tcx>, + /// To allocate lowered patterns pub pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>, + /// To allocate the result of `self.ctor_sub_tys()` pub dropless_arena: &'p DroplessArena, /// Lint level at the match. pub match_lint_level: HirId, @@ -768,7 +766,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); let kind = match pat.ctor() { Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, - IntRange(range) => return self.hoist_pat_range(range, pat.ty()), + IntRange(range) => return self.hoist_pat_range(range, *pat.ty()), Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns @@ -787,7 +785,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let variant = &adt_def.variant(variant_index); let subpatterns = cx - .list_variant_nonhidden_fields(pat.ty(), variant) + .list_variant_nonhidden_fields(*pat.ty(), variant) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); @@ -798,7 +796,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { PatKind::Leaf { subpatterns } } } - _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), + _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()), }, // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should // be careful to reconstruct the correct constant pattern here. However a string @@ -963,21 +961,21 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { self.tcx.features().exhaustive_patterns } - fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize { - self.ctor_arity(ctor, ty) + fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize { + self.ctor_arity(ctor, *ty) } fn ctor_sub_tys( &self, ctor: &crate::constructor::Constructor<Self>, - ty: Self::Ty, + ty: &Self::Ty, ) -> &[Self::Ty] { - self.ctor_sub_tys(ctor, ty) + self.ctor_sub_tys(ctor, *ty) } fn ctors_for_ty( &self, - ty: Self::Ty, + ty: &Self::Ty, ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> { - self.ctors_for_ty(ty) + self.ctors_for_ty(*ty) } fn debug_pat( @@ -989,6 +987,27 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { fn bug(&self, fmt: fmt::Arguments<'_>) -> ! { span_bug!(self.scrut_span, "{}", fmt) } + + fn lint_overlapping_range_endpoints( + &self, + pat: &crate::pat::DeconstructedPat<'_, Self>, + overlaps_on: IntRange, + overlaps_with: &[&crate::pat::DeconstructedPat<'_, Self>], + ) { + let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty()); + let overlaps: Vec<_> = overlaps_with + .iter() + .map(|pat| pat.data().unwrap().span) + .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) + .collect(); + let pat_span = pat.data().unwrap().span; + self.tcx.emit_spanned_lint( + lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, + self.match_lint_level, + pat_span, + errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span }, + ); + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 85b6a6a3b6c..d7852a2b2cb 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -732,20 +732,19 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R { /// Context that provides information local to a place under investigation. #[derive(derivative::Derivative)] #[derivative(Debug(bound = ""), Clone(bound = ""), Copy(bound = ""))] -pub(crate) struct PlaceCtxt<'a, 'p, Cx: TypeCx> { +pub(crate) struct PlaceCtxt<'a, Cx: TypeCx> { #[derivative(Debug = "ignore")] - pub(crate) mcx: MatchCtxt<'a, 'p, Cx>, + pub(crate) mcx: MatchCtxt<'a, Cx>, /// Type of the place under investigation. - pub(crate) ty: Cx::Ty, - /// Whether the place is the original scrutinee place, as opposed to a subplace of it. - pub(crate) is_scrutinee: bool, + #[derivative(Clone(clone_with = "Clone::clone"))] // See rust-derivative#90 + pub(crate) ty: &'a Cx::Ty, } -impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> { +impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> { /// A `PlaceCtxt` when code other than `is_useful` needs one. #[cfg_attr(not(feature = "rustc"), allow(dead_code))] - pub(crate) fn new_dummy(mcx: MatchCtxt<'a, 'p, Cx>, ty: Cx::Ty) -> Self { - PlaceCtxt { mcx, ty, is_scrutinee: false } + pub(crate) fn new_dummy(mcx: MatchCtxt<'a, Cx>, ty: &'a Cx::Ty) -> Self { + PlaceCtxt { mcx, ty } } pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize { @@ -768,9 +767,6 @@ impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> { pub enum ValidityConstraint { ValidOnly, MaybeInvalid, - /// Option for backwards compatibility: the place is not known to be valid but we allow omitting - /// `useful && !reachable` arms anyway. - MaybeInvalidButAllowOmittingArms, } impl ValidityConstraint { @@ -778,20 +774,9 @@ impl ValidityConstraint { if is_valid_only { ValidOnly } else { MaybeInvalid } } - fn allow_omitting_side_effecting_arms(self) -> Self { - match self { - MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms, - // There are no side-effecting empty arms here, nothing to do. - ValidOnly => ValidOnly, - } - } - fn is_known_valid(self) -> bool { matches!(self, ValidOnly) } - fn allows_omitting_empty_arms(self) -> bool { - matches!(self, ValidOnly | MaybeInvalidButAllowOmittingArms) - } /// If the place has validity given by `self` and we read that the value at the place has /// constructor `ctor`, this computes what we can assume about the validity of the constructor @@ -814,7 +799,7 @@ impl fmt::Display for ValidityConstraint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { ValidOnly => "✓", - MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?", + MaybeInvalid => "?", }; write!(f, "{s}") } @@ -1039,8 +1024,8 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { matrix } - fn head_ty(&self) -> Option<Cx::Ty> { - self.place_ty.first().copied() + fn head_ty(&self) -> Option<&Cx::Ty> { + self.place_ty.first() } fn column_count(&self) -> usize { self.place_ty.len() @@ -1067,14 +1052,14 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { /// This computes `specialize(ctor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: &PlaceCtxt<'_, 'p, Cx>, + pcx: &PlaceCtxt<'_, Cx>, ctor: &Constructor<Cx>, ctor_is_relevant: bool, ) -> Matrix<'p, Cx> { let ctor_sub_tys = pcx.ctor_sub_tys(ctor); let arity = ctor_sub_tys.len(); let specialized_place_ty = - ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).copied().collect(); + ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).cloned().collect(); let ctor_sub_validity = self.place_validity[0].specialize(ctor); let specialized_place_validity = std::iter::repeat(ctor_sub_validity) .take(arity) @@ -1226,11 +1211,11 @@ impl<Cx: TypeCx> WitnessStack<Cx> { /// pats: [(false, "foo"), _, true] /// result: [Enum::Variant { a: (false, "foo"), b: _ }, true] /// ``` - fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, '_, Cx>, ctor: &Constructor<Cx>) { + fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, Cx>, ctor: &Constructor<Cx>) { let len = self.0.len(); let arity = ctor.arity(pcx); let fields = self.0.drain((len - arity)..).rev().collect(); - let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty); + let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty.clone()); self.0.push(pat); } } @@ -1277,7 +1262,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { /// Reverses specialization by `ctor`. See the section on `unspecialize` at the top of the file. fn apply_constructor( &mut self, - pcx: &PlaceCtxt<'_, '_, Cx>, + pcx: &PlaceCtxt<'_, Cx>, missing_ctors: &[Constructor<Cx>], ctor: &Constructor<Cx>, report_individual_missing_ctors: bool, @@ -1340,10 +1325,10 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { /// We can however get false negatives because exhaustiveness does not explore all cases. See the /// section on relevancy at the top of the file. fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( + mcx: MatchCtxt<'_, Cx>, overlap_range: IntRange, matrix: &Matrix<'p, Cx>, specialized_matrix: &Matrix<'p, Cx>, - overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>, ) { let overlap = overlap_range.lo; // Ranges that look like `lo..=overlap`. @@ -1373,11 +1358,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - overlapping_range_endpoints.push(OverlappingRanges { - pat, - overlaps_on: overlap_range, - overlaps_with, - }); + mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } suffixes.push((child_row_id, pat)) @@ -1393,11 +1374,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( .map(|&(_, pat)| pat) .collect(); if !overlaps_with.is_empty() { - overlapping_range_endpoints.push(OverlappingRanges { - pat, - overlaps_on: overlap_range, - overlaps_with, - }); + mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with); } } prefixes.push((child_row_id, pat)) @@ -1421,9 +1398,8 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( /// This is all explained at the top of the file. #[instrument(level = "debug", skip(mcx, is_top_level), ret)] fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( - mcx: MatchCtxt<'a, 'p, Cx>, + mcx: MatchCtxt<'a, Cx>, matrix: &mut Matrix<'p, Cx>, - overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>, is_top_level: bool, ) -> Result<WitnessMatrix<Cx>, Cx::Error> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); @@ -1435,7 +1411,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( return Ok(WitnessMatrix::empty()); } - let Some(ty) = matrix.head_ty() else { + let Some(ty) = matrix.head_ty().cloned() else { // The base case: there are no columns in the matrix. We are morally pattern-matching on (). // A row is useful iff it has no (unguarded) rows above it. let mut useful = true; // Whether the next row is useful. @@ -1456,41 +1432,44 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( }; debug!("ty: {ty:?}"); - let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level }; + let pcx = &PlaceCtxt { mcx, ty: &ty }; + let ctors_for_ty = pcx.ctors_for_ty()?; // Whether the place/column we are inspecting is known to contain valid data. let place_validity = matrix.place_validity[0]; - // For backwards compability we allow omitting some empty arms that we ideally shouldn't. - let place_validity = place_validity.allow_omitting_side_effecting_arms(); + // We treat match scrutinees of type `!` or `EmptyEnum` differently. + let is_toplevel_exception = + is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors); + // Whether empty patterns can be omitted for exhaustiveness. + let can_omit_empty_arms = is_toplevel_exception || mcx.tycx.is_exhaustive_patterns_feature_on(); + // Whether empty patterns are counted as useful or not. + let empty_arms_are_unreachable = place_validity.is_known_valid() && can_omit_empty_arms; // Analyze the constructors present in this column. let ctors = matrix.heads().map(|p| p.ctor()); - let ctors_for_ty = pcx.ctors_for_ty()?; - let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. - let split_set = ctors_for_ty.split(pcx, ctors); + let mut split_set = ctors_for_ty.split(ctors); let all_missing = split_set.present.is_empty(); - // Build the set of constructors we will specialize with. It must cover the whole type. + // We need to iterate over a full set of constructors, so we add `Missing` to represent the + // missing ones. This is explained under "Constructor Splitting" at the top of this file. let mut split_ctors = split_set.present; - if !split_set.missing.is_empty() { - // We need to iterate over a full set of constructors, so we add `Missing` to represent the - // missing ones. This is explained under "Constructor Splitting" at the top of this file. - split_ctors.push(Constructor::Missing); - } else if !split_set.missing_empty.is_empty() && !place_validity.is_known_valid() { - // The missing empty constructors are reachable if the place can contain invalid data. + if !(split_set.missing.is_empty() + && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable)) + { split_ctors.push(Constructor::Missing); } // Decide what constructors to report. + let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); let always_report_all = is_top_level && !is_integers; // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". let report_individual_missing_ctors = always_report_all || !all_missing; // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() => - // split_ctors.contains(Missing)`. The converse usually holds except in the - // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case. + // split_ctors.contains(Missing)`. The converse usually holds except when + // `!place_validity.is_known_valid()`. let mut missing_ctors = split_set.missing; - if !place_validity.allows_omitting_empty_arms() { - missing_ctors.extend(split_set.missing_empty); + if !can_omit_empty_arms { + missing_ctors.append(&mut split_set.missing_empty); } let mut ret = WitnessMatrix::empty(); @@ -1503,12 +1482,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty(); let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant); let mut witnesses = ensure_sufficient_stack(|| { - compute_exhaustiveness_and_usefulness( - mcx, - &mut spec_matrix, - overlapping_range_endpoints, - false, - ) + compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false) })?; // Transform witnesses for `spec_matrix` into witnesses for `matrix`. @@ -1537,12 +1511,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( && spec_matrix.rows.len() >= 2 && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty()) { - collect_overlapping_range_endpoints( - overlap_range, - matrix, - &spec_matrix, - overlapping_range_endpoints, - ); + collect_overlapping_range_endpoints(mcx, overlap_range, matrix, &spec_matrix); } } } @@ -1569,15 +1538,6 @@ pub enum Usefulness<'p, Cx: TypeCx> { Redundant, } -/// Indicates that the range `pat` overlapped with all the ranges in `overlaps_with`, where the -/// range they overlapped over is `overlaps_on`. We only detect singleton overlaps. -#[derive(Clone, Debug)] -pub struct OverlappingRanges<'p, Cx: TypeCx> { - pub pat: &'p DeconstructedPat<'p, Cx>, - pub overlaps_on: IntRange, - pub overlaps_with: Vec<&'p DeconstructedPat<'p, Cx>>, -} - /// The output of checking a match for exhaustiveness and arm usefulness. pub struct UsefulnessReport<'p, Cx: TypeCx> { /// For each arm of the input, whether that arm is useful after the arms above it. @@ -1585,25 +1545,19 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> { /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>, - pub overlapping_range_endpoints: Vec<OverlappingRanges<'p, Cx>>, } /// Computes whether a match is exhaustive and which of its arms are useful. #[instrument(skip(cx, arms), level = "debug")] pub fn compute_match_usefulness<'p, Cx: TypeCx>( - cx: MatchCtxt<'_, 'p, Cx>, + cx: MatchCtxt<'_, Cx>, arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { - let mut overlapping_range_endpoints = Vec::new(); let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity); - let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness( - cx, - &mut matrix, - &mut overlapping_range_endpoints, - true, - )?; + let non_exhaustiveness_witnesses = + compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?; let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); let arm_usefulness: Vec<_> = arms @@ -1621,9 +1575,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( }) .collect(); - Ok(UsefulnessReport { - arm_usefulness, - non_exhaustiveness_witnesses, - overlapping_range_endpoints, - }) + Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }) } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 88eb4603069..c57f22a0da2 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start field-offset = "0.3.5" -measureme = "10.0.0" +measureme = "11" rustc-rayon-core = { version = "0.5.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 1f09de0ed70..0f57688063e 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -891,7 +891,7 @@ impl<D: Deps> DepGraphData<D> { insertion for {dep_node:?}" ); - if !side_effects.is_empty() { + if side_effects.maybe_any() { qcx.dep_context().dep_graph().with_query_deserialization(|| { self.emit_side_effects(qcx, dep_node_index, side_effects) }); diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 504763f6cdb..f6f6aa07cb3 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -169,6 +169,7 @@ impl EdgeHeader { } } +#[inline] fn mask(bits: usize) -> usize { usize::MAX >> ((std::mem::size_of::<usize>() * 8) - bits) } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index eec0433ae68..e7eb9694f47 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,10 +1,8 @@ use crate::ich; use rustc_ast as ast; -use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::definitions::DefPathHash; use rustc_session::cstore::Untracked; @@ -23,7 +21,6 @@ pub struct StableHashingContext<'a> { // The value of `-Z incremental-ignore-spans`. // This field should only be used by `unstable_opts_incremental_ignore_span` incremental_ignore_spans: bool, - pub(super) body_resolver: BodyResolver<'a>, // Very often, we are hashing something that does not need the // `CachingSourceMapView`, so we initialize it lazily. raw_source_map: &'a SourceMap, @@ -31,26 +28,12 @@ pub struct StableHashingContext<'a> { hashing_controls: HashingControls, } -/// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`. -/// We could also just store a plain reference to the `hir::Crate` but we want -/// to avoid that the crate is used to get untracked access to all of the HIR. -#[derive(Clone, Copy)] -pub(super) enum BodyResolver<'tcx> { - Forbidden, - Ignore, - Traverse { - owner: hir::OwnerId, - bodies: &'tcx SortedMap<hir::ItemLocalId, &'tcx hir::Body<'tcx>>, - }, -} - impl<'a> StableHashingContext<'a> { #[inline] pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self { let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans; StableHashingContext { - body_resolver: BodyResolver::Forbidden, untracked, incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, caching_source_map: None, @@ -60,24 +43,6 @@ impl<'a> StableHashingContext<'a> { } #[inline] - pub fn without_hir_bodies(&mut self, f: impl FnOnce(&mut StableHashingContext<'_>)) { - f(&mut StableHashingContext { body_resolver: BodyResolver::Ignore, ..self.clone() }); - } - - #[inline] - pub fn with_hir_bodies( - &mut self, - owner: hir::OwnerId, - bodies: &SortedMap<hir::ItemLocalId, &hir::Body<'_>>, - f: impl FnOnce(&mut StableHashingContext<'_>), - ) { - f(&mut StableHashingContext { - body_resolver: BodyResolver::Traverse { owner, bodies }, - ..self.clone() - }); - } - - #[inline] pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) { let prev_hash_spans = self.hashing_controls.hash_spans; self.hashing_controls.hash_spans = hash_spans; diff --git a/compiler/rustc_query_system/src/ich/impls_hir.rs b/compiler/rustc_query_system/src/ich/impls_hir.rs deleted file mode 100644 index aa008d404c3..00000000000 --- a/compiler/rustc_query_system/src/ich/impls_hir.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! This module contains `HashStable` implementations for various HIR data -//! types in no particular order. - -use crate::ich::hcx::BodyResolver; -use crate::ich::StableHashingContext; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir as hir; - -impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { - #[inline] - fn hash_body_id(&mut self, id: hir::BodyId, hasher: &mut StableHasher) { - let hcx = self; - match hcx.body_resolver { - BodyResolver::Forbidden => panic!("Hashing HIR bodies is forbidden."), - BodyResolver::Ignore => {} - BodyResolver::Traverse { owner, bodies } => { - assert_eq!(id.hir_id.owner, owner); - bodies[&id.hir_id.local_id].hash_stable(hcx, hasher); - } - } - } -} diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index d170cd36ca6..5bd4fe04848 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -57,6 +57,8 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { } } +impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {} + impl<'a> HashStable<StableHashingContext<'a>> for SourceFile { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let SourceFile { diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 0a1c350b2db..86e3ecb1edd 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -4,7 +4,6 @@ pub use self::hcx::StableHashingContext; use rustc_span::symbol::{sym, Symbol}; mod hcx; -mod impls_hir; mod impls_syntax; pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 0240f012da0..674a0984ae9 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -2,8 +2,11 @@ use crate::dep_graph::DepNodeIndex; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sharded::{self, Sharded}; -use rustc_data_structures::sync::OnceLock; +use rustc_data_structures::sync::{Lock, OnceLock}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_index::{Idx, IndexVec}; +use rustc_span::def_id::DefId; +use rustc_span::def_id::DefIndex; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; @@ -148,6 +151,8 @@ where #[inline(always)] fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> { + // FIXME: lock_shard_by_hash will use high bits which are usually zero in the index() passed + // here. This makes sharding essentially useless, always selecting the zero'th shard. let lock = self.cache.lock_shard_by_hash(key.index() as u64); if let Some(Some(value)) = lock.get(*key) { Some(*value) } else { None } } @@ -168,3 +173,75 @@ where } } } + +pub struct DefIdCacheSelector; + +impl<'tcx, V: 'tcx> CacheSelector<'tcx, V> for DefIdCacheSelector { + type Cache = DefIdCache<V> + where + V: Copy; +} + +pub struct DefIdCache<V> { + /// Stores the local DefIds in a dense map. Local queries are much more often dense, so this is + /// a win over hashing query keys at marginal memory cost (~5% at most) compared to FxHashMap. + /// + /// The second element of the tuple is the set of keys actually present in the IndexVec, used + /// for faster iteration in `iter()`. + // FIXME: This may want to be sharded, like VecCache. However *how* to shard an IndexVec isn't + // super clear; VecCache is effectively not sharded today (see FIXME there). For now just omit + // that complexity here. + local: Lock<(IndexVec<DefIndex, Option<(V, DepNodeIndex)>>, Vec<DefIndex>)>, + foreign: DefaultCache<DefId, V>, +} + +impl<V> Default for DefIdCache<V> { + fn default() -> Self { + DefIdCache { local: Default::default(), foreign: Default::default() } + } +} + +impl<V> QueryCache for DefIdCache<V> +where + V: Copy, +{ + type Key = DefId; + type Value = V; + + #[inline(always)] + fn lookup(&self, key: &DefId) -> Option<(V, DepNodeIndex)> { + if key.krate == LOCAL_CRATE { + let cache = self.local.lock(); + cache.0.get(key.index).and_then(|v| *v) + } else { + self.foreign.lookup(key) + } + } + + #[inline] + fn complete(&self, key: DefId, value: V, index: DepNodeIndex) { + if key.krate == LOCAL_CRATE { + let mut cache = self.local.lock(); + let (cache, present) = &mut *cache; + let slot = cache.ensure_contains_elem(key.index, Default::default); + if slot.is_none() { + // FIXME: Only store the present set when running in incremental mode. `iter` is not + // used outside of saving caches to disk and self-profile. + present.push(key.index); + } + *slot = Some((value, index)); + } else { + self.foreign.complete(key, value, index) + } + } + + fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + let guard = self.local.lock(); + let (cache, present) = &*guard; + for &idx in present.iter() { + let value = cache[idx].unwrap(); + f(&DefId { krate: LOCAL_CRATE, index: idx }, &value.0, value.1); + } + self.foreign.iter(f); + } +} diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 9ff04c4e910..02b3c740b63 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -10,7 +10,8 @@ pub use self::job::{ mod caches; pub use self::caches::{ - CacheSelector, DefaultCacheSelector, QueryCache, SingleCacheSelector, VecCacheSelector, + CacheSelector, DefIdCacheSelector, DefaultCacheSelector, QueryCache, SingleCacheSelector, + VecCacheSelector, }; mod config; @@ -92,10 +93,13 @@ pub struct QuerySideEffects { } impl QuerySideEffects { + /// Returns true if there might be side effects. #[inline] - pub fn is_empty(&self) -> bool { + pub fn maybe_any(&self) -> bool { let QuerySideEffects { diagnostics } = self; - diagnostics.is_empty() + // Use `has_capacity` so that the destructor for `self.diagnostics` can be skipped + // if `maybe_any` is known to be false. + diagnostics.has_capacity() } pub fn append(&mut self, other: QuerySideEffects) { let QuerySideEffects { diagnostics } = self; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 3bb2cc5634f..9158ba00901 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -538,10 +538,9 @@ where prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - let diagnostics = diagnostics.into_inner(); - let side_effects = QuerySideEffects { diagnostics }; + let side_effects = QuerySideEffects { diagnostics: diagnostics.into_inner() }; - if std::intrinsics::unlikely(!side_effects.is_empty()) { + if std::intrinsics::unlikely(side_effects.maybe_any()) { if query.anon() { qcx.store_side_effects_for_anon_node(dep_node_index, side_effects); } else { diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 3f8df16e03f..c8ec10cad17 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -36,6 +36,10 @@ resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion = resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion = this would need to be a `{$suggestion}` +resolve_binding_in_never_pattern = + never patterns cannot contain variable bindings + .suggestion = use a wildcard `_` instead + resolve_binding_shadows_something_unacceptable = {$shadowing_binding}s cannot shadow {$shadowed_binding}s .label = cannot be named the same as {$article} {$shadowed_binding} diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index b5700661385..4a0c522b6ec 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -959,6 +959,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }), ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }), ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }), + ResolutionError::BindingInNeverPattern => { + self.dcx().create_err(errs::BindingInNeverPattern { span }) + } } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 1fdb193e571..821b1e946f3 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -486,6 +486,15 @@ pub(crate) struct LowercaseSelf { pub(crate) span: Span, } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag(resolve_binding_in_never_pattern)] +pub(crate) struct BindingInNeverPattern { + #[primary_span] + #[suggestion(code = "_", applicability = "machine-applicable", style = "short")] + pub(crate) span: Span, +} + #[derive(Diagnostic)] #[diag(resolve_trait_impl_duplicate, code = "E0201")] pub(crate) struct TraitImplDuplicate { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 82f50227911..abd724a6cc2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -16,7 +16,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; +use rustc_errors::{Applicability, DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -65,6 +65,8 @@ enum IsRepeatExpr { Yes, } +struct IsNeverPattern; + /// Describes whether an `AnonConst` is a type level const arg or /// some other form of anon const (i.e. inline consts or enum discriminants) #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -531,7 +533,7 @@ impl<'a> PathSource<'a> { } } - fn error_code(self, has_unexpected_resolution: bool) -> DiagnosticId { + fn error_code(self, has_unexpected_resolution: bool) -> String { use rustc_errors::error_code; match (self, has_unexpected_resolution) { (PathSource::Trait(_), true) => error_code!(E0404), @@ -3246,12 +3248,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.resolve_pattern_top(&local.pat, PatternSource::Let); } - /// build a map from pattern identifiers to binding-info's. - /// this is done hygienically. This could arise for a macro - /// that expands into an or-pattern where one 'x' was from the - /// user and one 'x' came from the macro. - fn binding_mode_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> { + /// Build a map from pattern identifiers to binding-info's, and check the bindings are + /// consistent when encountering or-patterns and never patterns. + /// This is done hygienically: this could arise for a macro that expands into an or-pattern + /// where one 'x' was from the user and one 'x' came from the macro. + /// + /// A never pattern by definition indicates an unreachable case. For example, matching on + /// `Result<T, &!>` could look like: + /// ```rust + /// # #![feature(never_type)] + /// # #![feature(never_patterns)] + /// # fn bar(_x: u32) {} + /// let foo: Result<u32, &!> = Ok(0); + /// match foo { + /// Ok(x) => bar(x), + /// Err(&!), + /// } + /// ``` + /// This extends to product types: `(x, !)` is likewise unreachable. So it doesn't make sense to + /// have a binding here, and we tell the user to use `_` instead. + fn compute_and_check_binding_map( + &mut self, + pat: &Pat, + ) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> { let mut binding_map = FxIndexMap::default(); + let mut is_never_pat = false; pat.walk(&mut |pat| { match pat.kind { @@ -3263,18 +3284,27 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PatKind::Or(ref ps) => { // Check the consistency of this or-pattern and // then add all bindings to the larger map. - for bm in self.check_consistent_bindings(ps) { - binding_map.extend(bm); + match self.compute_and_check_or_pat_binding_map(ps) { + Ok(bm) => binding_map.extend(bm), + Err(IsNeverPattern) => is_never_pat = true, } return false; } + PatKind::Never => is_never_pat = true, _ => {} } true }); - binding_map + if is_never_pat { + for (_, binding) in binding_map { + self.report_error(binding.span, ResolutionError::BindingInNeverPattern); + } + Err(IsNeverPattern) + } else { + Ok(binding_map) + } } fn is_base_res_local(&self, nid: NodeId) -> bool { @@ -3284,33 +3314,52 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ) } - /// Checks that all of the arms in an or-pattern have exactly the - /// same set of bindings, with the same binding modes for each. - fn check_consistent_bindings( + /// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern + /// have exactly the same set of bindings, with the same binding modes for each. + /// Returns the computed binding map and a boolean indicating whether the pattern is a never + /// pattern. + /// + /// A never pattern by definition indicates an unreachable case. For example, destructuring a + /// `Result<T, &!>` could look like: + /// ```rust + /// # #![feature(never_type)] + /// # #![feature(never_patterns)] + /// # fn foo() -> Result<bool, &'static !> { Ok(true) } + /// let (Ok(x) | Err(&!)) = foo(); + /// # let _ = x; + /// ``` + /// Because the `Err(&!)` branch is never reached, it does not need to have the same bindings as + /// the other branches of the or-pattern. So we must ignore never pattern when checking the + /// bindings of an or-pattern. + /// Moreover, if all the subpatterns are never patterns (e.g. `Ok(!) | Err(!)`), then the + /// pattern as a whole counts as a never pattern (since it's definitionallly unreachable). + fn compute_and_check_or_pat_binding_map( &mut self, pats: &[P<Pat>], - ) -> Vec<FxIndexMap<Ident, BindingInfo>> { - // pats is consistent. + ) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> { let mut missing_vars = FxIndexMap::default(); let mut inconsistent_vars = FxIndexMap::default(); - // 1) Compute the binding maps of all arms. - let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>(); + // 1) Compute the binding maps of all arms; we must ignore never patterns here. + let not_never_pats = pats + .iter() + .filter_map(|pat| { + let binding_map = self.compute_and_check_binding_map(pat).ok()?; + Some((binding_map, pat)) + }) + .collect::<Vec<_>>(); // 2) Record any missing bindings or binding mode inconsistencies. - for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) { + for (map_outer, pat_outer) in not_never_pats.iter() { // Check against all arms except for the same pattern which is always self-consistent. - let inners = maps + let inners = not_never_pats .iter() - .zip(pats.iter()) .filter(|(_, pat)| pat.id != pat_outer.id) - .flat_map(|(map, _)| map) - .map(|(key, binding)| (key.name, map_outer.get(key), binding)); - - let inners = inners.collect::<Vec<_>>(); + .flat_map(|(map, _)| map); - for (name, info, &binding_inner) in inners { - match info { + for (key, binding_inner) in inners { + let name = key.name; + match map_outer.get(key) { None => { // The inner binding is missing in the outer. let binding_error = @@ -3351,19 +3400,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1)); } - // 5) Finally bubble up all the binding maps. - maps + // 5) Bubble up the final binding map. + if not_never_pats.is_empty() { + // All the patterns are never patterns, so the whole or-pattern is one too. + Err(IsNeverPattern) + } else { + let mut binding_map = FxIndexMap::default(); + for (bm, _) in not_never_pats { + binding_map.extend(bm); + } + Ok(binding_map) + } } - /// Check the consistency of the outermost or-patterns. - fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) { + /// Check the consistency of bindings wrt or-patterns and never patterns. + fn check_consistent_bindings(&mut self, pat: &'ast Pat) { + let mut is_or_or_never = false; pat.walk(&mut |pat| match pat.kind { - PatKind::Or(ref ps) => { - self.check_consistent_bindings(ps); + PatKind::Or(..) | PatKind::Never => { + is_or_or_never = true; false } _ => true, - }) + }); + if is_or_or_never { + let _ = self.compute_and_check_binding_map(pat); + } } fn resolve_arm(&mut self, arm: &'ast Arm) { @@ -3392,7 +3454,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { visit::walk_pat(self, pat); self.resolve_pattern_inner(pat, pat_src, bindings); // This has to happen *after* we determine which pat_idents are variants: - self.check_consistent_bindings_top(pat); + self.check_consistent_bindings(pat); } /// Resolve bindings in a pattern. This is a helper to `resolve_pattern`. @@ -3759,7 +3821,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { swap(&mut err.span, &mut parent_err.span); err.children = take(&mut parent_err.children); err.sort_span = parent_err.sort_span; - err.is_lint = parent_err.is_lint; + err.is_lint = parent_err.is_lint.clone(); // merge the parent's suggestions with the typo suggestions fn append_result<T, E>(res1: &mut Result<Vec<T>, E>, res2: Result<Vec<T>, E>) { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 58ff4d8c793..abb0a7a465a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1745,12 +1745,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // Doing analysis on local `DefId`s would cause infinite recursion. return; } + let Ok(impls) = self.r.tcx.inherent_impls(def_id) else { return }; // Look at all the associated functions without receivers in the type's // inherent impls to look for builders that return `Self` - let mut items = self - .r - .tcx - .inherent_impls(def_id) + let mut items = impls .iter() .flat_map(|i| self.r.tcx.associated_items(i).in_definition_order()) // Only assoc fn with no receivers. @@ -2569,8 +2567,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { debug!(?param.ident, ?param.ident.span, ?use_span); let elidable = matches!(use_ctxt, LifetimeCtxt::Ref); + let deletion_span = + if param.bounds.is_empty() { deletion_span() } else { None }; - let deletion_span = deletion_span(); self.r.lint_buffer.buffer_lint_with_diagnostic( lint::builtin::SINGLE_USE_LIFETIMES, param.id, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 90aa7d79bf0..341c566d97f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -257,7 +257,7 @@ enum ResolutionError<'a> { kind: &'static str, trait_path: String, trait_item_span: Span, - code: rustc_errors::DiagnosticId, + code: String, }, /// Error E0201: multiple impl items for the same trait item. TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, @@ -265,6 +265,8 @@ enum ResolutionError<'a> { InvalidAsmSym, /// `self` used instead of `Self` in a generic parameter LowercaseSelf, + /// A never pattern has a binding. + BindingInNeverPattern, } enum VisResolutionError<'a> { diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 35275821453..48bee4cd2f0 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -16,7 +16,6 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(ptr_sub_ptr)] -#![feature(slice_first_last_chunk)] #![cfg_attr(test, feature(test))] #![allow(rustc::internal)] #![deny(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 53bdef6dfa0..af8c962a2ed 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -75,8 +75,6 @@ session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be u session_not_supported = not supported -session_nul_in_c_str = null characters in C string literals are not supported - session_octal_float_literal_not_supported = octal float literal is not supported session_optimization_fuel_exhausted = optimization-fuel-exhausted: {$msg} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fe1166457ba..e751ff13a34 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -533,21 +533,6 @@ impl Default for ErrorOutputType { } } -/// Parameter to control path trimming. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] -pub enum TrimmedDefPaths { - /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive - /// query. - #[default] - Never, - /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call - /// `good_path_delayed_bug`. - Always, - /// `try_print_trimmed_def_path` calls the expensive query, the query calls - /// `good_path_delayed_bug`. - GoodPath, -} - #[derive(Clone, Hash, Debug)] pub enum ResolveDocLinks { /// Do not resolve doc links. @@ -1089,7 +1074,7 @@ impl Default for Options { debug_assertions: true, actually_rustdoc: false, resolve_doc_links: ResolveDocLinks::None, - trimmed_def_paths: TrimmedDefPaths::default(), + trimmed_def_paths: false, cli_forced_codegen_units: None, cli_forced_local_thinlto_off: false, remap_path_prefix: Vec::new(), @@ -2926,7 +2911,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M debug_assertions, actually_rustdoc: false, resolve_doc_links: ResolveDocLinks::ExportedMetadata, - trimmed_def_paths: TrimmedDefPaths::default(), + trimmed_def_paths: false, cli_forced_codegen_units: codegen_units, cli_forced_local_thinlto_off: disable_local_thinlto, remap_path_prefix, @@ -3205,12 +3190,12 @@ pub enum WasiExecModel { /// how the hash should be calculated when adding a new command-line argument. pub(crate) mod dep_tracking { use super::{ - BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression, - ErrorOutputType, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, - LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy, OptLevel, - OutFileName, OutputType, OutputTypes, Polonius, RemapPathScopeComponents, ResolveDocLinks, - SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, - TrimmedDefPaths, WasiExecModel, + BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, 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, }; use crate::lint; use crate::utils::NativeLib; @@ -3289,6 +3274,7 @@ pub(crate) mod dep_tracking { LtoCli, DebugInfo, DebugInfoCompression, + CollapseMacroDebuginfo, UnstableFeatures, NativeLib, SanitizerSet, @@ -3305,7 +3291,6 @@ pub(crate) mod dep_tracking { SymbolManglingVersion, RemapPathScopeComponents, SourceFileHashAlgorithm, - TrimmedDefPaths, OutFileName, OutputType, RealFileName, @@ -3453,6 +3438,25 @@ pub enum ProcMacroExecutionStrategy { CrossThread, } +/// How to perform collapse macros debug info +/// if-ext - if macro from different crate (related to callsite code) +/// | cmd \ attr | no | (unspecified) | external | yes | +/// | no | no | no | no | no | +/// | (unspecified) | no | no | if-ext | yes | +/// | external | no | if-ext | if-ext | yes | +/// | yes | yes | yes | yes | yes | +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CollapseMacroDebuginfo { + /// Don't collapse debuginfo for the macro + No = 0, + /// Unspecified value + Unspecified = 1, + /// Collapse debuginfo if the macro comes from a different crate + External = 2, + /// Collapse debuginfo for the macro + Yes = 3, +} + /// Which format to use for `-Z dump-mono-stats` #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum DumpMonoStatsFormat { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 21a206798af..e19f0fd84de 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -6,7 +6,7 @@ use rustc_errors::{ error_code, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, IntoDiagnostic, Level, MultiSpan, }; use rustc_macros::Diagnostic; -use rustc_span::{BytePos, Span, Symbol}; +use rustc_span::{Span, Symbol}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; use crate::parse::ParseSess; @@ -346,13 +346,6 @@ pub(crate) struct BinaryFloatLiteralNotSupported { pub span: Span, } -#[derive(Diagnostic)] -#[diag(session_nul_in_c_str)] -pub(crate) struct NulInCStr { - #[primary_span] - pub span: Span, -} - pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { @@ -432,12 +425,6 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: }; dcx.emit_err(IntLiteralTooLarge { span, limit }); } - LitError::NulInCStr(range) => { - let lo = BytePos(span.lo().0 + range.start as u32 + 2); - let hi = BytePos(span.lo().0 + range.end as u32 + 2); - let span = span.with_lo(lo).with_hi(hi); - dcx.emit_err(NulInCStr { span }); - } } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 2d91a3fbd91..1337ade62c0 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -183,7 +183,7 @@ top_level_options!( resolve_doc_links: ResolveDocLinks [TRACKED], /// Control path trimming. - trimmed_def_paths: TrimmedDefPaths [TRACKED], + trimmed_def_paths: bool [TRACKED], /// Specifications of codegen units / ThinLTO which are forced as a /// result of parsing command line options. These are not necessarily @@ -388,6 +388,7 @@ mod desc { pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; pub const parse_debuginfo: &str = "either an integer (0, 1, 2), `none`, `line-directives-only`, `line-tables-only`, `limited`, or `full`"; pub const parse_debuginfo_compression: &str = "one of `none`, `zlib`, or `zstd`"; + pub const parse_collapse_macro_debuginfo: &str = "one of `no`, `external`, or `yes`"; pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; @@ -1302,6 +1303,19 @@ mod parse { true } + pub(crate) fn parse_collapse_macro_debuginfo( + slot: &mut CollapseMacroDebuginfo, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("no") => CollapseMacroDebuginfo::No, + Some("external") => CollapseMacroDebuginfo::External, + Some("yes") => CollapseMacroDebuginfo::Yes, + _ => return false, + }; + true + } + pub(crate) fn parse_proc_macro_execution_strategy( slot: &mut ProcMacroExecutionStrategy, v: Option<&str>, @@ -1534,6 +1548,9 @@ options! { "instrument control-flow architecture protection"), codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], "the backend to use"), + collapse_macro_debuginfo: CollapseMacroDebuginfo = (CollapseMacroDebuginfo::Unspecified, + parse_collapse_macro_debuginfo, [TRACKED], + "set option to collapse debuginfo for macros"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED], @@ -1724,6 +1741,8 @@ options! { "run all passes except codegen; no output"), no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED], "omit DWARF address ranges that give faster lookups"), + no_implied_bounds_compat: bool = (false, parse_bool, [TRACKED], + "disable the compatibility version of the `implied_bounds_ty` query"), no_jump_tables: bool = (false, parse_no_flag, [TRACKED], "disable the jump tables and lookup tables that can be generated from a switch case lowering"), no_leak_check: bool = (false, parse_no_flag, [UNTRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index c629c9884c8..16036e5be38 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -15,8 +15,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, DiagCtxt}; use rustc_errors::{ - fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - MultiSpan, StashKey, + fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan, StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -148,7 +147,7 @@ pub fn feature_warn_issue( // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; let future_incompatible = lint.future_incompatible.as_ref().unwrap(); - err.code(DiagnosticId::Lint { name: lint.name_lower(), has_future_breakage: false }); + err.is_lint(lint.name_lower(), /* has_future_breakage */ false); err.warn(lint.desc); err.note(format!("for more information, see {}", future_incompatible.reference)); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1c3d5a3cb88..cba6ce0d235 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -14,15 +14,16 @@ use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread}; +use rustc_data_structures::sync::{ + AtomicU64, DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, +}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ - error_code, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticId, - DiagnosticMessage, ErrorGuaranteed, FatalAbort, FluentBundle, IntoDiagnostic, - LazyFallbackBundle, TerminalUrl, + error_code, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, + ErrorGuaranteed, FatalAbort, FluentBundle, IntoDiagnostic, LazyFallbackBundle, TerminalUrl, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -36,7 +37,6 @@ use rustc_target::spec::{ }; use std::any::Any; -use std::cell::{self, RefCell}; use std::env; use std::fmt; use std::ops::{Div, Mul}; @@ -150,7 +150,7 @@ pub struct Session { /// Input, input file path and output file path to this compilation process. pub io: CompilerIO, - incr_comp_session: OneThread<RefCell<IncrCompSession>>, + incr_comp_session: RwLock<IncrCompSession>, /// Used by `-Z self-profile`. pub prof: SelfProfilerRef, @@ -534,9 +534,9 @@ impl Session { *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; } - pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + pub fn incr_comp_session_dir(&self) -> MappedReadGuard<'_, PathBuf> { let incr_comp_session = self.incr_comp_session.borrow(); - cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { + ReadGuard::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { IncrCompSession::NotInitialized => panic!( "trying to get session directory from `IncrCompSession`: {:?}", *incr_comp_session, @@ -549,7 +549,7 @@ impl Session { }) } - pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<'_, PathBuf>> { + pub fn incr_comp_session_dir_opt(&self) -> Option<MappedReadGuard<'_, PathBuf>> { self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) } @@ -906,7 +906,7 @@ impl Session { CodegenUnits::Default(16) } - pub fn teach(&self, code: &DiagnosticId) -> bool { + pub fn teach(&self, code: &str) -> bool { self.opts.unstable_opts.teach && self.dcx().must_teach(code) } @@ -1177,7 +1177,7 @@ pub fn build_session( parse_sess, sysroot, io, - incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), + incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, code_stats: Default::default(), optimization_fuel, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 4bac98909ad..b99640d2f2d 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -190,35 +190,120 @@ where stable_mir::compiler_interface::run(&tables, || init(&tables, f)) } +/// Instantiate and run the compiler with the provided arguments and callback. +/// +/// The callback will be invoked after the compiler ran all its analyses, but before code generation. +/// Note that this macro accepts two different formats for the callback: +/// 1. An ident that resolves to a function that accepts no argument and returns `ControlFlow<B, C>` +/// ```ignore(needs-extern-crate) +/// # extern crate rustc_driver; +/// # extern crate rustc_interface; +/// # #[macro_use] +/// # extern crate rustc_smir; +/// # extern crate stable_mir; +/// # +/// # fn main() { +/// # use std::ops::ControlFlow; +/// # use stable_mir::CompilerError; +/// fn analyze_code() -> ControlFlow<(), ()> { +/// // Your code goes in here. +/// # ControlFlow::Continue(()) +/// } +/// # let args = vec!["--verbose".to_string()]; +/// let result = run!(args, analyze_code); +/// # assert_eq!(result, Err(CompilerError::Skipped)) +/// # } +/// ``` +/// 2. A closure expression: +/// ```ignore(needs-extern-crate) +/// # extern crate rustc_driver; +/// # extern crate rustc_interface; +/// # #[macro_use] +/// # extern crate rustc_smir; +/// # extern crate stable_mir; +/// # +/// # fn main() { +/// # use std::ops::ControlFlow; +/// # use stable_mir::CompilerError; +/// fn analyze_code(extra_args: Vec<String>) -> ControlFlow<(), ()> { +/// # let _ = extra_args; +/// // Your code goes in here. +/// # ControlFlow::Continue(()) +/// } +/// # let args = vec!["--verbose".to_string()]; +/// # let extra_args = vec![]; +/// let result = run!(args, || analyze_code(extra_args)); +/// # assert_eq!(result, Err(CompilerError::Skipped)) +/// # } +/// ``` #[macro_export] macro_rules! run { + ($args:expr, $callback_fn:ident) => { + run_driver!($args, || $callback_fn()) + }; + ($args:expr, $callback:expr) => { + run_driver!($args, $callback) + }; +} + +/// Instantiate and run the compiler with the provided arguments and callback. +/// +/// This is similar to `run` but it invokes the callback with the compiler's `TyCtxt`, +/// which can be used to invoke internal APIs. +#[macro_export] +macro_rules! run_with_tcx { + ($args:expr, $callback_fn:ident) => { + run_driver!($args, |tcx| $callback_fn(tcx), with_tcx) + }; ($args:expr, $callback:expr) => { - run!($args, tcx, $callback) + run_driver!($args, $callback, with_tcx) + }; +} + +/// Optionally include an ident. This is needed due to macro hygiene. +#[macro_export] +#[doc(hidden)] +macro_rules! optional { + (with_tcx $ident:ident) => { + $ident }; - ($args:expr, $tcx:ident, $callback:expr) => {{ +} + +/// Prefer using [run!] and [run_with_tcx] instead. +/// +/// This macro implements the instantiation of a StableMIR driver, and it will invoke +/// the given callback after the compiler analyses. +/// +/// The third argument determines whether the callback requires `tcx` as an argument. +#[macro_export] +#[doc(hidden)] +macro_rules! run_driver { + ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ use rustc_driver::{Callbacks, Compilation, RunCompiler}; use rustc_interface::{interface, Queries}; use stable_mir::CompilerError; use std::ops::ControlFlow; - pub struct StableMir<B = (), C = ()> + pub struct StableMir<B = (), C = (), F = fn($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C>> where B: Send, C: Send, + F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send, { args: Vec<String>, - callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>, + callback: Option<F>, result: Option<ControlFlow<B, C>>, } - impl<B, C> StableMir<B, C> + impl<B, C, F> StableMir<B, C, F> where B: Send, C: Send, + F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send, { /// Creates a new `StableMir` instance, with given test_function and arguments. - pub fn new(args: Vec<String>, callback: fn(TyCtxt<'_>) -> ControlFlow<B, C>) -> Self { - StableMir { args, callback, result: None } + pub fn new(args: Vec<String>, callback: F) -> Self { + StableMir { args, callback: Some(callback), result: None } } /// Runs the compiler against given target and tests it with `test_function` @@ -238,10 +323,11 @@ macro_rules! run { } } - impl<B, C> Callbacks for StableMir<B, C> + impl<B, C, F> Callbacks for StableMir<B, C, F> where B: Send, C: Send, + F: FnOnce($(optional!($with_tcx TyCtxt))?) -> ControlFlow<B, C> + Send, { /// Called after analysis. Return value instructs the compiler whether to /// continue the compilation afterwards (defaults to `Compilation::Continue`) @@ -251,20 +337,24 @@ macro_rules! run { queries: &'tcx Queries<'tcx>, ) -> Compilation { queries.global_ctxt().unwrap().enter(|tcx| { - rustc_internal::run(tcx, || { - self.result = Some((self.callback)(tcx)); - }) - .unwrap(); - if self.result.as_ref().is_some_and(|val| val.is_continue()) { - Compilation::Continue + if let Some(callback) = self.callback.take() { + rustc_internal::run(tcx, || { + self.result = Some(callback($(optional!($with_tcx tcx))?)); + }) + .unwrap(); + if self.result.as_ref().is_some_and(|val| val.is_continue()) { + Compilation::Continue + } else { + Compilation::Stop + } } else { - Compilation::Stop + Compilation::Continue } }) } } - StableMir::new($args, |$tcx| $callback).run() + StableMir::new($args, $callback).run() }}; } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c5fb6f7a26f..2f3d9d69b85 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -609,7 +609,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { fn_span: _, } => TerminatorKind::Call { func: func.stable(tables), - args: args.iter().map(|arg| arg.stable(tables)).collect(), + args: args.iter().map(|arg| arg.node.stable(tables)).collect(), destination: destination.stable(tables), target: target.map(|t| t.as_usize()), unwind: unwind.stable(tables), diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d03965b539c..3d26efec5a6 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -330,7 +330,7 @@ pub(crate) struct HygieneData { /// would have collisions without a disambiguator. /// The keys of this map are always computed with `ExpnData.disambiguator` /// set to 0. - expn_data_disambiguators: FxHashMap<Hash64, u32>, + expn_data_disambiguators: UnhashMap<Hash64, u32>, } impl HygieneData { @@ -359,7 +359,7 @@ impl HygieneData { dollar_crate_name: kw::DollarCrate, }], syntax_context_map: FxHashMap::default(), - expn_data_disambiguators: FxHashMap::default(), + expn_data_disambiguators: UnhashMap::default(), } } @@ -464,12 +464,15 @@ impl HygieneData { &self, mut span: Span, to: Span, - collapse_debuginfo_enabled: bool, + collapse_debuginfo_feature_enabled: bool, ) -> Span { let orig_span = span; let mut ret_span = span; - debug!("walk_chain_collapsed({:?}, {:?})", span, to); + debug!( + "walk_chain_collapsed({:?}, {:?}), feature_enable={}", + span, to, collapse_debuginfo_feature_enabled, + ); debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt()); while !span.eq_ctxt(to) && span.from_expansion() { let outer_expn = self.outer_expn(span.ctxt()); @@ -477,7 +480,7 @@ impl HygieneData { let expn_data = self.expn_data(outer_expn); debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data); span = expn_data.call_site; - if !collapse_debuginfo_enabled || expn_data.collapse_debuginfo { + if !collapse_debuginfo_feature_enabled || expn_data.collapse_debuginfo { ret_span = span; } } @@ -601,8 +604,14 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } -pub fn walk_chain_collapsed(span: Span, to: Span, collapse_debuginfo_enabled: bool) -> Span { - HygieneData::with(|hdata| hdata.walk_chain_collapsed(span, to, collapse_debuginfo_enabled)) +pub fn walk_chain_collapsed( + span: Span, + to: Span, + collapse_debuginfo_feature_enabled: bool, +) -> Span { + HygieneData::with(|hdata| { + hdata.walk_chain_collapsed(span, to, collapse_debuginfo_feature_enabled) + }) } pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 8253ffefcaa..df7635e447d 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -10,8 +10,8 @@ //! information, source code snippets, etc. use crate::*; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{IntoDynSyncSend, MappedReadGuard, ReadGuard, RwLock}; +use rustc_data_structures::unhash::UnhashMap; use std::fs; use std::io::{self, BorrowedBuf, Read}; use std::path::{self}; @@ -70,7 +70,7 @@ mod monotonic { impl<T> !DerefMut for MonotonicVec<T> {} } -#[derive(Clone, Encodable, Decodable, Debug, Copy, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, Copy, PartialEq, Hash, HashStable_Generic)] pub struct Spanned<T> { pub node: T, pub span: Span, @@ -164,7 +164,7 @@ impl FileLoader for RealFileLoader { #[derive(Default)] struct SourceMapFiles { source_files: monotonic::MonotonicVec<Lrc<SourceFile>>, - stable_id_to_source_file: FxHashMap<StableSourceFileId, Lrc<SourceFile>>, + stable_id_to_source_file: UnhashMap<StableSourceFileId, Lrc<SourceFile>>, } pub struct SourceMap { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72b62a795fc..7b0138d50ba 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -749,6 +749,7 @@ symbols! { extern_in_paths, extern_prelude, extern_types, + external, external_doc, f, f16c_target_feature, @@ -1152,6 +1153,7 @@ symbols! { offset, offset_of, offset_of_enum, + offset_of_nested, ok_or_else, omit_gdb_pretty_printer_section, on, @@ -1811,6 +1813,7 @@ symbols! { xmm_reg, yeet_desugar_details, yeet_expr, + yes, yield_expr, ymm_reg, zmm_reg, diff --git a/compiler/rustc_target/src/spec/base/freebsd.rs b/compiler/rustc_target/src/spec/base/freebsd.rs index 8c141aaaec3..80b3da8a752 100644 --- a/compiler/rustc_target/src/spec/base/freebsd.rs +++ b/compiler/rustc_target/src/spec/base/freebsd.rs @@ -6,6 +6,7 @@ pub fn opts() -> TargetOptions { dynamic_linking: true, families: cvs!["unix"], has_rpath: true, + crt_static_respected: true, position_independent_executables: true, relro_level: RelroLevel::Full, abi_return_struct_as_int: true, diff --git a/compiler/rustc_target/src/spec/targets/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/asmjs_unknown_emscripten.rs deleted file mode 100644 index e4768c67af5..00000000000 --- a/compiler/rustc_target/src/spec/targets/asmjs_unknown_emscripten.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::spec::{targets::wasm32_unknown_emscripten, LinkerFlavor, Target}; - -pub fn target() -> Target { - let mut target = wasm32_unknown_emscripten::target(); - target.add_post_link_args(LinkerFlavor::EmCc, &["-sWASM=0", "--memory-init-file", "0"]); - target -} diff --git a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs index 13f8b6b5a0a..a221538b902 100644 --- a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: ios_sim_llvm_target(arch).into(), pointer_width: 32, data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:128-n8:16:32-S128" + i128:128-f64:32:64-f80:128-n8:16:32-S128" .into(), arch: arch.target_arch(), options: TargetOptions { max_atomic_width: Some(64), ..opts("ios", arch) }, diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs index 3ebf4bcf523..ea8b919e748 100644 --- a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { llvm_target: "i586-pc-unknown".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: TargetOptions { diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_netbsd.rs index 8375fa4c0c0..574dc658b78 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_netbsd.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { llvm_target: "i586-unknown-netbsdelf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: TargetOptions { mcount: "__mcount".into(), ..base }, diff --git a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs index 59069fe4e3a..25617b4436c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { llvm_target: macos_llvm_target(Arch::I686).into(), pointer_width: 32, data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:128-n8:16:32-S128" + i128:128-f64:32:64-f80:128-n8:16:32-S128" .into(), arch: arch.target_arch(), options: TargetOptions { mcount: "\u{1}mcount".into(), ..base }, diff --git a/compiler/rustc_target/src/spec/targets/i686_linux_android.rs b/compiler/rustc_target/src/spec/targets/i686_linux_android.rs index 79471040f0d..12ebf0c72f8 100644 --- a/compiler/rustc_target/src/spec/targets/i686_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/i686_linux_android.rs @@ -17,7 +17,7 @@ pub fn target() -> Target { llvm_target: "i686-linux-android".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: TargetOptions { supported_sanitizers: SanitizerSet::ADDRESS, ..base }, diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index 8d4a39b5811..11818034414 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -19,7 +19,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-gnu".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs index bb410cd8caf..f4e33b88178 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-gnu".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs index 5abc3017bf8..9f1c8f4676c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs @@ -25,7 +25,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:128-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs index 927b2ab877d..d90f481c68c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-freebsd".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs index bc7fd6fbc68..330a390c981 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-haiku".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs index 6884e078c27..c32f7177c1a 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-hurd-gnu".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs index 3b7be48dbbc..9bc38a72f4d 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-linux-gnu".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index ef58b4fb458..4a82e0986c7 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -25,7 +25,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-linux-musl".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs index 5f3afbe5afd..ea72656607e 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-netbsdelf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: TargetOptions { mcount: "__mcount".into(), ..base }, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs index f44584a10be..945dc0f8967 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-openbsd".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs index 25315e19cdd..37d57c1cdf3 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs @@ -80,7 +80,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-windows-gnu".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs index ec211a710eb..0830033dc23 100644 --- a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-gnu".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs index 7cca2fc6b9f..534dd6ee54f 100644 --- a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:128-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index 5b91682e168..35ad81dc052 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -25,7 +25,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".into(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:128-n8:16:32-a:0:32-S32" + i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs index ee501c5165e..30c2295e402 100644 --- a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-linux-gnu".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - f64:32:64-f80:32-n8:16:32-S128" + i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index e2e707f4a1a..90df3c69684 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { Target { llvm_target: "powerpc64-unknown-linux-musl".into(), pointer_width: 64, - data_layout: "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), + data_layout: "E-m:e-Fn32-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), arch: "powerpc64".into(), options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs index 59df3937ea6..356c6721868 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs @@ -17,8 +17,8 @@ pub fn target() -> Target { // correctly, we do too. llvm_target: macos_llvm_target(arch).into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { mcount: "\u{1}mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs index d0e2ac44a2a..55165ea4ec6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs @@ -9,8 +9,8 @@ pub fn target() -> Target { Target { llvm_target: ios_sim_llvm_target(arch).into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { max_atomic_width: Some(128), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs index 8ef4b88b8b1..ff21e489333 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs @@ -12,8 +12,8 @@ pub fn target() -> Target { Target { llvm_target: llvm_target.into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { max_atomic_width: Some(128), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs index 17efd437f2f..20b01612553 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs @@ -6,8 +6,8 @@ pub fn target() -> Target { Target { llvm_target: tvos_sim_llvm_target(arch).into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { max_atomic_width: Some(128), ..opts("tvos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs index b1f72ee2f21..806a58e980b 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs @@ -6,8 +6,8 @@ pub fn target() -> Target { Target { llvm_target: watchos_sim_llvm_target(arch).into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { max_atomic_width: Some(128), ..opts("watchos", arch) }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs index f4117edc3ff..c38a1a08536 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs @@ -75,8 +75,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-elf".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: opts, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/targets/x86_64_linux_android.rs index 0e6d41abc9b..30b1ee73630 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_linux_android.rs @@ -16,8 +16,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-linux-android".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: TargetOptions { supported_sanitizers: SanitizerSet::ADDRESS, ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 93aec4c425a..0ef01597399 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -4,8 +4,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-unknown".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: TargetOptions { cpu: "x86-64".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs index f15ad8dda63..ade4dd6d431 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs @@ -13,8 +13,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-solaris".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs index 6d3e0727051..9e964d248bf 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs @@ -16,8 +16,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-windows-gnu".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs index a56ebfa585e..1facf9450cd 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs @@ -11,8 +11,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-windows-gnu".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs index 3a4da91c244..357261073a8 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs @@ -10,8 +10,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-windows-msvc".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs index e6159fca3ae..6c9d1147548 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unikraft_linux_musl.rs @@ -5,8 +5,8 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-linux-musl".into(), pointer_width: 64, arch: "x86_64".into(), - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), options: TargetOptions { cpu: "x86-64".into(), plt_by_default: false, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_dragonfly.rs index 80adb8fa2d9..ce682bb8005 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_dragonfly.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_dragonfly.rs @@ -11,8 +11,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-dragonfly".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_freebsd.rs index fa321035367..6d6940e7891 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_freebsd.rs @@ -14,8 +14,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-freebsd".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs index 5b19ed1b5ff..50139e5c1d8 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs @@ -12,8 +12,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-fuchsia".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_haiku.rs index 0f927be962b..c00c689f7ef 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_haiku.rs @@ -13,8 +13,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-haiku".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_hermit.rs index df191f515bd..fb46848ba8c 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_hermit.rs @@ -5,8 +5,8 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-hermit".into(), pointer_width: 64, arch: "x86_64".into(), - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), options: TargetOptions { cpu: "x86-64".into(), features: "+rdrnd,+rdseed".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_illumos.rs index eb2b13cb5c2..3e3f4921322 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_illumos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_illumos.rs @@ -13,8 +13,8 @@ pub fn target() -> Target { // so we still pass Solaris to it llvm_target: "x86_64-pc-solaris".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs index 7b86fe738b1..646623668c6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_l4re_uclibc.rs @@ -10,8 +10,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-l4re-uclibc".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index bf10f7e5d2d..2296b58f45d 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -19,8 +19,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-gnu".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs index 1856c6afd52..d4b6519262e 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs @@ -16,7 +16,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-linux-gnux32".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:128-n8:16:32:64-S128" + i64:64-i128:128-f80:128-n8:16:32:64-S128" .into(), arch: "x86_64".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs index 8dc5503e336..c71dc65670c 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_musl.rs @@ -18,8 +18,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-musl".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs index 35862656aa2..c25105f6e35 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs @@ -18,8 +18,8 @@ pub fn target() -> Target { // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. llvm_target: "x86_64-unknown-linux-musl".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_netbsd.rs index e2cee0513ba..466498acb0b 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_netbsd.rs @@ -19,8 +19,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-netbsd".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: TargetOptions { mcount: "__mcount".into(), ..base }, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs index 8f7655d8ccd..ceded7790a6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs @@ -30,8 +30,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-none-elf".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: opts, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_openbsd.rs index 1133b50f3d2..b542a569bbd 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_openbsd.rs @@ -12,8 +12,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-openbsd".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_redox.rs index c1f57311292..550b7a3d282 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_redox.rs @@ -11,8 +11,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-redox".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs index 5abfb8162f7..95847e57a0f 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs @@ -32,8 +32,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-windows".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs index a46d3a0e27b..c2981ddbad6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs @@ -15,8 +15,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-windows-gnu".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs index a3bc27aa065..3f0702c7ad6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_msvc.rs @@ -9,8 +9,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-pc-windows-msvc".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs index 5a59839ebc6..fd26b6e8cae 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs @@ -10,8 +10,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-win7-windows-msvc".into(), pointer_width: 64, - data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs index 765239bdd39..f7a78b48f95 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs @@ -12,8 +12,8 @@ pub fn target() -> Target { Target { llvm_target: "x86_64-unknown-linux-gnu".into(), pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: "x86_64".into(), options: base, } diff --git a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs index 0c731e369eb..e0e4cb93e31 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs @@ -35,8 +35,8 @@ pub fn target() -> Target { // correctly, we do too. llvm_target: macos_llvm_target(arch).into(), pointer_width: 64, - data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), + data_layout: + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { mcount: "\u{1}mcount".into(), ..base }, } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 251f0628a71..ef4a0f52f9e 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,13 +1,14 @@ -use crate::solve::FulfillmentCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, DefiningAnchor, ObligationCtxt}; +use crate::traits::{self, DefiningAnchor, ObligationCtxt, SelectionContext}; +use crate::traits::TraitEngineExt as _; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_infer::traits::{TraitEngine, TraitEngineExt}; +use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt as _}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, ToPredicate}; use rustc_span::DUMMY_SP; @@ -21,7 +22,8 @@ pub trait InferCtxtExt<'tcx> { fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; - /// Check whether a `ty` implements given trait(trait_def_id). + /// Check whether a `ty` implements given trait(trait_def_id) without side-effects. + /// /// The inputs are: /// /// - the def-id of the trait @@ -29,8 +31,8 @@ pub trait InferCtxtExt<'tcx> { /// - the parameter environment /// /// Invokes `evaluate_obligation`, so in the event that evaluating - /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent (or EvaluatedToAmbigStackDependent) - /// will be returned. + /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent + /// (or EvaluatedToAmbigStackDependent) will be returned. fn type_implements_trait( &self, trait_def_id: DefId, @@ -38,7 +40,18 @@ pub trait InferCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; - fn could_impl_trait( + /// Returns `Some` if a type implements a trait shallowly, without side-effects, + /// along with any errors that would have been reported upon further obligation + /// processing. + /// + /// - If this returns `Some([])`, then the trait holds modulo regions. + /// - If this returns `Some([errors..])`, then the trait has an impl for + /// the self type, but some nested obligations do not hold. + /// - If this returns `None`, no implementation that applies could be found. + /// + /// FIXME(-Znext-solver): Due to the recursive nature of the new solver, + /// this will probably only ever return `Some([])` or `None`. + fn type_implements_trait_shallow( &self, trait_def_id: DefId, ty: Ty<'tcx>, @@ -86,64 +99,26 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) } - fn could_impl_trait( + fn type_implements_trait_shallow( &self, trait_def_id: DefId, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option<Vec<traits::FulfillmentError<'tcx>>> { self.probe(|_snapshot| { - if let ty::Adt(def, args) = ty.kind() - && let Some((impl_def_id, _)) = self - .tcx - .all_impls(trait_def_id) - .filter_map(|impl_def_id| { - self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) - }) - .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) - .find(|(_, imp)| match imp.self_ty().peel_refs().kind() { - ty::Adt(i_def, _) if i_def.did() == def.did() => true, - _ => false, - }) - { - let mut fulfill_cx = FulfillmentCtxt::new(self); - // We get all obligations from the impl to talk about specific - // trait bounds. - let obligations = self - .tcx - .predicates_of(impl_def_id) - .instantiate(self.tcx, args) - .into_iter() - .map(|(clause, span)| { - traits::Obligation::new( - self.tcx, - traits::ObligationCause::dummy_with_span(span), - param_env, - clause, - ) - }) - .collect::<Vec<_>>(); - fulfill_cx.register_predicate_obligations(self, obligations); - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); - let obligation = traits::Obligation::new( - self.tcx, - traits::ObligationCause::dummy(), - param_env, - trait_ref, - ); - fulfill_cx.register_predicate_obligation(self, obligation); - let mut errors = fulfill_cx.select_all_or_error(self); - // We remove the last predicate failure, which corresponds to - // the top-level obligation, because most of the type we only - // care about the other ones, *except* when it is the only one. - // This seems to only be relevant for arbitrary self-types. - // Look at `tests/ui/moves/move-fn-self-receiver.rs`. - if errors.len() > 1 { - errors.truncate(errors.len() - 1); + let mut selcx = SelectionContext::new(self); + match selcx.select(&Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty::TraitRef::new(self.tcx, trait_def_id, [ty]), + )) { + Ok(Some(selection)) => { + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self); + fulfill_cx.register_predicate_obligations(self, selection.nested_obligations()); + Some(fulfill_cx.select_all_or_error(self)) } - Some(errors) - } else { - None + Ok(None) | Err(_) => None, } }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 2139210b873..f08622816ec 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -11,7 +11,7 @@ use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use super::eval_ctxt::GenerateProofTree; -use super::{Certainty, InferCtxtEvalExt}; +use super::{Certainty, Goal, InferCtxtEvalExt}; /// A trait engine using the new trait solver. /// @@ -36,8 +36,28 @@ pub struct FulfillmentCtxt<'tcx> { impl<'tcx> FulfillmentCtxt<'tcx> { pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> { + assert!( + infcx.next_trait_solver(), + "new trait solver fulfillment context created when \ + infcx is set up for old trait solver" + ); FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() } } + + fn inspect_evaluated_obligation( + &self, + infcx: &InferCtxt<'tcx>, + obligation: &PredicateObligation<'tcx>, + result: &Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>, + ) { + if let Some(inspector) = infcx.obligation_inspector.get() { + let result = match result { + Ok((_, c, _)) => Ok(*c), + Err(NoSolution) => Err(NoSolution), + }; + (inspector)(infcx, &obligation, result); + } + } } impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { @@ -61,10 +81,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { .0 { Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => { - FulfillmentErrorCode::CodeAmbiguity { overflow: false } + FulfillmentErrorCode::Ambiguity { overflow: false } } Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => { - FulfillmentErrorCode::CodeAmbiguity { overflow: true } + FulfillmentErrorCode::Ambiguity { overflow: true } } Ok((_, Certainty::Yes, _)) => { bug!("did not expect successful goal when collecting ambiguity errors") @@ -95,65 +115,66 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - let (changed, certainty, nested_goals) = - match infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0 { - Ok(result) => result, - Err(NoSolution) => { - errors.push(FulfillmentError { - obligation: obligation.clone(), - code: match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { - FulfillmentErrorCode::CodeProjectionError( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::NormalizesTo(..) => { - FulfillmentErrorCode::CodeProjectionError( - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::CodeProjectionError( - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::Subtype(pred) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::CodeSubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::Coerce(pred) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(false, a, b); - FulfillmentErrorCode::CodeSubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::Clause(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::Ambiguous => { - FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ) - } - ty::PredicateKind::ConstEquate(..) => { - bug!("unexpected goal: {goal:?}") - } - }, - root_obligation: obligation, - }); - continue; - } - }; + let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0; + self.inspect_evaluated_obligation(infcx, &obligation, &result); + let (changed, certainty, nested_goals) = match result { + Ok(result) => result, + Err(NoSolution) => { + errors.push(FulfillmentError { + obligation: obligation.clone(), + code: match goal.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { + FulfillmentErrorCode::ProjectionError( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::ProjectionError( + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::ProjectionError( + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::Subtype(pred) => { + let (a, b) = infcx.instantiate_binder_with_placeholders( + goal.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(true, a, b); + FulfillmentErrorCode::SubtypeError( + expected_found, + TypeError::Sorts(expected_found), + ) + } + ty::PredicateKind::Coerce(pred) => { + let (a, b) = infcx.instantiate_binder_with_placeholders( + goal.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(false, a, b); + FulfillmentErrorCode::SubtypeError( + expected_found, + TypeError::Sorts(expected_found), + ) + } + ty::PredicateKind::Clause(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::Ambiguous => { + FulfillmentErrorCode::SelectionError( + SelectionError::Unimplemented, + ) + } + ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected goal: {goal:?}") + } + }, + root_obligation: obligation, + }); + continue; + } + }; // Push any nested goals that we get from unifying our canonical response // with our obligation onto the fulfillment context. self.obligations.extend(nested_goals.into_iter().map(|goal| { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 6c0909d2853..0e33e9cd790 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -859,6 +859,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id) && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id) && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) + && let trait_pred = predicate.unwrap_or(trait_pred) + // Only run this code on binary operators + && hir::lang_items::BINARY_OPERATORS + .iter() + .filter_map(|&op| self.tcx.lang_items().get(op)) + .any(|op| { + op == trait_pred.skip_binder().trait_ref.def_id + }) { // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index b970ec7f726..149dcffe333 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -14,8 +14,8 @@ use crate::traits::specialize::to_pretty_impl_header; use crate::traits::NormalizeExt; use crate::traits::{ elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, - ObligationCause, ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionError, TraitNotObjectSafe, + ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, + SelectionError, SignatureMismatch, TraitNotObjectSafe, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ @@ -30,7 +30,7 @@ use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; -use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch}; +use rustc_middle::traits::{DefiningAnchor, SignatureMismatchData}; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable}; @@ -785,14 +785,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Subtype(predicate) => { // Errors for Subtype predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, + // `FulfillmentErrorCode::SubtypeError`, // not selection error. span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) } ty::PredicateKind::Coerce(predicate) => { // Errors for Coerce predicates show up as - // `FulfillmentErrorCode::CodeSubtypeError`, + // `FulfillmentErrorCode::SubtypeError`, // not selection error. span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } @@ -891,22 +891,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + SignatureMismatch(box SignatureMismatchData { found_trait_ref, expected_trait_ref, terr: terr @ TypeError::CyclicTy(_), - }) => self.report_type_parameter_mismatch_cyclic_type_error( + }) => self.report_cyclic_signature_error( &obligation, found_trait_ref, expected_trait_ref, terr, ), - OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + SignatureMismatch(box SignatureMismatchData { found_trait_ref, expected_trait_ref, terr: _, }) => { - match self.report_type_parameter_mismatch_error( + match self.report_signature_mismatch_error( &obligation, span, found_trait_ref, @@ -1492,7 +1492,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> { kind: ty::ClosureKind, ) -> DiagnosticBuilder<'tcx>; - fn report_type_parameter_mismatch_cyclic_type_error( + fn report_cyclic_signature_error( &self, obligation: &PredicateObligation<'tcx>, found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, @@ -1506,7 +1506,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> { def_id: DefId, ) -> DiagnosticBuilder<'tcx>; - fn report_type_parameter_mismatch_error( + fn report_signature_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, span: Span, @@ -1572,23 +1572,23 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } match error.code { - FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + FulfillmentErrorCode::SelectionError(ref selection_error) => { self.report_selection_error( error.obligation.clone(), &error.root_obligation, selection_error, ); } - FulfillmentErrorCode::CodeProjectionError(ref e) => { + FulfillmentErrorCode::ProjectionError(ref e) => { self.report_projection_error(&error.obligation, e); } - FulfillmentErrorCode::CodeAmbiguity { overflow: false } => { + FulfillmentErrorCode::Ambiguity { overflow: false } => { self.maybe_report_ambiguity(&error.obligation); } - FulfillmentErrorCode::CodeAmbiguity { overflow: true } => { + FulfillmentErrorCode::Ambiguity { overflow: true } => { self.report_overflow_no_abort(error.obligation.clone()); } - FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + FulfillmentErrorCode::SubtypeError(ref expected_found, ref err) => { self.report_mismatched_types( &error.obligation.cause, expected_found.expected, @@ -1597,7 +1597,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) .emit(); } - FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + FulfillmentErrorCode::ConstEquateError(ref expected_found, ref err) => { let mut diag = self.report_mismatched_consts( &error.obligation.cause, expected_found.expected, @@ -1622,7 +1622,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } diag.emit(); } - FulfillmentErrorCode::CodeCycle(ref cycle) => { + FulfillmentErrorCode::Cycle(ref cycle) => { self.report_overflow_obligation_cycle(cycle); } } @@ -3366,7 +3366,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.dcx().create_err(err) } - fn report_type_parameter_mismatch_cyclic_type_error( + fn report_cyclic_signature_error( &self, obligation: &PredicateObligation<'tcx>, found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, @@ -3427,7 +3427,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } - fn report_type_parameter_mismatch_error( + fn report_signature_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, span: Span, @@ -3446,10 +3446,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; let found_did = match *found_trait_ty.kind() { - ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Coroutine(did, ..) => { - Some(did) - } - ty::Adt(def, _) => Some(def.did()), + ty::Closure(did, _) | ty::FnDef(did, _) | ty::Coroutine(did, ..) => Some(did), _ => None, }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 045d7e444b6..0337672b302 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -18,9 +18,6 @@ use super::const_evaluatable; use super::project::{self, ProjectAndUnifyResult}; use super::select::SelectionContext; use super::wf; -use super::CodeAmbiguity; -use super::CodeProjectionError; -use super::CodeSelectionError; use super::EvaluationResult; use super::PredicateObligation; use super::Unimplemented; @@ -80,6 +77,11 @@ static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> { + assert!( + !infcx.next_trait_solver(), + "old trait solver fulfillment context created when \ + infcx is set up for new trait solver" + ); FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: infcx.num_open_snapshots(), @@ -135,7 +137,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { _infcx: &InferCtxt<'tcx>, ) -> Vec<FulfillmentError<'tcx>> { self.predicates - .to_errors(CodeAmbiguity { overflow: false }) + .to_errors(FulfillmentErrorCode::Ambiguity { overflow: false }) .into_iter() .map(to_fulfillment_error) .collect() @@ -409,7 +411,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::ObjectSafe(trait_def_id) => { if !self.selcx.tcx().check_is_object_safe(trait_def_id) { - ProcessResult::Error(CodeSelectionError(Unimplemented)) + ProcessResult::Error(FulfillmentErrorCode::SelectionError(Unimplemented)) } else { ProcessResult::Changed(vec![]) } @@ -480,7 +482,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(Err(err)) => { let expected_found = ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); - ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + ProcessResult::Error(FulfillmentErrorCode::SubtypeError( expected_found, err, )) @@ -503,7 +505,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), Ok(Err(err)) => { let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); - ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + ProcessResult::Error(FulfillmentErrorCode::SubtypeError( expected_found, err, )) @@ -529,7 +531,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Err( e @ NotConstEvaluatable::MentionsParam | e @ NotConstEvaluatable::Error(_), - ) => ProcessResult::Error(CodeSelectionError( + ) => ProcessResult::Error(FulfillmentErrorCode::SelectionError( SelectionError::NotConstEvaluatable(e), )), } @@ -618,20 +620,22 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(inf_ok) => { ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) } - Err(err) => ProcessResult::Error( - FulfillmentErrorCode::CodeConstEquateError( + Err(err) => { + ProcessResult::Error(FulfillmentErrorCode::ConstEquateError( ExpectedFound::new(true, c1, c2), err, - ), - ), + )) + } } } (Err(ErrorHandled::Reported(reported, _)), _) - | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( - CodeSelectionError(SelectionError::NotConstEvaluatable( - NotConstEvaluatable::Error(reported.into()), - )), - ), + | (_, Err(ErrorHandled::Reported(reported, _))) => { + ProcessResult::Error(FulfillmentErrorCode::SelectionError( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error( + reported.into(), + )), + )) + } (Err(ErrorHandled::TooGeneric(_)), _) | (_, Err(ErrorHandled::TooGeneric(_))) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { @@ -639,7 +643,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } else { // Two different constants using generic parameters ~> error. let expected_found = ExpectedFound::new(true, c1, c2); - ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError( + ProcessResult::Error(FulfillmentErrorCode::ConstEquateError( expected_found, TypeError::ConstMismatch(expected_found), )) @@ -654,7 +658,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty, ) { Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), - Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError( + Err(_) => ProcessResult::Error(FulfillmentErrorCode::SelectionError( SelectionError::Unimplemented, )), } @@ -677,7 +681,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(()) } else { let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - Err(FulfillmentErrorCode::CodeCycle(cycle)) + Err(FulfillmentErrorCode::Cycle(cycle)) } } } @@ -732,7 +736,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { Err(selection_err) => { debug!("selecting trait at depth {} yielded Err", obligation.recursion_depth); - ProcessResult::Error(CodeSelectionError(selection_err)) + ProcessResult::Error(FulfillmentErrorCode::SelectionError(selection_err)) } } } @@ -784,7 +788,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { project_obligation.with(tcx, project_obligation.predicate), ])), ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { - ProcessResult::Error(CodeProjectionError(e)) + ProcessResult::Error(FulfillmentErrorCode::ProjectionError(e)) } } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 073174127d6..0cd376fcbbd 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -194,7 +194,7 @@ pub fn all_fields_implement_trait<'tcx>( infcx.implied_bounds_tys( param_env, parent_cause.body_id, - FxIndexSet::from_iter([self_type]), + &FxIndexSet::from_iter([self_type]), ), ); let errors = infcx.resolve_regions(&outlives_env); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 5b349d576b6..52631d4353b 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -9,123 +9,153 @@ use rustc_span::def_id::LocalDefId; pub use rustc_middle::traits::query::OutlivesBound; +pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; pub trait InferCtxtExt<'a, 'tcx> { - fn implied_outlives_bounds( - &self, + /// Do *NOT* call this directly. + fn implied_bounds_tys_compat( + &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, - ty: Ty<'tcx>, - ) -> Vec<OutlivesBound<'tcx>>; + tys: &'a FxIndexSet<Ty<'tcx>>, + compat: bool, + ) -> BoundsCompat<'a, 'tcx>; + /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` + /// with `compat` set to `true`, otherwise `false`. fn implied_bounds_tys( &'a self, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet<Ty<'tcx>>, + tys: &'a FxIndexSet<Ty<'tcx>>, ) -> Bounds<'a, 'tcx>; } -impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { - /// Implied bounds are region relationships that we deduce - /// automatically. The idea is that (e.g.) a caller must check that a - /// function's argument types are well-formed immediately before - /// calling that fn, and hence the *callee* can assume that its - /// argument types are well-formed. This may imply certain relationships - /// between generic parameters. For example: - /// ``` - /// fn foo<T>(x: &T) {} - /// ``` - /// can only be called with a `'a` and `T` such that `&'a T` is WF. - /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. - /// - /// # Parameters - /// - /// - `param_env`, the where-clauses in scope - /// - `body_id`, the body-id to use when normalizing assoc types. - /// Note that this may cause outlives obligations to be injected - /// into the inference context with this body-id. - /// - `ty`, the type that we are supposed to assume is WF. - #[instrument(level = "debug", skip(self, param_env, body_id), ret)] - fn implied_outlives_bounds( - &self, - param_env: ty::ParamEnv<'tcx>, - body_id: LocalDefId, - ty: Ty<'tcx>, - ) -> Vec<OutlivesBound<'tcx>> { - let ty = self.resolve_vars_if_possible(ty); - let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); +/// Implied bounds are region relationships that we deduce +/// automatically. The idea is that (e.g.) a caller must check that a +/// function's argument types are well-formed immediately before +/// calling that fn, and hence the *callee* can assume that its +/// argument types are well-formed. This may imply certain relationships +/// between generic parameters. For example: +/// ``` +/// fn foo<T>(x: &T) {} +/// ``` +/// can only be called with a `'a` and `T` such that `&'a T` is WF. +/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +/// +/// # Parameters +/// +/// - `param_env`, the where-clauses in scope +/// - `body_id`, the body-id to use when normalizing assoc types. +/// Note that this may cause outlives obligations to be injected +/// into the inference context with this body-id. +/// - `ty`, the type that we are supposed to assume is WF. +#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)] +fn implied_outlives_bounds<'a, 'tcx>( + infcx: &'a InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + compat: bool, +) -> Vec<OutlivesBound<'tcx>> { + let ty = infcx.resolve_vars_if_possible(ty); + let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty); - // We do not expect existential variables in implied bounds. - // We may however encounter unconstrained lifetime variables - // in very rare cases. - // - // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for - // an example. - assert!(!ty.has_non_region_infer()); + // We do not expect existential variables in implied bounds. + // We may however encounter unconstrained lifetime variables + // in very rare cases. + // + // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for + // an example. + assert!(!ty.has_non_region_infer()); - let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values); - let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { - return vec![]; - }; + let mut canonical_var_values = OriginalQueryValues::default(); + let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values); + let implied_bounds_result = if compat { + infcx.tcx.implied_outlives_bounds_compat(canonical_ty) + } else { + infcx.tcx.implied_outlives_bounds(canonical_ty) + }; + let Ok(canonical_result) = implied_bounds_result else { + return vec![]; + }; - let mut constraints = QueryRegionConstraints::default(); - let Ok(InferOk { value: mut bounds, obligations }) = self - .instantiate_nll_query_response_and_region_obligations( - &ObligationCause::dummy(), - param_env, - &canonical_var_values, - canonical_result, - &mut constraints, - ) - else { - return vec![]; - }; - assert_eq!(&obligations, &[]); + let mut constraints = QueryRegionConstraints::default(); + let Ok(InferOk { value: mut bounds, obligations }) = infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + &mut constraints, + ) + else { + return vec![]; + }; + assert_eq!(&obligations, &[]); - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - bounds.retain(|bound| !bound.has_placeholders()); + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + bounds.retain(|bound| !bound.has_placeholders()); - if !constraints.is_empty() { - let span = self.tcx.def_span(body_id); + if !constraints.is_empty() { + let span = infcx.tcx.def_span(body_id); - debug!(?constraints); - if !constraints.member_constraints.is_empty() { - span_bug!(span, "{:#?}", constraints.member_constraints); - } + debug!(?constraints); + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } - // Instantiation may have produced new inference variables and constraints on those - // variables. Process these constraints. - let ocx = ObligationCtxt::new(self); - let cause = ObligationCause::misc(span, body_id); - for &constraint in &constraints.outlives { - ocx.register_obligation(self.query_outlives_constraint_to_obligation( - constraint, - cause.clone(), - param_env, - )); - } + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let ocx = ObligationCtxt::new(infcx); + let cause = ObligationCause::misc(span, body_id); + for &constraint in &constraints.outlives { + ocx.register_obligation(infcx.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + )); + } - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - self.dcx().span_delayed_bug( - span, - "implied_outlives_bounds failed to solve obligations from instantiation", - ); - } - }; + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.dcx().span_delayed_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + }; - bounds + bounds +} + +impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { + fn implied_bounds_tys_compat( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: LocalDefId, + tys: &'a FxIndexSet<Ty<'tcx>>, + compat: bool, + ) -> BoundsCompat<'a, 'tcx> { + tys.iter() + .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) } fn implied_bounds_tys( &'a self, param_env: ParamEnv<'tcx>, body_id: LocalDefId, - tys: FxIndexSet<Ty<'tcx>>, + tys: &'a FxIndexSet<Ty<'tcx>>, ) -> Bounds<'a, 'tcx> { - tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) + tys.iter().flat_map(move |ty| { + implied_outlives_bounds( + self, + param_env, + body_id, + *ty, + !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) + }) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index dd4e69efe37..abbc2066eac 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2466,7 +2466,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( debug!(?cache_projection, ?obligation_projection); match infcx.at(cause, param_env).eq( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, cache_projection, obligation_projection, ) { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 152ceeee869..0aaeacb6a23 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -63,13 +63,16 @@ fn relate_mir_and_user_ty<'tcx>( user_ty: Ty<'tcx>, ) -> Result<(), NoSolution> { let cause = ObligationCause::dummy_with_span(span); + ocx.register_obligation(Obligation::new( + ocx.infcx.tcx, + cause.clone(), + param_env, + ty::ClauseKind::WellFormed(user_ty.into()), + )); + let user_ty = ocx.normalize(&cause, param_env, user_ty); ocx.eq(&cause, param_env, mir_ty, user_ty)?; - // FIXME(#104764): We should check well-formedness before normalization. - let predicate = - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into()))); - ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); Ok(()) } @@ -113,31 +116,38 @@ fn relate_mir_and_user_args<'tcx>( ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); } + // Now prove the well-formedness of `def_id` with `substs`. + // Note for some items, proving the WF of `ty` is not sufficient because the + // well-formedness of an item may depend on the WF of gneneric args not present in the + // item's type. Currently this is true for associated consts, e.g.: + // ```rust + // impl<T> MyTy<T> { + // const CONST: () = { /* arbitrary code that depends on T being WF */ }; + // } + // ``` + for arg in args { + ocx.register_obligation(Obligation::new( + tcx, + cause.clone(), + param_env, + ty::ClauseKind::WellFormed(arg), + )); + } + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + ocx.register_obligation(Obligation::new( + tcx, + cause.clone(), + param_env, + ty::ClauseKind::WellFormed(self_ty.into()), + )); + let self_ty = ocx.normalize(&cause, param_env, self_ty); let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args); let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty); ocx.eq(&cause, param_env, self_ty, impl_self_ty)?; - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - impl_self_ty.into(), - ))); - ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); } - // In addition to proving the predicates, we have to - // prove that `ty` is well-formed -- this is because - // the WF of `ty` is predicated on the args being - // well-formed, and we haven't proven *that*. We don't - // want to prove the WF of types from `args` directly because they - // haven't been normalized. - // - // FIXME(nmatsakis): Well, perhaps we should normalize - // them? This would only be relevant if some input - // type were ill-formed but did not appear in `ty`, - // which...could happen with normalization... - let predicate = - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into()))); - ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate)); Ok(()) } 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 ba6ed298774..83d23597c0c 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 @@ -5,10 +5,11 @@ 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_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; @@ -47,14 +48,22 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { param_env.and(ty) }); - tcx.implied_outlives_bounds(canonicalized) + if tcx.sess.opts.unstable_opts.no_implied_bounds_compat { + tcx.implied_outlives_bounds(canonicalized) + } else { + tcx.implied_outlives_bounds_compat(canonicalized) + } } fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + } else { + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) + } } } @@ -63,6 +72,85 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { + let normalize_op = |ty| { + let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty); + if !ocx.select_all_or_error().is_empty() { + return Err(NoSolution); + } + let ty = ocx.infcx.resolve_vars_if_possible(ty); + let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); + Ok(ty) + }; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Because the resulting predicates aren't always + // guaranteed to be a subset of the original type, so we need to store the + // WF args we've computed in a set. + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); + let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()]; + + let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![]; + + while let Some(arg) = wf_args.pop() { + if !checked_wf_args.insert(arg) { + continue; + } + + // From the full set of obligations, just filter down to the region relationships. + for obligation in + wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten() + { + assert!(!obligation.has_escaping_bound_vars()); + let Some(pred) = obligation.predicate.kind().no_bound_vars() else { + continue; + }; + match pred { + // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound + // if we ever support that + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + wf_args.push(arg); + } + + // We need to register region relationships + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( + ty::OutlivesPredicate(r_a, r_b), + )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)), + + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => { + let ty_a = normalize_op(ty_a)?; + let mut components = smallvec![]; + push_outlives_components(ocx.infcx.tcx, ty_a, &mut components); + outlives_bounds.extend(implied_bounds_from_components(r_b, components)) + } + } + } + } + + Ok(outlives_bounds) +} + +pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { let tcx = ocx.infcx.tcx; // Sometimes when we ask what it takes for T: WF, we get back that diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index e21ede47f6d..14f14ae6e2e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -30,6 +30,23 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { } } + if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) = + key.value.predicate.kind().skip_binder() + { + match arg.as_type()?.kind() { + ty::Param(_) + | ty::Bool + | ty::Char + | ty::Int(_) + | ty::Float(_) + | ty::Str + | ty::Uint(_) => { + return Some(()); + } + _ => {} + } + } + None } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e20bb06d777..74f388e53a3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; +use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, TraitPredicate, Ty, TyCtxt, TypeVisitableExt, @@ -26,9 +26,9 @@ use crate::traits::vtable::{ }; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, - ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, - OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection, - SelectionError, TraitNotObjectSafe, Unimplemented, + ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, + PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe, + Unimplemented, }; use super::BuiltinImplConditions; @@ -922,7 +922,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations }) .map_err(|terr| { - OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch { + SignatureMismatch(Box::new(SignatureMismatchData { expected_trait_ref: obligation_trait_ref, found_trait_ref: expected_trait_ref, terr, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 200d022c80c..b37d9714ddd 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -26,7 +26,7 @@ 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::{ErrorGuaranteed, Span, DUMMY_SP}; +use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP}; use super::util; use super::SelectionContext; @@ -142,10 +142,30 @@ pub fn translate_args_with_cause<'tcx>( pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { // The feature gate should prevent introducing new specializations, but not // taking advantage of upstream ones. + // If specialization is enabled for this crate then no extra checks are needed. + // If it's not, and either of the `impl`s is local to this crate, then this definitely + // isn't specializing - unless specialization is enabled for the `impl` span, + // e.g. if it comes from an `allow_internal_unstable` macro let features = tcx.features(); let specialization_enabled = features.specialization || features.min_specialization; - if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { - return false; + if !specialization_enabled { + if impl1_def_id.is_local() { + let span = tcx.def_span(impl1_def_id); + if !span.allows_unstable(sym::specialization) + && !span.allows_unstable(sym::min_specialization) + { + return false; + } + } + + if impl2_def_id.is_local() { + let span = tcx.def_span(impl2_def_id); + if !span.allows_unstable(sym::specialization) + && !span.allows_unstable(sym::min_specialization) + { + return false; + } + } } // We determine whether there's a subset relationship by: diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 2cd1c3b502a..f3fae63ecc7 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -63,7 +63,7 @@ pub fn codegen_select_candidate<'tcx>( // Cycle errors are the only post-monomorphization errors possible; emit them now so // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization. for err in errors { - if let FulfillmentErrorCode::CodeCycle(cycle) = err.code { + if let FulfillmentErrorCode::Cycle(cycle) = err.code { infcx.err_ctxt().report_overflow_obligation_cycle(&cycle); } } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 959838ab348..24e91c263e3 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -8,13 +8,29 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; +use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ + compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, +}; use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { + *p = Providers { implied_outlives_bounds_compat, ..*p }; *p = Providers { implied_outlives_bounds, ..*p }; } +fn implied_outlives_bounds_compat<'tcx>( + tcx: TyCtxt<'tcx>, + goal: CanonicalTyGoal<'tcx>, +) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, + NoSolution, +> { + tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { + let (param_env, ty) = key.into_parts(); + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) + }) +} + fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalTyGoal<'tcx>, diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index b8c71bc96f8..21a016e29d6 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -54,7 +54,7 @@ fn normalize_projection_ty<'tcx>( // that impl vars are constrained by the signature, for example). if !tcx.sess.opts.actually_rustdoc { for error in &errors { - if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code { + if let FulfillmentErrorCode::Cycle(cycle) = &error.code { ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 6e8293dac31..333aec1d38d 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -83,7 +83,11 @@ fn fn_sig_for_fn_abi<'tcx>( kind: ty::BoundRegionKind::BrEnv, }; let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - let env_ty = tcx.closure_env_ty(def_id, args, env_region).unwrap(); + let env_ty = tcx.closure_env_ty( + Ty::new_closure(tcx, def_id, args), + args.as_closure().kind(), + env_region, + ); let sig = sig.skip_binder(); ty::Binder::bind_with_vars( diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b8351463c55..2fc4bfd4aa3 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1052,6 +1052,8 @@ fn variant_info_for_coroutine<'tcx>( def_id: DefId, args: ty::GenericArgsRef<'tcx>, ) -> (Vec<VariantInfo>, Option<Size>) { + use itertools::Itertools; + let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else { return (vec![], None); }; @@ -1064,7 +1066,7 @@ fn variant_info_for_coroutine<'tcx>( .as_coroutine() .upvar_tys() .iter() - .zip(upvar_names) + .zip_eq(upvar_names) .enumerate() .map(|(field_idx, (_, name))| { let field_layout = layout.field(cx, field_idx); diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index bff93859645..83efbfb855f 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -327,21 +327,21 @@ impl UniverseIndex { /// name the region `'a`, but that region was not nameable from /// `U` because it was not in scope there. pub fn next_universe(self) -> UniverseIndex { - UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) + UniverseIndex::from_u32(self.as_u32().checked_add(1).unwrap()) } /// Returns `true` if `self` can name a name from `other` -- in other words, /// if the set of names in `self` is a superset of those in /// `other` (`self >= other`). pub fn can_name(self, other: UniverseIndex) -> bool { - self.private >= other.private + self >= other } /// Returns `true` if `self` cannot name some names from `other` -- in other /// words, if the set of names in `self` is a strict subset of /// those in `other` (`self < other`). pub fn cannot_name(self, other: UniverseIndex) -> bool { - self.private < other.private + self < other } } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index eba2ac57012..b0a08cd9b79 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -12,7 +12,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Ty(pub usize); +pub struct Ty(usize); impl Debug for Ty { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -138,7 +138,7 @@ impl Const { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ConstId(pub usize); +pub struct ConstId(usize); type Ident = Opaque; |
