diff options
Diffstat (limited to 'compiler')
555 files changed, 15494 insertions, 11686 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f705d004422..ac2328a5824 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1390,7 +1390,7 @@ pub enum ExprKind { /// A closure (e.g., `move |a, b, c| a + b + c`). /// /// The final span is the span of the argument block `|...|`. - Closure(CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span), + Closure(ClosureBinder, CaptureBy, Async, Movability, P<FnDecl>, P<Expr>, Span), /// A block (`'label: { ... }`). Block(P<Block>, Option<Label>), /// An async block (`async move { ... }`). @@ -1518,6 +1518,31 @@ pub enum Movability { Movable, } +/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum ClosureBinder { + /// The binder is not present, all closure lifetimes are inferred. + NotPresent, + /// The binder is present. + For { + /// Span of the whole `for<>` clause + /// + /// ```text + /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... } + /// ^^^^^^^^^^^ -- this + /// ``` + span: Span, + + /// Lifetimes in the `for<>` closure + /// + /// ```text + /// for<'a, 'b> |_: &'a (), _: &'b ()| { ... } + /// ^^^^^^ -- this + /// ``` + generic_params: P<[GenericParam]>, + }, +} + /// Represents a macro invocation. The `path` indicates which macro /// is being invoked, and the `args` are arguments passed to it. #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 4b94ec0d6d8..838f77512c5 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -14,7 +14,7 @@ #![feature(const_trait_impl)] #![feature(if_let_guard)] #![feature(label_break_value)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(slice_internals)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 85bb5296486..d933ea2da9e 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -125,6 +125,10 @@ pub trait MutVisitor: Sized { noop_visit_asyncness(a, self); } + fn visit_closure_binder(&mut self, b: &mut ClosureBinder) { + noop_visit_closure_binder(b, self); + } + fn visit_block(&mut self, b: &mut P<Block>) { noop_visit_block(b, self); } @@ -825,6 +829,17 @@ pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) { } } +pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis: &mut T) { + match binder { + ClosureBinder::NotPresent => {} + ClosureBinder::For { span: _, generic_params } => { + let mut vec = std::mem::take(generic_params).into_vec(); + vec.flat_map_in_place(|param| vis.flat_map_generic_param(param)); + *generic_params = P::from_vec(vec); + } + } +} + pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) { match asyncness { Async::Yes { span: _, closure_id, return_impl_trait_id } => { @@ -1336,7 +1351,8 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_expr(expr); arms.flat_map_in_place(|arm| vis.flat_map_arm(arm)); } - ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => { + ExprKind::Closure(binder, _capture_by, asyncness, _movability, decl, body, span) => { + vis.visit_closure_binder(binder); vis.visit_asyncness(asyncness); vis.visit_fn_decl(decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2ce8590d771..3f830acbf27 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -56,14 +56,14 @@ pub enum FnKind<'a> { Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, &'a Generics, Option<&'a Block>), /// E.g., `|x, y| body`. - Closure(&'a FnDecl, &'a Expr), + Closure(&'a ClosureBinder, &'a FnDecl, &'a Expr), } impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), - FnKind::Closure(_, _) => None, + FnKind::Closure(_, _, _) => None, } } @@ -77,7 +77,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, - FnKind::Closure(decl, _) => decl, + FnKind::Closure(_, decl, _) => decl, } } @@ -155,6 +155,9 @@ pub trait Visitor<'ast>: Sized { fn visit_generics(&mut self, g: &'ast Generics) { walk_generics(self, g) } + fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) { + walk_closure_binder(self, b) + } fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { walk_where_predicate(self, p) } @@ -636,6 +639,15 @@ pub fn walk_generics<'a, V: Visitor<'a>>(visitor: &mut V, generics: &'a Generics walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates); } +pub fn walk_closure_binder<'a, V: Visitor<'a>>(visitor: &mut V, binder: &'a ClosureBinder) { + match binder { + ClosureBinder::NotPresent => {} + ClosureBinder::For { generic_params, span: _ } => { + walk_list!(visitor, visit_generic_param, generic_params) + } + } +} + pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate) { match *predicate { WherePredicate::BoundPredicate(WhereBoundPredicate { @@ -682,7 +694,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>, _span: Spa walk_fn_decl(visitor, &sig.decl); walk_list!(visitor, visit_block, body); } - FnKind::Closure(decl, body) => { + FnKind::Closure(binder, decl, body) => { + visitor.visit_closure_binder(binder); walk_fn_decl(visitor, decl); visitor.visit_expr(body); } @@ -856,8 +869,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure(_, _, _, ref decl, ref body, _decl_span) => { - visitor.visit_fn(FnKind::Closure(decl, body), expression.span, expression.id) + ExprKind::Closure(ref binder, _, _, _, ref decl, ref body, _decl_span) => { + visitor.visit_fn(FnKind::Closure(binder, decl, body), expression.span, expression.id) } ExprKind::Block(ref block, ref opt_label) => { walk_list!(visitor, visit_label, opt_label); diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 0e395d70335..4166b4fc2e5 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -4,8 +4,7 @@ use super::LoweringContext; use rustc_ast::ptr::P; use rustc_ast::*; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -145,7 +144,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { InlineAsmRegOrRegClass::Reg(s) => { asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| { - let msg = format!("invalid register `{}`: {}", s.as_str(), e); + let msg = format!("invalid register `{}`: {}", s, e); sess.struct_span_err(*op_sp, &msg).emit(); asm::InlineAsmReg::Err }) @@ -156,7 +155,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { InlineAsmRegOrRegClass::RegClass(s) => { asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| { - let msg = format!("invalid register class `{}`: {}", s.as_str(), e); + let msg = format!("invalid register class `{}`: {}", s, e); sess.struct_span_err(*op_sp, &msg).emit(); asm::InlineAsmRegClass::Err }) diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 9444fffc331..7cbfe143b4d 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -1,8 +1,8 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; -use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind}; +use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind}; use rustc_hir as hir; use rustc_session::parse::feature_err; -use rustc_span::{sym, DesugaringKind}; +use rustc_span::sym; use smallvec::SmallVec; @@ -36,21 +36,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); - match &local.kind { - LocalKind::InitElse(init, els) => { - let e = self.lower_let_else(hir_id, local, init, els, tail); - expr = Some(e); - // remaining statements are in let-else expression - break; - } - _ => { - let local = self.lower_local(local); - self.alias_attrs(hir_id, local.hir_id); - let kind = hir::StmtKind::Local(local); - let span = self.lower_span(s.span); - stmts.push(hir::Stmt { hir_id, kind, span }); - } - } + let local = self.lower_local(local); + self.alias_attrs(hir_id, local.hir_id); + let kind = hir::StmtKind::Local(local); + let span = self.lower_span(s.span); + stmts.push(hir::Stmt { hir_id, kind, span }); } StmtKind::Item(ref it) => { stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map( @@ -101,10 +91,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); let pat = self.lower_pat(&l.pat); + let els = if let LocalKind::InitElse(_, els) = &l.kind { + if !self.tcx.features().let_else { + feature_err( + &self.tcx.sess.parse_sess, + sym::let_else, + l.span, + "`let...else` statements are unstable", + ) + .emit(); + } + Some(self.lower_block(els, false)) + } else { + None + }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source }) + self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { @@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - - fn lower_let_else( - &mut self, - stmt_hir_id: hir::HirId, - local: &Local, - init: &Expr, - els: &Block, - tail: &[Stmt], - ) -> &'hir hir::Expr<'hir> { - let ty = local - .ty - .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); - let span = self.lower_span(local.span); - let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); - let init = self.lower_expr(init); - let local_hir_id = self.lower_node_id(local.id); - self.lower_attrs(local_hir_id, &local.attrs); - let let_expr = { - let lex = self.arena.alloc(hir::Let { - hir_id: local_hir_id, - pat: self.lower_pat(&local.pat), - ty, - init, - span, - }); - self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new())) - }; - let then_expr = { - let (stmts, expr) = self.lower_stmts(tail); - let block = self.block_all(span, stmts, expr); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - let else_expr = { - let block = self.lower_block(els, false); - self.arena.alloc(self.expr_block(block, AttrVec::new())) - }; - self.alias_attrs(let_expr.hir_id, local_hir_id); - self.alias_attrs(else_expr.hir_id, local_hir_id); - let if_expr = self.arena.alloc(hir::Expr { - hir_id: stmt_hir_id, - span, - kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), - }); - if !self.tcx.features().let_else { - feature_err( - &self.tcx.sess.parse_sess, - sym::let_else, - local.span, - "`let...else` statements are unstable", - ) - .emit(); - } - if_expr - } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9e02e7ed3b9..983efa48a45 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -155,6 +155,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_expr_await(span, expr) } ExprKind::Closure( + ref binder, capture_clause, asyncness, movability, @@ -164,6 +165,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) => { if let Async::Yes { closure_id, .. } = asyncness { self.lower_expr_async_closure( + binder, capture_clause, e.id, closure_id, @@ -173,6 +175,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } else { self.lower_expr_closure( + binder, capture_clause, e.id, movability, @@ -605,13 +608,18 @@ impl<'hir> LoweringContext<'_, 'hir> { }); // `static |_task_context| -> <ret_ty> { body }`: - let generator_kind = hir::ExprKind::Closure { - capture_clause, - bound_generic_params: &[], - fn_decl, - body, - fn_decl_span: self.lower_span(span), - movability: Some(hir::Movability::Static), + let generator_kind = { + let c = self.arena.alloc(hir::Closure { + binder: hir::ClosureBinder::Default, + capture_clause, + bound_generic_params: &[], + fn_decl, + body, + fn_decl_span: self.lower_span(span), + movability: Some(hir::Movability::Static), + }); + + hir::ExprKind::Closure(c) }; let generator = hir::Expr { hir_id: self.lower_node_id(closure_node_id), @@ -831,6 +839,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_closure( &mut self, + binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, movability: Movability, @@ -838,7 +847,9 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, ) -> hir::ExprKind<'hir> { - let (body, generator_option) = self.with_new_scopes(move |this| { + let (binder_clause, generic_params) = self.lower_closure_binder(binder); + + let (body_id, generator_option) = self.with_new_scopes(move |this| { let prev = this.current_item; this.current_item = Some(fn_decl_span); let mut generator_kind = None; @@ -853,18 +864,21 @@ impl<'hir> LoweringContext<'_, 'hir> { (body_id, generator_option) }); - self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| { + self.with_lifetime_binder(closure_id, generic_params, |this, bound_generic_params| { // Lower outside new scope to preserve `is_in_loop_condition`. let fn_decl = this.lower_fn_decl(decl, None, FnDeclKind::Closure, None); - hir::ExprKind::Closure { + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, capture_clause, bound_generic_params, fn_decl, - body, + body: body_id, fn_decl_span: this.lower_span(fn_decl_span), movability: generator_option, - } + }); + + hir::ExprKind::Closure(c) }) } @@ -906,8 +920,24 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn lower_closure_binder<'c>( + &mut self, + binder: &'c ClosureBinder, + ) -> (hir::ClosureBinder, &'c [GenericParam]) { + let (binder, params) = match binder { + ClosureBinder::NotPresent => (hir::ClosureBinder::Default, &[][..]), + &ClosureBinder::For { span, ref generic_params } => { + let span = self.lower_span(span); + (hir::ClosureBinder::For { span }, &**generic_params) + } + }; + + (binder, params) + } + fn lower_expr_async_closure( &mut self, + binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, inner_closure_id: NodeId, @@ -915,6 +945,15 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, ) -> hir::ExprKind<'hir> { + if let &ClosureBinder::For { span, .. } = binder { + self.tcx.sess.span_err( + span, + "`for<...>` binders on `async` closures are not currently supported", + ); + } + + let (binder_clause, generic_params) = self.lower_closure_binder(binder); + let outer_decl = FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; @@ -952,20 +991,22 @@ impl<'hir> LoweringContext<'_, 'hir> { body_id }); - self.with_lifetime_binder(closure_id, &[], |this, bound_generic_params| { + self.with_lifetime_binder(closure_id, generic_params, |this, bound_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 // closure argument types. let fn_decl = this.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None); - hir::ExprKind::Closure { + let c = self.arena.alloc(hir::Closure { + binder: binder_clause, capture_clause, bound_generic_params, fn_decl, body, fn_decl_span: this.lower_span(fn_decl_span), movability: None, - } + }); + hir::ExprKind::Closure(c) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fdf60e60914..a1bf0f94964 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -31,7 +31,7 @@ //! in the HIR, especially for multiple identifiers. #![feature(box_patterns)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] @@ -427,7 +427,7 @@ pub fn lower_to_hir<'hir>(tcx: TyCtxt<'hir>, (): ()) -> hir::Crate<'hir> { sess.time("drop_ast", || std::mem::drop(krate)); // Discard hygiene data, which isn't required after lowering to HIR. - if !sess.opts.debugging_opts.keep_hygiene_data { + if !sess.opts.unstable_opts.keep_hygiene_data { rustc_span::hygiene::clear_syntax_context_map(); } @@ -699,7 +699,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Intercept all spans entering HIR. /// Mark a span as relative to the current owning item. fn lower_span(&self, span: Span) -> Span { - if self.tcx.sess.opts.debugging_opts.incremental_relative_spans { + if self.tcx.sess.opts.unstable_opts.incremental_relative_spans { span.with_parent(Some(self.current_hir_id_owner)) } else { // Do not make spans relative when not using incremental compilation. @@ -1835,7 +1835,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug!(?self.captured_lifetimes); let name = match res { LifetimeRes::Param { mut param, binder } => { - debug_assert_ne!(ident.name, kw::UnderscoreLifetime); let p_name = ParamName::Plain(ident); if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { if !captured_lifetimes.binders_to_ignore.contains(&binder) { @@ -1884,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } hir::LifetimeName::Param(param, ParamName::Fresh) } - LifetimeRes::Anonymous { binder, elided } => { - let mut l_name = None; - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - ); - captured_lifetimes - .captures - .insert(p_def_id, (span, p_id, ParamName::Fresh, res)); - l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh)); - } - self.captured_lifetimes = Some(captured_lifetimes); - }; - l_name.unwrap_or(if elided { - hir::LifetimeName::Implicit - } else { - hir::LifetimeName::Underscore - }) - } + LifetimeRes::Infer => hir::LifetimeName::Infer, LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), @@ -2146,7 +2123,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } - let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None }; + let local = hir::Local { + hir_id, + init, + pat, + els: None, + source, + span: self.lower_span(span), + ty: None, + }; self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local))) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 3942062656f..60560b1c00e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -119,33 +119,23 @@ impl<'a> AstValidator<'a> { /// Emits an error banning the `let` expression provided in the given location. fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) { - let sess = &self.session; - if sess.opts.unstable_features.is_nightly_build() { - let err = "`let` expressions are not supported here"; - let mut diag = sess.struct_span_err(expr.span, err); - diag.note("only supported directly in conditions of `if` and `while` expressions"); - match forbidden_let_reason { - ForbiddenLetReason::GenericForbidden => {} - ForbiddenLetReason::NotSupportedOr(span) => { - diag.span_note( - span, - "`||` operators are not supported in let chain expressions", - ); - } - ForbiddenLetReason::NotSupportedParentheses(span) => { - diag.span_note( - span, - "`let`s wrapped in parentheses are not supported in a context with let \ - chains", - ); - } + let err = "`let` expressions are not supported here"; + let mut diag = self.session.struct_span_err(expr.span, err); + diag.note("only supported directly in conditions of `if` and `while` expressions"); + match forbidden_let_reason { + ForbiddenLetReason::GenericForbidden => {} + ForbiddenLetReason::NotSupportedOr(span) => { + diag.span_note(span, "`||` operators are not supported in let chain expressions"); + } + ForbiddenLetReason::NotSupportedParentheses(span) => { + diag.span_note( + span, + "`let`s wrapped in parentheses are not supported in a context with let \ + chains", + ); } - diag.emit(); - } else { - sess.struct_span_err(expr.span, "expected expression, found statement (`let`)") - .note("variable declaration using `let` is a statement") - .emit(); } + diag.emit(); } fn check_gat_where( @@ -1282,7 +1272,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(item.ident); self.visit_generics(generics); - self.with_banned_tilde_const(|this| { + self.with_tilde_const_allowed(|this| { walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) }); walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait); @@ -1597,6 +1587,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } + if let FnKind::Closure(ClosureBinder::For { generic_params, .. }, ..) = fk { + self.check_late_bound_lifetime_defs(generic_params); + } + if let FnKind::Fn( _, _, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index fd2dd6cf6c7..ad9ed798e55 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -62,9 +62,9 @@ impl<'a> PostExpansionVisitor<'a> { let ast::StrLit { symbol_unescaped, span, .. } = abi; if let ast::Const::Yes(_) = constness { - match symbol_unescaped.as_str() { + match symbol_unescaped { // Stable - "Rust" | "C" => {} + sym::Rust | sym::C => {} abi => gate_feature_post!( &self, const_extern_fn, @@ -274,10 +274,12 @@ impl<'a> PostExpansionVisitor<'a> { ); } abi => { - self.sess.parse_sess.span_diagnostic.delay_span_bug( - span, - &format!("unrecognized ABI not caught in lowering: {}", abi), - ); + if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) { + self.sess.parse_sess.span_diagnostic.delay_span_bug( + span, + &format!("unrecognized ABI not caught in lowering: {}", abi), + ); + } } } } @@ -402,8 +404,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(self, rustdoc_internals, attr.span, msg); } - if nested_meta.has_name(sym::tuple_variadic) { - let msg = "`#[doc(tuple_variadic)]` is meant for internal use only"; + if nested_meta.has_name(sym::fake_variadic) { + let msg = "`#[doc(fake_variadic)]` is meant for internal use only"; gate_feature_post!(self, rustdoc_internals, attr.span, msg); } } @@ -738,12 +740,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { "`if let` guards are experimental", "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`" ); - gate_all!(let_chains, "`let` expressions in this position are unstable"); gate_all!( async_closure, "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); + gate_all!( + closure_lifetime_binder, + "`for<...>` binders for closures are experimental", + "consider removing `for<...>`" + ); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(generators, "yield syntax is experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 9d52c32885d..2e3ac0c6018 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -8,7 +8,7 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9f44f1b6cc2..ead38caee28 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -389,6 +389,7 @@ impl<'a> State<'a> { self.bclose(expr.span, empty); } ast::ExprKind::Closure( + ref binder, capture_clause, asyncness, movability, @@ -396,6 +397,7 @@ impl<'a> State<'a> { ref body, _, ) => { + self.print_closure_binder(binder); self.print_movability(movability); self.print_asyncness(asyncness); self.print_capture_clause(capture_clause); @@ -594,6 +596,15 @@ impl<'a> State<'a> { self.end(); // Close enclosing cbox. } + fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) { + match binder { + ast::ClosureBinder::NotPresent => {} + ast::ClosureBinder::For { generic_params, .. } => { + self.print_formal_generic_params(&generic_params) + } + } + } + fn print_movability(&mut self, movability: ast::Movability) { match movability { ast::Movability::Static => self.word_space("static"), diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index ce7c0eb72cd..10a9cfb626e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -135,9 +135,42 @@ impl ConstStability { #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] pub enum StabilityLevel { - // Reason for the current stability level and the relevant rust-lang issue - Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }, - Stable { since: Symbol }, + /// `#[unstable]` + Unstable { + /// Reason for the current stability level. + reason: UnstableReason, + /// Relevant `rust-lang/rust` issue. + issue: Option<NonZeroU32>, + is_soft: bool, + /// If part of a feature is stabilized and a new feature is added for the remaining parts, + /// then the `implied_by` attribute is used to indicate which now-stable feature previously + /// contained a item. + /// + /// ```pseudo-Rust + /// #[unstable(feature = "foo", issue = "...")] + /// fn foo() {} + /// #[unstable(feature = "foo", issue = "...")] + /// fn foobar() {} + /// ``` + /// + /// ...becomes... + /// + /// ```pseudo-Rust + /// #[stable(feature = "foo", since = "1.XX.X")] + /// fn foo() {} + /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] + /// fn foobar() {} + /// ``` + implied_by: Option<Symbol>, + }, + /// `#[stable]` + Stable { + /// Rust release which stabilized this feature. + since: Symbol, + /// Is this item allowed to be referred to on stable, despite being contained in unstable + /// modules? + allowed_through_unstable_modules: bool, + }, } impl StabilityLevel { @@ -149,6 +182,32 @@ impl StabilityLevel { } } +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum UnstableReason { + None, + Default, + Some(Symbol), +} + +impl UnstableReason { + fn from_opt_reason(reason: Option<Symbol>) -> Self { + // UnstableReason::Default constructed manually + match reason { + Some(r) => Self::Some(r), + None => Self::None, + } + } + + pub fn to_opt_reason(&self) -> Option<Symbol> { + match self { + Self::None => None, + Self::Default => Some(sym::unstable_location_reason_default), + Self::Some(r) => Some(*r), + } + } +} + /// Collects stability info from all stability attributes in `attrs`. /// Returns `None` if no stability attributes are found. pub fn find_stability( @@ -172,6 +231,7 @@ where let mut stab: Option<(Stability, Span)> = None; let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; + let mut allowed_through_unstable_modules = false; let diagnostic = &sess.parse_sess.span_diagnostic; @@ -182,6 +242,7 @@ where sym::unstable, sym::stable, sym::rustc_promotable, + sym::rustc_allowed_through_unstable_modules, ] .iter() .any(|&s| attr.has_name(s)) @@ -193,6 +254,8 @@ where if attr.has_name(sym::rustc_promotable) { promotable = true; + } else if attr.has_name(sym::rustc_allowed_through_unstable_modules) { + allowed_through_unstable_modules = true; } // attributes with data else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta { @@ -239,6 +302,7 @@ where let mut issue = None; let mut issue_num = None; let mut is_soft = false; + let mut implied_by = None; for meta in metas { let Some(mi) = meta.meta_item() else { handle_errors( @@ -304,6 +368,11 @@ where } is_soft = true; } + sym::implied_by => { + if !get(mi, &mut implied_by) { + continue 'outer; + } + } _ => { handle_errors( &sess.parse_sess, @@ -328,7 +397,12 @@ where ); continue; } - let level = Unstable { reason, issue: issue_num, is_soft }; + let level = Unstable { + reason: UnstableReason::from_opt_reason(reason), + issue: issue_num, + is_soft, + implied_by, + }; if sym::unstable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { @@ -387,7 +461,7 @@ where meta.span(), AttrError::UnknownMetaItem( pprust::path_to_string(&mi.path), - &["since", "note"], + &["feature", "since"], ), ); continue 'outer; @@ -406,7 +480,7 @@ where match (feature, since) { (Some(feature), Some(since)) => { - let level = Stable { since }; + let level = Stable { since, allowed_through_unstable_modules: false }; if sym::stable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { @@ -447,6 +521,27 @@ where } } + if allowed_through_unstable_modules { + if let Some(( + Stability { + level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, + .. + }, + _, + )) = stab + { + *allowed_through_unstable_modules = true; + } else { + struct_span_err!( + diagnostic, + item_sp, + E0789, + "`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute" + ) + .emit(); + } + } + (stab, const_stab) } @@ -856,7 +951,6 @@ pub enum ReprAttr { ReprSimd, ReprTransparent, ReprAlign(u32), - ReprNoNiche, } #[derive(Eq, PartialEq, Debug, Copy, Clone)] @@ -904,7 +998,6 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { sym::packed => Some(ReprPacked(1)), sym::simd => Some(ReprSimd), sym::transparent => Some(ReprTransparent), - sym::no_niche => Some(ReprNoNiche), sym::align => { let mut err = struct_span_err!( diagnostic, @@ -943,7 +1036,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { Ok(literal) => acc.push(ReprPacked(literal)), Err(message) => literal_error = Some(message), }; - } else if matches!(name, sym::C | sym::simd | sym::transparent | sym::no_niche) + } else if matches!(name, sym::C | sym::simd | sym::transparent) || int_type_of_word(name).is_some() { recognised = true; @@ -1001,7 +1094,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { } else { if matches!( meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche + sym::C | sym::simd | sym::transparent ) || int_type_of_word(meta_item.name_or_empty()).is_some() { recognised = true; @@ -1039,7 +1132,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { .emit(); } else if matches!( meta_item.name_or_empty(), - sym::C | sym::simd | sym::transparent | sym::no_niche + sym::C | sym::simd | sym::transparent ) || int_type_of_word(meta_item.name_or_empty()).is_some() { recognised = true; diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index c3f9f0cf362..afe1d191d10 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,7 +4,7 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #[macro_use] diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 97daad201d9..efc17a173f4 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -2,7 +2,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_index::vec::IndexVec; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; use rustc_middle::mir::Body; use rustc_middle::ty::{self, TyCtxt}; @@ -31,7 +31,7 @@ pub fn get_body_with_borrowck_facts<'tcx>( def: ty::WithOptConstParam<LocalDefId>, ) -> BodyWithBorrowckFacts<'tcx> { let (input_body, promoted) = tcx.mir_promoted(def); - tcx.infer_ctxt().with_opaque_type_inference(def.did).enter(|infcx| { + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| { let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexVec<_, _> = &promoted.borrow(); *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap() diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 687ff0fb505..1b3c6cac9c4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; @@ -23,7 +23,7 @@ use rustc_middle::ty::{ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::sym; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::TraitEngineExt as _; @@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { from_closure: false, region_name: RegionName { - source: - RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), .. }, span, @@ -1500,7 +1499,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | BorrowExplanation::UsedLaterInLoop(..) | BorrowExplanation::UsedLaterWhenDropped { .. } => { // Only give this note and suggestion if it could be relevant. - err.note("consider using a `let` binding to create a longer lived value"); + let sm = self.infcx.tcx.sess.source_map(); + let mut suggested = false; + let msg = "consider using a `let` binding to create a longer lived value"; + + /// We check that there's a single level of block nesting to ensure always correct + /// suggestions. If we don't, then we only provide a free-form message to avoid + /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. + /// We could expand the analysis to suggest hoising all of the relevant parts of + /// the users' code to make the code compile, but that could be too much. + struct NestedStatementVisitor { + span: Span, + current: usize, + found: usize, + } + + impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { + fn visit_block(&mut self, block: &hir::Block<'tcx>) { + self.current += 1; + walk_block(self, block); + self.current -= 1; + } + fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { + if self.span == expr.span { + self.found = self.current; + } + walk_expr(self, expr); + } + } + let source_info = self.body.source_info(location); + if let Some(scope) = self.body.source_scopes.get(source_info.scope) + && let ClearCrossCrate::Set(scope_data) = &scope.local_data + && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) + && let Some(id) = node.body_id() + && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind + { + for stmt in block.stmts { + let mut visitor = NestedStatementVisitor { + span: proper_span, + current: 0, + found: 0, + }; + visitor.visit_stmt(stmt); + if visitor.found == 0 + && stmt.span.contains(proper_span) + && let Some(p) = sm.span_to_margin(stmt.span) + && let Ok(s) = sm.span_to_snippet(proper_span) + { + let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); + err.multipart_suggestion_verbose( + msg, + vec![ + (stmt.span.shrink_to_lo(), addition), + (proper_span, "binding".to_string()), + ], + Applicability::MaybeIncorrect, + ); + suggested = true; + break; + } + } + } + if !suggested { + err.note(msg); + } } _ => {} } @@ -1598,21 +1660,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let return_ty = tcx.erase_regions(return_ty); // to avoid panics - if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) { - if self + if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) + && self .infcx .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env) .must_apply_modulo_regions() - { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { - err.span_suggestion_hidden( - return_span, - "use `.collect()` to allocate the iterator", - format!("{snippet}.collect::<Vec<_>>()"), - Applicability::MaybeIncorrect, - ); - } - } + { + err.span_suggestion_hidden( + return_span.shrink_to_hi(), + "use `.collect()` to allocate the iterator", + ".collect::<Vec<_>>()", + Applicability::MaybeIncorrect, + ); } } @@ -1702,7 +1761,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span: Span, name: &Option<String>, upvar_span: Span, - upvar_name: &str, + upvar_name: Symbol, escape_span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { let tcx = self.infcx.tcx; diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 230ccf51990..72aee0267ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::{self, RegionVid, TyCtxt}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use crate::region_infer::BlameConstraint; @@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ) { if let ConstraintCategory::OpaqueType = category { let suggestable_name = - if region_name.was_named() { region_name.to_string() } else { "'_".to_string() }; + if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; let msg = format!( "you can add a bound to the {}to make it last less than `'static` and match `{}`", diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 9c7671eee38..53c07a3d481 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -891,7 +891,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did); let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); - if let hir::ExprKind::Closure { body, fn_decl_span, .. } = expr { + if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr { for (captured_place, place) in self .infcx .tcx @@ -904,11 +904,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if target_place == place.as_ref() => { debug!("closure_span: found captured local {:?}", place); - let body = self.infcx.tcx.hir().body(*body); + let body = self.infcx.tcx.hir().body(body); let generator_kind = body.generator_kind(); return Some(( - *fn_decl_span, + fn_decl_span, generator_kind, captured_place.get_capture_kind_span(self.infcx.tcx), captured_place.get_path_span(self.infcx.tcx), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 5d3997289bb..176090c3b7a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,6 +1,6 @@ //! Error reporting machinery for lifetime errors. -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::Region; use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::sym; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use crate::borrowck_errors; @@ -79,6 +78,8 @@ pub(crate) enum RegionErrorKind<'tcx> { span: Span, /// The hidden type. hidden_ty: Ty<'tcx>, + /// The opaque type. + key: ty::OpaqueTypeKey<'tcx>, /// The unexpected region. member_region: ty::Region<'tcx>, }, @@ -181,11 +182,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx); let origin = RelateParamBound(type_test_span, generic_ty, None); self.buffer_error(self.infcx.construct_generic_bound_failure( + self.body.source.def_id().expect_local(), type_test_span, Some(origin), type_test.generic_kind, lower_bound_region, - self.body.source.def_id().as_local(), )); } else { // FIXME. We should handle this case better. It @@ -206,14 +207,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => { + RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); + let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); self.buffer_error(unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, named_region, + named_key, )); } @@ -758,7 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { return; }; - let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() }; + let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime }; let arg = match param.param.pat.simple_ident() { Some(simple_ident) => format!("argument `{}`", simple_ident), @@ -770,7 +773,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.infcx.tcx, diag, fn_returns, - lifetime, + lifetime.to_string(), Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), @@ -851,13 +854,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); - multi_span.push_span_label( - *span, - "this has an implicit `'static` lifetime requirement".to_string(), - ); + multi_span + .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); multi_span.push_span_label( ident.span, - "calling this method introduces the `impl`'s 'static` requirement".to_string(), + "calling this method introduces the `impl`'s 'static` requirement", ); err.span_note(multi_span, "the used `impl` has a `'static` requirement"); err.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index e60e11f11df..0662d4d882f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource { /// The `'static` region. Static, /// The free region corresponding to the environment of a closure. - SynthesizedFreeEnvRegion(Span, String), + SynthesizedFreeEnvRegion(Span, &'static str), /// The region corresponding to an argument. AnonRegionFromArgument(RegionNameHighlight), /// The region corresponding to a closure upvar. - AnonRegionFromUpvar(Span, String), + AnonRegionFromUpvar(Span, Symbol), /// The region corresponding to the return type of a closure. - AnonRegionFromOutput(RegionNameHighlight, String), + AnonRegionFromOutput(RegionNameHighlight, &'static str), /// The region from a type yielded by a generator. AnonRegionFromYieldTy(Span, String), /// An anonymous region from an async fn. @@ -110,7 +110,7 @@ impl RegionName { } RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { diag.span_label(*span, format!("lifetime `{self}` represents this closure's body")); - diag.note(note); + diag.note(*note); } RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( span, @@ -325,7 +325,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // Can't have BrEnv in functions, constants or generators. bug!("BrEnv outside of closure."); }; - let hir::ExprKind::Closure { fn_decl_span, .. } + let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) = tcx.hir().expect_expr(self.mir_hir_id()).kind else { bug!("Closure is not defined by a closure expr"); @@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: region_name, - source: RegionNameSource::SynthesizedFreeEnvRegion( - fn_decl_span, - note.to_string(), - ), + source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note), }) } @@ -592,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::LifetimeName::Param(_, hir::ParamName::Fresh) | hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => { + | hir::LifetimeName::Infer => { // In this case, the user left off the lifetime; so // they wrote something like: // @@ -678,7 +674,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: region_name, - source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()), + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), }) } @@ -701,16 +697,16 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) { hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl, body, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }), .. }) => { let (mut span, mut hir_ty) = match fn_decl.output { hir::FnRetTy::DefaultReturn(_) => { - (tcx.sess.source_map().end_point(*fn_decl_span), None) + (tcx.sess.source_map().end_point(fn_decl_span), None) } hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)), }; - let mir_description = match hir.body(*body).generator_kind { + let mir_description = match hir.body(body).generator_kind { Some(hir::GeneratorKind::Async(gen)) => match gen { hir::AsyncGeneratorKind::Block => " of async block", hir::AsyncGeneratorKind::Closure => " of async closure", @@ -756,7 +752,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: self.synthesize_region_name(), - source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()), + source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description), }) } @@ -841,9 +837,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let yield_span = match tcx.hir().get(self.mir_hir_id()) { hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. - }) => (tcx.sess.source_map().end_point(*fn_decl_span)), + }) => (tcx.sess.source_map().end_point(fn_decl_span)), _ => self.body.span, }; diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 7f0a637c9d3..22134d5a71c 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -40,7 +40,7 @@ pub(crate) trait AllFactsExt { impl AllFactsExt for AllFacts { /// Return fn enabled(tcx: TyCtxt<'_>) -> bool { - tcx.sess.opts.debugging_opts.nll_facts || tcx.sess.opts.debugging_opts.polonius + tcx.sess.opts.unstable_opts.nll_facts || tcx.sess.opts.unstable_opts.polonius } fn write_to_dir( diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5bda6a41ced..3e2d57ae00c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,7 +2,7 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] @@ -24,7 +24,7 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::ChunkedBitSet; use rustc_index::vec::IndexVec; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, @@ -130,11 +130,14 @@ fn mir_borrowck<'tcx>( debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id())); let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner; - let opt_closure_req = tcx.infer_ctxt().with_opaque_type_inference(hir_owner).enter(|infcx| { - let input_body: &Body<'_> = &input_body.borrow(); - let promoted: &IndexVec<_, _> = &promoted.borrow(); - do_mir_borrowck(&infcx, input_body, promoted, false).0 - }); + let opt_closure_req = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner)) + .enter(|infcx| { + let input_body: &Body<'_> = &input_body.borrow(); + let promoted: &IndexVec<_, _> = &promoted.borrow(); + do_mir_borrowck(&infcx, input_body, promoted, false).0 + }); debug!("mir_borrowck done"); tcx.arena.alloc(opt_closure_req) @@ -231,7 +234,7 @@ fn do_mir_borrowck<'a, 'tcx>( let borrow_set = Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); - let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.debugging_opts.polonius; + let use_polonius = return_body_with_facts || infcx.tcx.sess.opts.unstable_opts.polonius; // Compute non-lexical lifetimes. let nll::NllOutput { diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index e91fcf1472d..43253a2aab0 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -38,6 +38,8 @@ pub(crate) struct NllMemberConstraint<'tcx> { /// The hidden type in which `R0` appears. (Used in error reporting.) pub(crate) hidden_ty: Ty<'tcx>, + pub(crate) key: ty::OpaqueTypeKey<'tcx>, + /// The region `R0`. pub(crate) member_region_vid: ty::RegionVid, @@ -90,6 +92,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { member_region_vid, definition_span: m_c.definition_span, hidden_ty: m_c.hidden_ty, + key: m_c.key, start_index, end_index, }); diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 3a919e954a4..0961203d76d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -1,7 +1,7 @@ //! The entry point of the NLL borrow checker. use rustc_data_structures::vec_map::VecMap; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere}; @@ -44,7 +44,7 @@ pub type PoloniusOutput = Output<RustcFacts>; /// closure requirements to propagate, and any generated errors. pub(crate) struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, - pub opaque_type_values: VecMap<DefId, OpaqueHiddenType<'tcx>>, + pub opaque_type_values: VecMap<LocalDefId, OpaqueHiddenType<'tcx>>, pub polonius_input: Option<Box<AllFacts>>, pub polonius_output: Option<Rc<PoloniusOutput>>, pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>, @@ -278,9 +278,9 @@ pub(crate) fn compute_regions<'cx, 'tcx>( // Dump facts if requested. let polonius_output = all_facts.as_ref().and_then(|all_facts| { - if infcx.tcx.sess.opts.debugging_opts.nll_facts { + if infcx.tcx.sess.opts.unstable_opts.nll_facts { let def_path = infcx.tcx.def_path(def_id); - let dir_path = PathBuf::from(&infcx.tcx.sess.opts.debugging_opts.nll_facts_dir) + let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir) .join(def_path.to_filename_friendly_no_crate()); all_facts.write_to_dir(dir_path, location_table).unwrap(); } @@ -373,7 +373,7 @@ pub(super) fn dump_annotation<'a, 'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'_>>, - opaque_type_values: &VecMap<DefId, OpaqueHiddenType<'tcx>>, + opaque_type_values: &VecMap<LocalDefId, OpaqueHiddenType<'tcx>>, errors: &mut crate::error::BorrowckErrors<'tcx>, ) { let tcx = infcx.tcx; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0cf04b369de..2894c6d29ec 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -589,7 +589,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // In Polonius mode, the errors about missing universal region relations are in the output // and need to be emitted or propagated. Otherwise, we need to check whether the // constraints were too strong, and if so, emit or propagate those errors. - if infcx.tcx.sess.opts.debugging_opts.polonius { + if infcx.tcx.sess.opts.unstable_opts.polonius { self.check_polonius_subset_errors( body, outlives_requirements.as_mut(), @@ -1763,6 +1763,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { span: m_c.definition_span, hidden_ty: m_c.hidden_ty, + key: m_c.key, member_region, }); } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 7c1fa28b8df..d6712b6a479 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,10 +1,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; -use rustc_infer::infer::InferCtxt; use rustc_infer::infer::TyCtxtInferExt as _; +use rustc_infer::infer::{DefiningAnchor, InferCtxt}; use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; @@ -63,8 +63,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'tcx>, opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>, - ) -> VecMap<DefId, OpaqueHiddenType<'tcx>> { - let mut result: VecMap<DefId, OpaqueHiddenType<'tcx>> = VecMap::new(); + ) -> VecMap<LocalDefId, OpaqueHiddenType<'tcx>> { + let mut result: VecMap<LocalDefId, OpaqueHiddenType<'tcx>> = VecMap::new(); for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls { let substs = opaque_type_key.substs; debug!(?concrete_type, ?substs); @@ -235,7 +235,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // lifetimes with 'static and remapping only those used in the // `impl Trait` return type, resulting in the parameters // shifting. - let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); + let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id()); debug!(?id_substs); let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); @@ -246,7 +246,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // after producing an error for each of them. let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( self.tcx, - def_id, + opaque_type_key, map, instantiated_ty.ty, instantiated_ty.span, @@ -268,60 +268,66 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // This logic duplicates most of `check_opaque_meets_bounds`. // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. let param_env = self.tcx.param_env(def_id); - let body_id = self.tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()); - self.tcx.infer_ctxt().enter(move |infcx| { - // Require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) - .to_predicate(infcx.tcx); - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - - // Require that the hidden type actually fulfills all the bounds of the opaque type, even without - // the bounds that the function supplies. - match infcx.register_hidden_type( - OpaqueTypeKey { def_id, substs: id_substs }, - ObligationCause::misc(instantiated_ty.span, body_id), - param_env, - definition_ty, - origin, - ) { - Ok(infer_ok) => { - for obligation in infer_ok.obligations { - fulfillment_cx.register_predicate_obligation(&infcx, obligation); + let body_id = self.tcx.local_def_id_to_hir_id(def_id); + // HACK This bubble is required for this tests to pass: + // type-alias-impl-trait/issue-67844-nested-opaque.rs + self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter( + move |infcx| { + // Require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = + ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) + .to_predicate(infcx.tcx); + let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + + // Require that the hidden type actually fulfills all the bounds of the opaque type, even without + // the bounds that the function supplies. + match infcx.register_hidden_type( + OpaqueTypeKey { def_id, substs: id_substs }, + ObligationCause::misc(instantiated_ty.span, body_id), + param_env, + definition_ty, + origin, + ) { + Ok(infer_ok) => { + for obligation in infer_ok.obligations { + fulfillment_cx.register_predicate_obligation(&infcx, obligation); + } + } + Err(err) => { + infcx + .report_mismatched_types( + &ObligationCause::misc(instantiated_ty.span, body_id), + self.tcx.mk_opaque(def_id.to_def_id(), id_substs), + definition_ty, + err, + ) + .emit(); } } - Err(err) => { - infcx - .report_mismatched_types( - &ObligationCause::misc(instantiated_ty.span, body_id), - self.tcx.mk_opaque(def_id, id_substs), - definition_ty, - err, - ) - .emit(); - } - } - fulfillment_cx.register_predicate_obligation( - &infcx, - Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), - ); + fulfillment_cx.register_predicate_obligation( + &infcx, + Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), + ); - // Check that all obligations are satisfied by the implementation's - // version. - let errors = fulfillment_cx.select_all_or_error(&infcx); + // Check that all obligations are satisfied by the implementation's + // version. + let errors = fulfillment_cx.select_all_or_error(&infcx); - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // This is still required for many(half of the tests in ui/type-alias-impl-trait) + // tests to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - if errors.is_empty() { - definition_ty - } else { - infcx.report_fulfillment_errors(&errors, None, false); - self.tcx.ty_error() - } - }) + if errors.is_empty() { + definition_ty + } else { + infcx.report_fulfillment_errors(&errors, None, false); + self.tcx.ty_error() + } + }, + ) } else { definition_ty } @@ -423,7 +429,7 @@ fn check_opaque_type_parameter_valid( struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, - opaque_type_def_id: DefId, + key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, map_missing_regions_to_empty: bool, @@ -437,14 +443,14 @@ struct ReverseMapper<'tcx> { impl<'tcx> ReverseMapper<'tcx> { fn new( tcx: TyCtxt<'tcx>, - opaque_type_def_id: DefId, + key: ty::OpaqueTypeKey<'tcx>, map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, hidden_ty: Ty<'tcx>, span: Span, ) -> Self { Self { tcx, - opaque_type_def_id, + key, map, map_missing_regions_to_empty: false, hidden_ty: Some(hidden_ty), @@ -498,7 +504,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { } } - let generics = self.tcx().generics_of(self.opaque_type_def_id); + let generics = self.tcx().generics_of(self.key.def_id); match self.map.get(&r.into()).map(|k| k.unpack()) { Some(GenericArgKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), @@ -507,9 +513,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { if let Some(hidden_ty) = self.hidden_ty.take() { unexpected_hidden_region_diagnostic( self.tcx, - self.tcx.def_span(self.opaque_type_def_id), + self.tcx.def_span(self.key.def_id), hidden_ty, r, + self.key, ) .emit(); } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 55c0bf05b48..6cfe5efb688 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -39,7 +39,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?; - if let Some(data) = &constraints { + if let Some(data) = constraints { self.push_region_constraints(locations, category, data); } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 3f9c0cecccc..16796091830 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -22,6 +22,16 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, + /// Each RBP `GK: 'a` is assumed to be true. These encode + /// relationships like `T: 'a` that are added via implicit bounds + /// or the `param_env`. + /// + /// Each region here is guaranteed to be a key in the `indices` + /// map. We use the "original" regions (i.e., the keys from the + /// map, and not the values) because the code in + /// `process_registered_region_obligations` has some special-cased + /// logic expecting to see (e.g.) `ReStatic`, and if we supplied + /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, 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 421ef5be812..cc0318ede54 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -2,6 +2,7 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; @@ -34,18 +35,6 @@ pub(crate) struct UniversalRegionRelations<'tcx> { inverse_outlives: TransitiveRelation<RegionVid>, } -/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to -/// be true. These encode relationships like `T: 'a` that are -/// added via implicit bounds. -/// -/// Each region here is guaranteed to be a key in the `indices` -/// map. We use the "original" regions (i.e., the keys from the -/// map, and not the values) because the code in -/// `process_registered_region_obligations` has some special-cased -/// logic expecting to see (e.g.) `ReStatic`, and if we supplied -/// our special inference variable there, we would mess that up. -type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; - /// As part of computing the free region relations, we also have to /// normalize the input-output types, which we then need later. So we /// return those. This vector consists of first the input types and @@ -71,7 +60,7 @@ pub(crate) fn create<'tcx>( implicit_region_bound, constraints, universal_regions: universal_regions.clone(), - region_bound_pairs: Vec::new(), + region_bound_pairs: Default::default(), relations: UniversalRegionRelations { universal_regions: universal_regions.clone(), outlives: Default::default(), @@ -335,7 +324,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { /// the same time, compute and add any implied bounds that come /// from this local. #[instrument(level = "debug", skip(self))] - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<Rc<QueryRegionConstraints<'tcx>>> { + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .param_env .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) @@ -371,11 +360,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 2a6ca5246da..4431a2e8ec6 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -225,7 +225,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!("{:?} normalized to {:?}", t, norm_ty); - for data in constraints.into_iter().collect::<Vec<_>>() { + for data in constraints { ConstraintConversion::new( self.infcx, &self.borrowck_context.universal_regions, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 3795378b568..42b577175e4 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -98,7 +98,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { struct DropData<'tcx> { dropck_result: DropckOutlivesResult<'tcx>, - region_constraint_data: Option<Rc<QueryRegionConstraints<'tcx>>>, + region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>, } struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 42bddd1b6ed..a2205c3613d 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -534,8 +534,8 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let mut template_strs = Vec::with_capacity(args.templates.len()); - for template_expr in args.templates.into_iter() { - if !template.is_empty() { + for (i, template_expr) in args.templates.into_iter().enumerate() { + if i != 0 { template.push(ast::InlineAsmTemplatePiece::String("\n".to_string())); } @@ -656,7 +656,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let span = arg_spans.next().unwrap_or(template_sp); let operand_idx = match arg.position { - parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => { + parse::ArgumentIs(idx, _) | parse::ArgumentImplicitlyIs(idx) => { if idx >= args.operands.len() || named_pos.contains_key(&idx) || args.reg_args.contains(&idx) diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 9e50d33486c..01152ff7df5 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Block(_, _) | ExprKind::Box(_) | ExprKind::Break(_, _) - | ExprKind::Closure(_, _, _, _, _, _) + | ExprKind::Closure(_, _, _, _, _, _, _) | ExprKind::ConstBlock(_) | ExprKind::Continue(_) | ExprKind::Err diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 9cd72ed0c67..7755ff779c4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -148,7 +148,7 @@ fn cs_clone_simple( ), } } - BlockOrExpr::new_mixed(stmts, cx.expr_deref(trait_span, cx.expr_self(trait_span))) + BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))) } fn cs_clone( @@ -161,7 +161,7 @@ fn cs_clone( let all_fields; let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { - let args = vec![cx.expr_addr_of(field.span, field.self_expr.clone())]; + let args = vec![field.self_expr.clone()]; cx.expr_call_global(field.span, fn_path.clone(), args) }; @@ -177,9 +177,7 @@ fn cs_clone( all_fields = af; vdata = &variant.data; } - EnumNonMatchingCollapsed(..) => { - cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,)) - } + EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)), StaticEnum(..) | StaticStruct(..) => { cx.span_bug(trait_span, &format!("associated function in `derive({})`", name)) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 859e995356e..1612be86237 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -63,10 +63,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl let [other_expr] = &field.other_selflike_exprs[..] else { cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); }; - let args = vec![ - cx.expr_addr_of(field.span, field.self_expr.clone()), - cx.expr_addr_of(field.span, other_expr.clone()), - ]; + let args = vec![field.self_expr.clone(), other_expr.clone()]; cx.expr_call_global(field.span, cmp_path.clone(), args) } CsFold::Combine(span, expr1, expr2) => { @@ -76,16 +73,6 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) } CsFold::Fieldless => cx.expr_path(equal_path.clone()), - CsFold::EnumNonMatching(span, tag_tuple) => { - if tag_tuple.len() != 2 { - cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`") - } else { - let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0])); - let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1])); - let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); - cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt]) - } - } }, ); BlockOrExpr::new_expr(expr) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 724c639984c..0141b337726 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -2,7 +2,8 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{path_local, path_std}; -use rustc_ast::{BinOpKind, MetaItem}; +use rustc_ast::ptr::P; +use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -32,11 +33,24 @@ pub fn expand_deriving_partial_eq( let [other_expr] = &field.other_selflike_exprs[..] else { cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`"); }; - cx.expr_binary(field.span, op, field.self_expr.clone(), other_expr.clone()) + + // We received `&T` arguments. Convert them to `T` by + // stripping `&` or adding `*`. This isn't necessary for + // type checking, but it results in much better error + // messages if something goes wrong. + let convert = |expr: &P<Expr>| { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = + &expr.kind + { + inner.clone() + } else { + cx.expr_deref(field.span, expr.clone()) + } + }; + cx.expr_binary(field.span, op, convert(&field.self_expr), convert(other_expr)) } CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2), CsFold::Fieldless => cx.expr_bool(span, base), - CsFold::EnumNonMatching(span, _tag_tuple) => cx.expr_bool(span, !base), }, ); BlockOrExpr::new_expr(expr) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 3f9843922da..2ebb01cc8a0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -71,10 +71,7 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_ let [other_expr] = &field.other_selflike_exprs[..] else { cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); }; - let args = vec![ - cx.expr_addr_of(field.span, field.self_expr.clone()), - cx.expr_addr_of(field.span, other_expr.clone()), - ]; + let args = vec![field.self_expr.clone(), other_expr.clone()]; cx.expr_call_global(field.span, partial_cmp_path.clone(), args) } CsFold::Combine(span, expr1, expr2) => { @@ -85,17 +82,6 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_ cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) } CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())), - CsFold::EnumNonMatching(span, tag_tuple) => { - if tag_tuple.len() != 2 { - cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") - } else { - let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0])); - let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1])); - let fn_partial_cmp_path = - cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); - cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt]) - } - } }, ); BlockOrExpr::new_expr(expr) diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index b99198054de..ceef893e862 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -45,7 +45,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), - EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => { + EnumTag(..) | StaticStruct(..) | StaticEnum(..) => { cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") } }; @@ -95,9 +95,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> ); args.push(name); } - // Use double indirection to make sure this works for unsized types + // Use an extra indirection to make sure this works for unsized types. let field = cx.expr_addr_of(field.span, field.self_expr.clone()); - let field = cx.expr_addr_of(field.span, field); args.push(field); } let expr = cx.expr_call_global(span, fn_path_debug, args); @@ -115,9 +114,9 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> )); } - // Use double indirection to make sure this works for unsized types - let value_ref = cx.expr_addr_of(field.span, field.self_expr.clone()); - value_exprs.push(cx.expr_addr_of(field.span, value_ref)); + // Use an extra indirection to make sure this works for unsized types. + let field = cx.expr_addr_of(field.span, field.self_expr.clone()); + value_exprs.push(field); } // `let names: &'static _ = &["field1", "field2"];` @@ -177,6 +176,6 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> stmts.push(names_let.unwrap()); } stmts.push(values_let); - BlockOrExpr::new_mixed(stmts, expr) + BlockOrExpr::new_mixed(stmts, Some(expr)) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 49dbe51f762..70167cac68a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -287,7 +287,7 @@ fn encodable_substructure( fn_emit_enum_path, vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk], ); - BlockOrExpr::new_mixed(vec![me], expr) + BlockOrExpr::new_mixed(vec![me], Some(expr)) } _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 74e18bffc2e..735017aa5a8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -21,21 +21,14 @@ //! `struct T(i32, char)`). //! - `EnumMatching`, when `Self` is an enum and all the arguments are the //! same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`) -//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments -//! are not the same variant (e.g., `None`, `Some(1)` and `None`). +//! - `EnumTag` when `Self` is an enum, for comparing the enum tags. //! - `StaticEnum` and `StaticStruct` for static methods, where the type //! being derived upon is either an enum or struct respectively. (Any //! argument with type Self is just grouped among the non-self //! arguments.) //! //! In the first two cases, the values from the corresponding fields in -//! all the arguments are grouped together. For `EnumNonMatchingCollapsed` -//! this isn't possible (different variants have different fields), so the -//! fields are inaccessible. (Previous versions of the deriving infrastructure -//! had a way to expand into code that could access them, at the cost of -//! generating exponential amounts of code; see issue #15375). There are no -//! fields with values in the static cases, so these are treated entirely -//! differently. +//! all the arguments are grouped together. //! //! The non-static cases have `Option<ident>` in several places associated //! with field `expr`s. This represents the name of the field it is @@ -142,21 +135,15 @@ //! }]) //! ``` //! -//! For `C0(a)` and `C1 {x}` , +//! For the tags, //! //! ```{.text} -//! EnumNonMatchingCollapsed( -//! &[<ident for self index value>, <ident of __arg_1 index value>]) +//! EnumTag( +//! &[<ident of self tag>, <ident of other tag>], <expr to combine with>) //! ``` -//! -//! It is the same for when the arguments are flipped to `C1 {x}` and -//! `C0(a)`; the only difference is what the values of the identifiers -//! <ident for self index value> and <ident of __arg_1 index value> will -//! be in the generated code. -//! -//! `EnumNonMatchingCollapsed` deliberately provides far less information -//! than is generally available for a given pair of variants; see #15375 -//! for discussion. +//! Note that this setup doesn't allow for the brute-force "match every variant +//! against every other variant" approach, which is bad because it produces a +//! quadratic amount of code (see #15375). //! //! ## Static //! @@ -180,10 +167,9 @@ use std::iter; use std::vec; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind}; +use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind}; use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; -use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -236,6 +222,8 @@ pub struct MethodDef<'a> { pub attributes: Vec<ast::Attribute>, /// Can we combine fieldless variants for enums into a single match arm? + /// If true, indicates that the trait operation uses the enum tag in some + /// way. pub unify_fieldless_variants: bool, pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, @@ -275,19 +263,22 @@ pub enum StaticFields { /// A summary of the possible sets of fields. pub enum SubstructureFields<'a> { + /// A non-static method with `Self` is a struct. Struct(&'a ast::VariantData, Vec<FieldInfo>), + /// Matching variants of the enum: variant index, variant count, ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>), - /// Non-matching variants of the enum, but with all state hidden from the - /// consequent code. The field is a list of `Ident`s bound to the variant - /// index values for each of the actual input `Self` arguments. - EnumNonMatchingCollapsed(&'a [Ident]), + /// The tag of an enum. The first field is a `FieldInfo` for the tags, as + /// if they were fields. The second field is the expression to combine the + /// tag expression with; it will be `None` if no match is necessary. + EnumTag(FieldInfo, Option<P<Expr>>), /// A static method where `Self` is a struct. StaticStruct(&'a ast::VariantData, StaticFields), + /// A static method where `Self` is an enum. StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), } @@ -325,8 +316,8 @@ impl BlockOrExpr { BlockOrExpr(vec![], Some(expr)) } - pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: P<Expr>) -> BlockOrExpr { - BlockOrExpr(stmts, Some(expr)) + pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: Option<P<Expr>>) -> BlockOrExpr { + BlockOrExpr(stmts, expr) } // Converts it into a block. @@ -344,7 +335,14 @@ impl BlockOrExpr { None => cx.expr_block(cx.block(span, vec![])), Some(expr) => expr, } + } else if self.0.len() == 1 + && let ast::StmtKind::Expr(expr) = &self.0[0].kind + && self.1.is_none() + { + // There's only a single statement expression. Pull it out. + expr.clone() } else { + // Multiple statements and/or expressions. cx.expr_block(self.into_block(cx, span)) } } @@ -455,7 +453,6 @@ impl<'a> TraitDef<'a> { }; let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id); - let use_temporaries = is_packed && always_copy; let newitem = match item.kind { ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def( @@ -464,11 +461,11 @@ impl<'a> TraitDef<'a> { item.ident, generics, from_scratch, - use_temporaries, is_packed, + always_copy, ), ast::ItemKind::Enum(ref enum_def, ref generics) => { - // We ignore `use_temporaries` here, because + // We ignore `is_packed`/`always_copy` here, because // `repr(packed)` enums cause an error later on. // // This can only cause further compilation errors @@ -484,8 +481,8 @@ impl<'a> TraitDef<'a> { item.ident, generics, from_scratch, - use_temporaries, is_packed, + always_copy, ) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -730,16 +727,8 @@ impl<'a> TraitDef<'a> { let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); let opt_trait_ref = Some(trait_ref); - let unused_qual = { - let word = rustc_ast::attr::mk_nested_word_item(Ident::new( - sym::unused_qualifications, - self.span, - )); - let list = rustc_ast::attr::mk_list_item(Ident::new(sym::allow, self.span), vec![word]); - cx.attribute(list) - }; - let mut a = vec![attr, unused_qual]; + let mut a = vec![attr]; a.extend(self.attributes.iter().cloned()); cx.item( @@ -766,8 +755,8 @@ impl<'a> TraitDef<'a> { type_ident: Ident, generics: &Generics, from_scratch: bool, - use_temporaries: bool, is_packed: bool, + always_copy: bool, ) -> P<ast::Item> { let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter().map(|field| field.ty.clone()).collect(); @@ -795,8 +784,8 @@ impl<'a> TraitDef<'a> { type_ident, &selflike_args, &nonselflike_args, - use_temporaries, is_packed, + always_copy, ) }; @@ -937,9 +926,7 @@ impl<'a> MethodDef<'a> { match ty { // Selflike (`&Self`) arguments only occur in non-static methods. - Ref(box Self_, _) if !self.is_static() => { - selflike_args.push(cx.expr_deref(span, arg_expr)) - } + Ref(box Self_, _) if !self.is_static() => selflike_args.push(arg_expr), Self_ => cx.span_bug(span, "`Self` in non-return position"), _ => nonselflike_args.push(arg_expr), } @@ -1008,7 +995,7 @@ impl<'a> MethodDef<'a> { /// ``` /// #[derive(PartialEq)] /// # struct Dummy; - /// struct A { x: i32, y: i32 } + /// struct A { x: u8, y: u8 } /// /// // equivalent to: /// impl PartialEq for A { @@ -1018,11 +1005,27 @@ impl<'a> MethodDef<'a> { /// } /// ``` /// But if the struct is `repr(packed)`, we can't use something like - /// `&self.x` on a packed type (as required for e.g. `Debug` and `Hash`) - /// because that might cause an unaligned ref. So we use let-destructuring - /// instead. + /// `&self.x` because that might cause an unaligned ref. So for any trait + /// method that takes a reference, if the struct impls `Copy` then we use a + /// local block to force a copy: + /// ``` + /// # struct A { x: u8, y: u8 } + /// impl PartialEq for A { + /// fn eq(&self, other: &A) -> bool { + /// // Desugars to `{ self.x }.eq(&{ other.y }) && ...` + /// { self.x } == { other.y } && { self.y } == { other.y } + /// } + /// } + /// impl Hash for A { + /// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + /// ::core::hash::Hash::hash(&{ self.x }, state); + /// ::core::hash::Hash::hash(&{ self.y }, state) + /// } + /// } + /// ``` + /// If the struct doesn't impl `Copy`, we use let-destructuring with `ref`: /// ``` - /// # struct A { x: i32, y: i32 } + /// # struct A { x: u8, y: u8 } /// impl PartialEq for A { /// fn eq(&self, other: &A) -> bool { /// let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self; @@ -1031,6 +1034,8 @@ impl<'a> MethodDef<'a> { /// } /// } /// ``` + /// This latter case only works if the fields match the alignment required + /// by the `packed(N)` attribute. (We'll get errors later on if not.) fn expand_struct_method_body<'b>( &self, cx: &mut ExtCtxt<'_>, @@ -1039,8 +1044,8 @@ impl<'a> MethodDef<'a> { type_ident: Ident, selflike_args: &[P<Expr>], nonselflike_args: &[P<Expr>], - use_temporaries: bool, is_packed: bool, + always_copy: bool, ) -> BlockOrExpr { let span = trait_.span; assert!(selflike_args.len() == 1 || selflike_args.len() == 2); @@ -1057,29 +1062,31 @@ impl<'a> MethodDef<'a> { if !is_packed { let selflike_fields = - trait_.create_struct_field_access_fields(cx, selflike_args, struct_def); + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, false); + mk_body(cx, selflike_fields) + } else if always_copy { + let selflike_fields = + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true); mk_body(cx, selflike_fields) } else { + // Neither packed nor copy. Need to use ref patterns. let prefixes: Vec<_> = (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect(); + let addr_of = always_copy; let selflike_fields = - trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, use_temporaries); + trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of); let mut body = mk_body(cx, selflike_fields); let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); - let patterns = trait_.create_struct_patterns( - cx, - struct_path, - struct_def, - &prefixes, - ast::Mutability::Not, - use_temporaries, - ); + let use_ref_pat = is_packed && !always_copy; + let patterns = + trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, use_ref_pat); // Do the let-destructuring. let mut stmts: Vec<_> = iter::zip(selflike_args, patterns) .map(|(selflike_arg_expr, pat)| { - cx.stmt_let_pat(span, pat, selflike_arg_expr.clone()) + let selflike_arg_expr = cx.expr_deref(span, selflike_arg_expr.clone()); + cx.stmt_let_pat(span, pat, selflike_arg_expr) }) .collect(); stmts.extend(std::mem::take(&mut body.0)); @@ -1119,82 +1126,128 @@ impl<'a> MethodDef<'a> { /// impl ::core::cmp::PartialEq for A { /// #[inline] /// fn eq(&self, other: &A) -> bool { - /// { - /// let __self_vi = ::core::intrinsics::discriminant_value(&*self); - /// let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other); - /// if true && __self_vi == __arg_1_vi { - /// match (&*self, &*other) { - /// (&A::A2(ref __self_0), &A::A2(ref __arg_1_0)) => - /// (*__self_0) == (*__arg_1_0), - /// _ => true, - /// } - /// } else { - /// false // catch-all handler + /// let __self_tag = ::core::intrinsics::discriminant_value(self); + /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); + /// __self_tag == __arg1_tag && + /// match (self, other) { + /// (A::A2(__self_0), A::A2(__arg1_0)) => + /// *__self_0 == *__arg1_0, + /// _ => true, /// } - /// } /// } /// } /// ``` - /// Creates a match for a tuple of all `selflike_args`, where either all - /// variants match, or it falls into a catch-all for when one variant - /// does not match. - /// - /// There are N + 1 cases because is a case for each of the N - /// variants where all of the variants match, and one catch-all for - /// when one does not match. - /// - /// As an optimization we generate code which checks whether all variants - /// match first which makes llvm see that C-like enums can be compiled into - /// a simple equality check (for PartialEq). - /// - /// The catch-all handler is provided access the variant index values - /// for each of the selflike_args, carried in precomputed variables. + /// Creates a tag check combined with a match for a tuple of all + /// `selflike_args`, with an arm for each variant with fields, possibly an + /// arm for each fieldless variant (if `!unify_fieldless_variants` is not + /// true), and possibly a default arm. fn expand_enum_method_body<'b>( &self, cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, type_ident: Ident, - mut selflike_args: Vec<P<Expr>>, + selflike_args: Vec<P<Expr>>, nonselflike_args: &[P<Expr>], ) -> BlockOrExpr { let span = trait_.span; let variants = &enum_def.variants; + // Traits that unify fieldless variants always use the tag(s). + let uses_tags = self.unify_fieldless_variants; + + // There is no sensible code to be generated for *any* deriving on a + // zero-variant enum. So we just generate a failing expression. + if variants.is_empty() { + return BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span))); + } + let prefixes = iter::once("__self".to_string()) .chain( selflike_args .iter() .enumerate() .skip(1) - .map(|(arg_count, _selflike_arg)| format!("__arg_{}", arg_count)), + .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)), ) .collect::<Vec<String>>(); - // The `vi_idents` will be bound, solely in the catch-all, to - // a series of let statements mapping each selflike_arg to an int - // value corresponding to its discriminant. - let vi_idents = prefixes - .iter() - .map(|name| { - let vi_suffix = format!("{}_vi", name); - Ident::from_str_and_span(&vi_suffix, span) - }) - .collect::<Vec<Ident>>(); + // Build a series of let statements mapping each selflike_arg + // to its discriminant value. + // + // e.g. for `PartialEq::eq` builds two statements: + // ``` + // let __self_tag = ::core::intrinsics::discriminant_value(self); + // let __arg1_tag = ::core::intrinsics::discriminant_value(other); + // ``` + let get_tag_pieces = |cx: &ExtCtxt<'_>| { + let tag_idents: Vec<_> = prefixes + .iter() + .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span)) + .collect(); - // Builds, via callback to call_substructure_method, the - // delegated expression that handles the catch-all case, - // using `__variants_tuple` to drive logic if necessary. - let catch_all_substructure = EnumNonMatchingCollapsed(&vi_idents); + let mut tag_exprs: Vec<_> = tag_idents + .iter() + .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident))) + .collect(); - let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); + let self_expr = tag_exprs.remove(0); + let other_selflike_exprs = tag_exprs; + let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs }; + + let tag_let_stmts: Vec<_> = iter::zip(&tag_idents, &selflike_args) + .map(|(&ident, selflike_arg)| { + let variant_value = deriving::call_intrinsic( + cx, + span, + sym::discriminant_value, + vec![selflike_arg.clone()], + ); + cx.stmt_let(span, false, ident, variant_value) + }) + .collect(); + + (tag_field, tag_let_stmts) + }; + + // There are some special cases involving fieldless enums where no + // match is necessary. + let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty()); + if all_fieldless { + if uses_tags && variants.len() > 1 { + // If the type is fieldless and the trait uses the tag and + // there are multiple variants, we need just an operation on + // the tag(s). + let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); + let mut tag_check = self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumTag(tag_field, None), + ); + tag_let_stmts.append(&mut tag_check.0); + return BlockOrExpr(tag_let_stmts, tag_check.1); + } + + if variants.len() == 1 { + // If there is a single variant, we don't need an operation on + // the tag(s). Just use the most degenerate result. + return self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumMatching(0, 1, &variants[0], Vec::new()), + ); + }; + } // These arms are of the form: // (Variant1, Variant1, ...) => Body1 // (Variant2, Variant2, ...) => Body2 // ... // where each tuple has length = selflike_args.len() - let mut match_arms: Vec<ast::Arm> = variants .iter() .enumerate() @@ -1203,30 +1256,22 @@ impl<'a> MethodDef<'a> { // A single arm has form (&VariantK, &VariantK, ...) => BodyK // (see "Final wrinkle" note below for why.) - let use_temporaries = false; // enums can't be repr(packed) - let fields = trait_.create_struct_pattern_fields( + let addr_of = false; // because enums can't be repr(packed) + let fields = + trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of); + + let sp = variant.span.with_ctxt(trait_.span.ctxt()); + let variant_path = cx.path(sp, vec![type_ident, variant.ident]); + let use_ref_pat = false; // because enums can't be repr(packed) + let mut subpats: Vec<_> = trait_.create_struct_patterns( cx, + variant_path, &variant.data, &prefixes, - use_temporaries, + use_ref_pat, ); - let sp = variant.span.with_ctxt(trait_.span.ctxt()); - let variant_path = cx.path(sp, vec![type_ident, variant.ident]); - let mut subpats: Vec<_> = trait_ - .create_struct_patterns( - cx, - variant_path, - &variant.data, - &prefixes, - ast::Mutability::Not, - use_temporaries, - ) - .into_iter() - .map(|p| cx.pat(span, PatKind::Ref(p, ast::Mutability::Not))) - .collect(); - - // Here is the pat = `(&VariantK, &VariantK, ...)` + // `(VariantK, VariantK, ...)` or just `VariantK`. let single_pat = if subpats.len() == 1 { subpats.pop().unwrap() } else { @@ -1256,27 +1301,28 @@ impl<'a> MethodDef<'a> { }) .collect(); + // Add a default arm to the match, if necessary. + let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); let default = match first_fieldless { Some(v) if self.unify_fieldless_variants => { - // We need a default case that handles the fieldless variants. - // The index and actual variant aren't meaningful in this case, - // so just use whatever - let substructure = EnumMatching(0, variants.len(), v, Vec::new()); + // We need a default case that handles all the fieldless + // variants. The index and actual variant aren't meaningful in + // this case, so just use dummy values. Some( self.call_substructure_method( cx, trait_, type_ident, nonselflike_args, - &substructure, + &EnumMatching(0, variants.len(), v, Vec::new()), ) .into_expr(cx, span), ) } _ if variants.len() > 1 && selflike_args.len() > 1 => { - // Since we know that all the arguments will match if we reach + // Because we know that all the arguments will match if we reach // the match expression we add the unreachable intrinsics as the - // result of the catch all which should help llvm in optimizing it + // result of the default which should help llvm in optimizing it. Some(deriving::call_unreachable(cx, span)) } _ => None, @@ -1285,111 +1331,41 @@ impl<'a> MethodDef<'a> { match_arms.push(cx.arm(span, cx.pat_wild(span), arm)); } - // We will usually need the catch-all after matching the - // tuples `(VariantK, VariantK, ...)` for each VariantK of the - // enum. But: - // - // * when there is only one Self arg, the arms above suffice - // (and the deriving we call back into may not be prepared to - // handle EnumNonMatchCollapsed), and, - // - // * when the enum has only one variant, the single arm that - // is already present always suffices. - // - // * In either of the two cases above, if we *did* add a - // catch-all `_` match, it would trigger the - // unreachable-pattern error. - // - if variants.len() > 1 && selflike_args.len() > 1 { - // Build a series of let statements mapping each selflike_arg - // to its discriminant value. - // - // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving - // with three Self args, builds three statements: - // ``` - // let __self_vi = std::intrinsics::discriminant_value(&self); - // let __arg_1_vi = std::intrinsics::discriminant_value(&arg1); - // let __arg_2_vi = std::intrinsics::discriminant_value(&arg2); - // ``` - let mut index_let_stmts: Vec<ast::Stmt> = Vec::with_capacity(vi_idents.len() + 1); - - // We also build an expression which checks whether all discriminants are equal: - // `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...` - let mut discriminant_test = cx.expr_bool(span, true); - for (i, (&ident, selflike_arg)) in iter::zip(&vi_idents, &selflike_args).enumerate() { - let selflike_addr = cx.expr_addr_of(span, selflike_arg.clone()); - let variant_value = deriving::call_intrinsic( - cx, - span, - sym::discriminant_value, - vec![selflike_addr], - ); - let let_stmt = cx.stmt_let(span, false, ident, variant_value); - index_let_stmts.push(let_stmt); - - if i > 0 { - let id0 = cx.expr_ident(span, vi_idents[0]); - let id = cx.expr_ident(span, ident); - let test = cx.expr_binary(span, BinOpKind::Eq, id0, id); - discriminant_test = if i == 1 { - test - } else { - cx.expr_binary(span, BinOpKind::And, discriminant_test, test) - }; - } - } - - let arm_expr = self - .call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - &catch_all_substructure, - ) - .into_expr(cx, span); - - // Final wrinkle: the selflike_args are expressions that deref - // down to desired places, but we cannot actually deref - // them when they are fed as r-values into a tuple - // expression; here add a layer of borrowing, turning - // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg)); - let match_arg = cx.expr(span, ast::ExprKind::Tup(selflike_args)); - - // Lastly we create an expression which branches on all discriminants being equal - // if discriminant_test { - // match (...) { - // (Variant1, Variant1, ...) => Body1 - // (Variant2, Variant2, ...) => Body2, - // ... - // _ => ::core::intrinsics::unreachable() - // } - // } - // else { - // <delegated expression referring to __self_vi, et al.> - // } - let all_match = cx.expr_match(span, match_arg, match_arms); - let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr)); - BlockOrExpr(index_let_stmts, Some(arm_expr)) - } else if variants.is_empty() { - // There is no sensible code to be generated for *any* deriving on - // a zero-variant enum. So we just generate a failing expression - // for the zero variant case. - BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span))) - } else { - // Final wrinkle: the selflike_args are expressions that deref - // down to desired places, but we cannot actually deref - // them when they are fed as r-values into a tuple - // expression; here add a layer of borrowing, turning - // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg)); + // Create a match expression with one arm per discriminant plus + // possibly a default arm, e.g.: + // match (self, other) { + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2, + // ... + // _ => ::core::intrinsics::unreachable() + // } + let get_match_expr = |mut selflike_args: Vec<P<Expr>>| { let match_arg = if selflike_args.len() == 1 { selflike_args.pop().unwrap() } else { cx.expr(span, ast::ExprKind::Tup(selflike_args)) }; - BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms))) + cx.expr_match(span, match_arg, match_arms) + }; + + // If the trait uses the tag and there are multiple variants, we need + // to add a tag check operation before the match. Otherwise, the match + // is enough. + if uses_tags && variants.len() > 1 { + let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); + + // Combine a tag check with the match. + let mut tag_check_plus_match = self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &EnumTag(tag_field, Some(get_match_expr(selflike_args))), + ); + tag_let_stmts.append(&mut tag_check_plus_match.0); + BlockOrExpr(tag_let_stmts, tag_check_plus_match.1) + } else { + BlockOrExpr(vec![], Some(get_match_expr(selflike_args))) } } @@ -1453,8 +1429,7 @@ impl<'a> TraitDef<'a> { struct_path: ast::Path, struct_def: &'a VariantData, prefixes: &[String], - mutbl: ast::Mutability, - use_temporaries: bool, + use_ref_pat: bool, ) -> Vec<P<ast::Pat>> { prefixes .iter() @@ -1462,10 +1437,10 @@ impl<'a> TraitDef<'a> { let pieces_iter = struct_def.fields().iter().enumerate().map(|(i, struct_field)| { let sp = struct_field.span.with_ctxt(self.span.ctxt()); - let binding_mode = if use_temporaries { - ast::BindingMode::ByValue(ast::Mutability::Not) + let binding_mode = if use_ref_pat { + ast::BindingMode::ByRef(ast::Mutability::Not) } else { - ast::BindingMode::ByRef(mutbl) + ast::BindingMode::ByValue(ast::Mutability::Not) }; let ident = self.mk_pattern_ident(prefix, i); let path = ident.with_span_pos(sp); @@ -1544,7 +1519,7 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt<'_>, struct_def: &'a VariantData, prefixes: &[String], - use_temporaries: bool, + addr_of: bool, ) -> Vec<FieldInfo> { self.create_fields(struct_def, |i, _struct_field, sp| { prefixes @@ -1552,7 +1527,7 @@ impl<'a> TraitDef<'a> { .map(|prefix| { let ident = self.mk_pattern_ident(prefix, i); let expr = cx.expr_path(cx.path_ident(sp, ident)); - if use_temporaries { expr } else { cx.expr_deref(sp, expr) } + if addr_of { cx.expr_addr_of(sp, expr) } else { expr } }) .collect() }) @@ -1563,20 +1538,17 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt<'_>, selflike_args: &[P<Expr>], struct_def: &'a VariantData, + copy: bool, ) -> Vec<FieldInfo> { self.create_fields(struct_def, |i, struct_field, sp| { selflike_args .iter() - .map(|mut selflike_arg| { - // We don't the need the deref, if there is one. - if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &selflike_arg.kind { - selflike_arg = inner; - } - // Note: we must use `struct_field.span` rather than `span` in the + .map(|selflike_arg| { + // Note: we must use `struct_field.span` rather than `sp` in the // `unwrap_or_else` case otherwise the hygiene is wrong and we get // "field `0` of struct `Point` is private" errors on tuple // structs. - cx.expr( + let mut field_expr = cx.expr( sp, ast::ExprKind::Field( selflike_arg.clone(), @@ -1584,7 +1556,13 @@ impl<'a> TraitDef<'a> { Ident::from_str_and_span(&i.to_string(), struct_field.span) }), ), - ) + ); + if copy { + field_expr = cx.expr_block( + cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]), + ); + } + cx.expr_addr_of(sp, field_expr) }) .collect() }) @@ -1605,11 +1583,6 @@ pub enum CsFold<'a> { // The fallback case for a struct or enum variant with no fields. Fieldless, - - /// The fallback case for non-matching enum variants. The slice is the - /// identifiers holding the variant index value for each of the `Self` - /// arguments. - EnumNonMatching(Span, &'a [Ident]), } /// Folds over fields, combining the expressions for each field in a sequence. @@ -1624,8 +1597,8 @@ pub fn cs_fold<F>( where F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>, { - match *substructure.fields { - EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => { + match substructure.fields { + EnumMatching(.., all_fields) | Struct(_, all_fields) => { if all_fields.is_empty() { return f(cx, CsFold::Fieldless); } @@ -1649,7 +1622,18 @@ where rest.iter().rfold(base_expr, op) } } - EnumNonMatchingCollapsed(tuple) => f(cx, CsFold::EnumNonMatching(trait_span, tuple)), + EnumTag(tag_field, match_expr) => { + let tag_check_expr = f(cx, CsFold::Single(tag_field)); + if let Some(match_expr) = match_expr { + if use_foldl { + f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone())) + } else { + f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr)) + } + } else { + tag_check_expr + } + } StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 4b20d87629d..4d46f7cd48a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -196,9 +196,8 @@ impl Bounds { } pub fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P<Expr>, ast::ExplicitSelf) { - // this constructs a fresh `self` path + // This constructs a fresh `self` path. let self_path = cx.expr_self(span); let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not)); - let self_expr = cx.expr_deref(span, self_path); - (self_expr, self_ty) + (self_path, self_ty) } diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 2213cd6d37d..32ae3d34478 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -1,6 +1,6 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use crate::deriving::{self, path_std, pathvec_std}; +use crate::deriving::{path_std, pathvec_std}; use rustc_ast::{MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -52,39 +52,29 @@ fn hash_substructure( let [state_expr] = substr.nonselflike_args else { cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); }; - let call_hash = |span, thing_expr| { + let call_hash = |span, expr| { let hash_path = { let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]); cx.expr_path(cx.path_global(span, strs)) }; - let ref_thing = cx.expr_addr_of(span, thing_expr); - let expr = cx.expr_call(span, hash_path, vec![ref_thing, state_expr.clone()]); + let expr = cx.expr_call(span, hash_path, vec![expr, state_expr.clone()]); cx.stmt_expr(expr) }; - let mut stmts = Vec::new(); - let fields = match substr.fields { - Struct(_, fs) | EnumMatching(_, 1, .., fs) => fs, - EnumMatching(.., fs) => { - let variant_value = deriving::call_intrinsic( - cx, - trait_span, - sym::discriminant_value, - vec![cx.expr_self(trait_span)], - ); - - stmts.push(call_hash(trait_span, variant_value)); - - fs + let (stmts, match_expr) = match substr.fields { + Struct(_, fields) | EnumMatching(.., fields) => { + let stmts = + fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect(); + (stmts, None) + } + EnumTag(tag_field, match_expr) => { + assert!(tag_field.other_selflike_exprs.is_empty()); + let stmts = vec![call_hash(tag_field.span, tag_field.self_expr.clone())]; + (stmts, match_expr.clone()) } _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`"), }; - stmts.extend( - fields - .iter() - .map(|FieldInfo { ref self_expr, span, .. }| call_hash(*span, self_expr.clone())), - ); - BlockOrExpr::new_stmts(stmts) + BlockOrExpr::new_mixed(stmts, match_expr) } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6c2ac343544..082c7893426 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -11,9 +11,11 @@ use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{InnerSpan, Span}; +use rustc_span::{BytePos, InnerSpan, Span}; use smallvec::SmallVec; +use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -29,6 +31,136 @@ enum Position { Named(Symbol, InnerSpan), } +/// Indicates how positional named argument (i.e. an named argument which is used by position +/// instead of by name) is used in format string +/// * `Arg` is the actual argument to print +/// * `Width` is width format argument +/// * `Precision` is precion format argument +/// Example: `{Arg:Width$.Precision$} +#[derive(Debug, Eq, PartialEq)] +enum PositionalNamedArgType { + Arg, + Width, + Precision, +} + +/// Contains information necessary to create a lint for a positional named argument +#[derive(Debug)] +struct PositionalNamedArg { + ty: PositionalNamedArgType, + /// The piece of the using this argument (multiple pieces can use the same argument) + cur_piece: usize, + /// The InnerSpan for in the string to be replaced with the named argument + /// This will be None when the position is implicit + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + /// The name to use instead of the position + replacement: Symbol, + /// The span for the positional named argument (so the lint can point a message to it) + positional_named_arg_span: Span, +} + +impl PositionalNamedArg { + /// Determines what span to replace with the name of the named argument + fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> { + if let Some(inner_span) = &self.inner_span_to_replace { + return Some( + cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }), + ); + } else if self.ty == PositionalNamedArgType::Arg { + // In the case of a named argument whose position is implicit, there will not be a span + // to replace. Instead, we insert the name after the `{`, which is the first character + // of arg_span. + return cx + .arg_spans + .get(self.cur_piece) + .map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()); + } + + None + } +} + +/// Encapsulates all the named arguments that have been used positionally +#[derive(Debug)] +struct PositionalNamedArgsLint { + positional_named_args: Vec<PositionalNamedArg>, +} + +impl PositionalNamedArgsLint { + /// For a given positional argument, check if the index is for a named argument. + /// + /// Since positional arguments are required to come before named arguments, if the positional + /// index is greater than or equal to the start of named arguments, we know it's a named + /// argument used positionally. + /// + /// Example: + /// println!("{} {} {2}", 0, a=1, b=2); + /// + /// In this case, the first piece (`{}`) would be ArgumentImplicitlyIs with an index of 0. The + /// total number of arguments is 3 and the number of named arguments is 2, so the start of named + /// arguments is index 1. Therefore, the index of 0 is okay. + /// + /// The second piece (`{}`) would be ArgumentImplicitlyIs with an index of 1, which is the start + /// of named arguments, and so we should add a lint to use the named argument `a`. + /// + /// The third piece (`{2}`) would be ArgumentIs with an index of 2, which is greater than the + /// start of named arguments, and so we should add a lint to use the named argument `b`. + /// + /// This same check also works for width and precision formatting when either or both are + /// CountIsParam, which contains an index into the arguments. + fn maybe_add_positional_named_arg( + &mut self, + current_positional_arg: usize, + total_args_length: usize, + format_argument_index: usize, + ty: PositionalNamedArgType, + cur_piece: usize, + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + names: &FxHashMap<Symbol, (usize, Span)>, + ) { + let start_of_named_args = total_args_length - names.len(); + if current_positional_arg >= start_of_named_args { + self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names) + } + } + + /// Try constructing a PositionalNamedArg struct and pushing it into the vec of positional + /// named arguments. If a named arg associated with `format_argument_index` cannot be found, + /// a new item will not be added as the lint cannot be emitted in this case. + fn maybe_push( + &mut self, + format_argument_index: usize, + ty: PositionalNamedArgType, + cur_piece: usize, + inner_span_to_replace: Option<rustc_parse_format::InnerSpan>, + names: &FxHashMap<Symbol, (usize, Span)>, + ) { + let named_arg = names + .iter() + .find(|&(_, &(index, _))| index == format_argument_index) + .map(|found| found.clone()); + + if let Some((&replacement, &(_, positional_named_arg_span))) = named_arg { + // In FormatSpec, `precision_span` starts at the leading `.`, which we want to keep in + // the lint suggestion, so increment `start` by 1 when `PositionalArgumentType` is + // `Precision`. + let inner_span_to_replace = if ty == PositionalNamedArgType::Precision { + inner_span_to_replace + .map(|is| rustc_parse_format::InnerSpan { start: is.start + 1, end: is.end }) + } else { + inner_span_to_replace + }; + self.positional_named_args.push(PositionalNamedArg { + ty, + cur_piece, + inner_span_to_replace, + replacement, + positional_named_arg_span, + }); + } + } +} + struct Context<'a, 'b> { ecx: &'a mut ExtCtxt<'b>, /// The macro's call site. References to unstable formatting internals must @@ -57,7 +189,7 @@ struct Context<'a, 'b> { /// Unique format specs seen for each argument. arg_unique_types: Vec<Vec<ArgumentType>>, /// Map from named arguments to their resolved indices. - names: FxHashMap<Symbol, usize>, + names: FxHashMap<Symbol, (usize, Span)>, /// The latest consecutive literal strings, or empty if there weren't any. literal: String, @@ -115,6 +247,7 @@ struct Context<'a, 'b> { /// Whether this format string came from a string literal, as opposed to a macro. is_literal: bool, + unused_names_lint: PositionalNamedArgsLint, } /// Parses the arguments from the given list of tokens, returning the diagnostic @@ -130,9 +263,9 @@ fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>)> { +) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, (usize, Span)>)> { let mut args = Vec::<P<ast::Expr>>::new(); - let mut names = FxHashMap::<Symbol, usize>::default(); + let mut names = FxHashMap::<Symbol, (usize, Span)>::default(); let mut p = ecx.new_parser_from_tts(tts); @@ -197,7 +330,7 @@ fn parse_args<'a>( p.bump(); p.expect(&token::Eq)?; let e = p.parse_expr()?; - if let Some(prev) = names.get(&ident.name) { + if let Some((prev, _)) = names.get(&ident.name) { ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", ident)) .span_label(args[*prev].span, "previously here") .span_label(e.span, "duplicate argument") @@ -210,7 +343,7 @@ fn parse_args<'a>( // if the input is valid, we can simply append to the positional // args. And remember the names. let slot = args.len(); - names.insert(ident.name, slot); + names.insert(ident.name, (slot, ident.span)); args.push(e); } _ => { @@ -222,7 +355,7 @@ fn parse_args<'a>( ); err.span_label(e.span, "positional arguments must be before named arguments"); for pos in names.values() { - err.span_label(args[*pos].span, "named argument"); + err.span_label(args[pos.0].span, "named argument"); } err.emit(); } @@ -239,16 +372,17 @@ impl<'a, 'b> Context<'a, 'b> { self.args.len() - self.num_captured_args } - fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { + fn resolve_name_inplace(&mut self, p: &mut parse::Piece<'_>) { // NOTE: the `unwrap_or` branch is needed in case of invalid format // arguments, e.g., `format_args!("{foo}")`. - let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0); + let lookup = + |s: &str| self.names.get(&Symbol::intern(s)).unwrap_or(&(0, Span::default())).0; match *p { parse::String(_) => {} parse::NextArgument(ref mut arg) => { if let parse::ArgumentNamed(s, _) = arg.position { - arg.position = parse::ArgumentIs(lookup(s)); + arg.position = parse::ArgumentIs(lookup(s), None); } if let parse::CountIsName(s, _) = arg.format.width { arg.format.width = parse::CountIsParam(lookup(s)); @@ -269,15 +403,48 @@ impl<'a, 'b> Context<'a, 'b> { parse::NextArgument(ref arg) => { // width/precision first, if they have implicit positional // parameters it makes more sense to consume them first. - self.verify_count(arg.format.width); - self.verify_count(arg.format.precision); + self.verify_count( + arg.format.width, + &arg.format.width_span, + PositionalNamedArgType::Width, + ); + self.verify_count( + arg.format.precision, + &arg.format.precision_span, + PositionalNamedArgType::Precision, + ); // argument second, if it's an implicit positional parameter // it's written second, so it should come after width/precision. let pos = match arg.position { - parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), + parse::ArgumentIs(i, arg_end) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + PositionalNamedArgType::Arg, + self.curpiece, + arg_end, + &self.names, + ); + + Exact(i) + } + parse::ArgumentImplicitlyIs(i) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + PositionalNamedArgType::Arg, + self.curpiece, + None, + &self.names, + ); + Exact(i) + } parse::ArgumentNamed(s, span) => { - Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)) + let symbol = Symbol::intern(s); + Named(symbol, InnerSpan::new(span.start, span.end)) } }; @@ -345,10 +512,24 @@ impl<'a, 'b> Context<'a, 'b> { } } - fn verify_count(&mut self, c: parse::Count<'_>) { + fn verify_count( + &mut self, + c: parse::Count<'_>, + inner_span: &Option<rustc_parse_format::InnerSpan>, + named_arg_type: PositionalNamedArgType, + ) { match c { parse::CountImplied | parse::CountIs(..) => {} parse::CountIsParam(i) => { + self.unused_names_lint.maybe_add_positional_named_arg( + i, + self.args.len(), + i, + named_arg_type, + self.curpiece, + *inner_span, + &self.names, + ); self.verify_arg_type(Exact(i), Count); } parse::CountIsName(s, span) => { @@ -481,7 +662,7 @@ impl<'a, 'b> Context<'a, 'b> { if let Some(span) = fmt.width_span { let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); match fmt.width { - parse::CountIsParam(pos) if pos > self.num_args() => { + parse::CountIsParam(pos) if pos >= self.num_args() => { e.span_label( span, &format!( @@ -548,7 +729,7 @@ impl<'a, 'b> Context<'a, 'b> { match self.names.get(&name) { Some(&idx) => { // Treat as positional arg. - self.verify_arg_type(Capture(idx), ty) + self.verify_arg_type(Capture(idx.0), ty) } None => { // For the moment capturing variables from format strings expanded from macros is @@ -565,7 +746,7 @@ impl<'a, 'b> Context<'a, 'b> { }; self.num_captured_args += 1; self.args.push(self.ecx.expr_ident(span, Ident::new(name, span))); - self.names.insert(name, idx); + self.names.insert(name, (idx, span)); self.verify_arg_type(Capture(idx), ty) } else { let msg = format!("there is no argument named `{}`", name); @@ -669,7 +850,7 @@ impl<'a, 'b> Context<'a, 'b> { // Build the position let pos = { match arg.position { - parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => { + parse::ArgumentIs(i, ..) | parse::ArgumentImplicitlyIs(i) => { // Map to index in final generated argument array // in case of multiple types specified let arg_idx = match arg_index_consumed.get_mut(i) { @@ -697,7 +878,7 @@ impl<'a, 'b> Context<'a, 'b> { // track the current argument ourselves. let i = self.curarg; self.curarg += 1; - parse::ArgumentIs(i) + parse::ArgumentIs(i, None) }, format: parse::FormatSpec { fill: arg.format.fill, @@ -967,6 +1148,30 @@ pub fn expand_format_args_nl<'cx>( expand_format_args_impl(ecx, sp, tts, true) } +fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) { + for named_arg in &cx.unused_names_lint.positional_named_args { + let arg_span = named_arg.get_span_to_replace(cx); + + let msg = format!("named argument `{}` is not used by name", named_arg.replacement); + let replacement = match named_arg.ty { + PositionalNamedArgType::Arg => named_arg.replacement.to_string(), + _ => named_arg.replacement.to_string() + "$", + }; + + cx.ecx.buffered_early_lint.push(BufferedEarlyLint { + span: MultiSpan::from_span(named_arg.positional_named_arg_span), + msg: msg.clone(), + node_id: ast::CRATE_NODE_ID, + lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), + diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally( + arg_span, + named_arg.positional_named_arg_span, + replacement, + ), + }); + } +} + /// Take the various parts of `format_args!(efmt, args..., name=names...)` /// and construct the appropriate formatting expression. pub fn expand_preparsed_format_args( @@ -974,7 +1179,7 @@ pub fn expand_preparsed_format_args( sp: Span, efmt: P<ast::Expr>, args: Vec<P<ast::Expr>>, - names: FxHashMap<Symbol, usize>, + names: FxHashMap<Symbol, (usize, Span)>, append_newline: bool, ) -> P<ast::Expr> { // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because @@ -1073,7 +1278,7 @@ pub fn expand_preparsed_format_args( .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) .collect(); - let named_pos: FxHashSet<usize> = names.values().cloned().collect(); + let named_pos: FxHashSet<usize> = names.values().cloned().map(|(i, _)| i).collect(); let mut cx = Context { ecx, @@ -1099,6 +1304,7 @@ pub fn expand_preparsed_format_args( arg_spans, arg_with_formatting: Vec::new(), is_literal: parser.is_literal, + unused_names_lint: PositionalNamedArgsLint { positional_named_args: vec![] }, }; // This needs to happen *after* the Parser has consumed all pieces to create all the spans @@ -1113,7 +1319,7 @@ pub fn expand_preparsed_format_args( let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg { parse::String(_) => false, - parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)), + parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(..)), }); cx.build_index_map(); @@ -1265,6 +1471,10 @@ pub fn expand_preparsed_format_args( } diag.emit(); + } else if cx.invalid_refs.is_empty() && cx.ecx.sess.err_count() == 0 { + // Only check for unused named argument names if there are no other errors to avoid causing + // too much noise in output errors, such as when a named argument is entirely unused. + create_lints_for_named_arguments_used_positionally(&mut cx); } cx.into_expr() diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 11565ba72d7..0192f3c8ca4 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -8,7 +8,7 @@ #![feature(decl_macro)] #![feature(if_let_guard)] #![feature(is_sorted)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e2a6e77ab44..0ebe29df95f 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -51,7 +51,7 @@ pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast let test_runner = get_test_runner(sess, span_diagnostic, &krate); if sess.opts.test { - let panic_strategy = match (panic_strategy, sess.opts.debugging_opts.panic_abort_tests) { + let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) { (PanicStrategy::Abort, true) => PanicStrategy::Abort, (PanicStrategy::Abort, false) => { if panic_strategy == platform_panic_strategy { diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index ecb20f22d8c..d88309e412e 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,10 +1,9 @@ { // source for rustc_* is not included in the rust-src component; disable the errors about this "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"], - "rust-analyzer.assist.importGranularity": "module", - "rust-analyzer.assist.importEnforceGranularity": true, - "rust-analyzer.assist.importPrefix": "crate", - "rust-analyzer.cargo.runBuildScripts": true, + "rust-analyzer.imports.granularity.enforce": true, + "rust-analyzer.imports.granularity.group": "module", + "rust-analyzer.imports.prefix": "crate", "rust-analyzer.cargo.features": ["unstable-features"], "rust-analyzer.linkedProjects": [ "./Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index da18ac7eacb..532049c858d 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -3,6 +3,17 @@ version = 3 [[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] name = "anyhow" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -26,6 +37,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -33,56 +50,57 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed44413e7e2fe3260d0ed73e6956ab188b69c10ee92b892e401e0f4f6808c68b" +checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5d83f0f26bf213f971f45589d17e5b65e4861f9ed22392b0cbb6eaa5bd329c" +checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", + "cranelift-isle", "gimli", "log", - "regalloc", + "regalloc2", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800dc386177df6ecc5a32680607ed8ba1fa0d31a2a59c8c61fbf44826b8191d" +checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c961f85070985ebc8fcdb81b838a5cf842294d1e6ed4852446161c7e246fd455" +checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" [[package]] name = "cranelift-entity" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2347b2b8d1d5429213668f2a8e36c85ee3c73984a2f6a79007e365d3e575e7ed" +checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" [[package]] name = "cranelift-frontend" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbcdbf7bed29e363568b778649b69dabc3d727256d5d25236096ef693757654" +checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" dependencies = [ "cranelift-codegen", "log", @@ -91,10 +109,16 @@ dependencies = [ ] [[package]] +name = "cranelift-isle" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" + +[[package]] name = "cranelift-jit" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c769d4e0d76f59c8b2a3bf0477d89ee149bb0731b53fbb245ee081d49063095" +checksum = "1c3c5ed067f2c81577e431f3039148a9c187b33cc79e0d1731fede27d801ec56" dependencies = [ "anyhow", "cranelift-codegen", @@ -110,9 +134,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab57d399a2401074bb0cc40b3031e420f3d66d46ec0cf21eeae53ac04bd73e2" +checksum = "eee6784303bf9af235237a4885f7417e09a35df896d38ea969a0081064b3ede4" dependencies = [ "anyhow", "cranelift-codegen", @@ -120,9 +144,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4cdf93552e5ceb2e3c042829ebb4de4378492705f769eadc6a7c6c5251624c" +checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" dependencies = [ "cranelift-codegen", "libc", @@ -131,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.83.0" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8e65f4839c26e6237fc0744911d79b0a2ac5e76b4e4eebd14db2b8d849fd31" +checksum = "0bf38b2c505db749276793116c0cb30bd096206c7810e471677a453134881881" dependencies = [ "anyhow", "cranelift-codegen", @@ -153,6 +177,26 @@ dependencies = [ ] [[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] name = "gimli" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -166,22 +210,31 @@ name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "indexmap" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] name = "libc" -version = "0.2.119" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libloading" @@ -219,11 +272,12 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "object" -version = "0.27.1" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ "crc32fast", + "hashbrown 0.11.2", "indexmap", "memchr", ] @@ -235,13 +289,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] -name = "regalloc" -version = "0.0.34" +name = "regalloc2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c" dependencies = [ + "fxhash", "log", - "rustc-hash", + "slice-group-by", "smallvec", ] @@ -258,12 +313,6 @@ dependencies = [ ] [[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] name = "rustc_codegen_cranelift" version = "0.1.0" dependencies = [ @@ -284,6 +333,12 @@ dependencies = [ ] [[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] name = "smallvec" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -296,6 +351,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" [[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index ec2c1f2ca71..61e977e3e69 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,18 +8,18 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.83.0", features = ["unwind", "all-arch"] } -cranelift-frontend = "0.83.0" -cranelift-module = "0.83.0" -cranelift-native = "0.83.0" -cranelift-jit = { version = "0.83.0", optional = true } -cranelift-object = "0.83.0" +cranelift-codegen = { version = "0.85.3", features = ["unwind", "all-arch"] } +cranelift-frontend = "0.85.3" +cranelift-module = "0.85.3" +cranelift-native = "0.85.3" +cranelift-jit = { version = "0.85.3", optional = true } +cranelift-object = "0.85.3" target-lexicon = "0.12.0" gimli = { version = "0.26.0", default-features = false, features = ["write"]} -object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.28.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } -indexmap = "1.8.0" +indexmap = "1.9.1" libloading = { version = "0.6.0", optional = true } once_cell = "1.10.0" smallvec = "1.8.1" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index efee6ef3f37..7b2cdd27336 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.72" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdbb35d279238cf77f0c9e8d90ad50d6c7bff476ab342baafa29440f0f10bff" +checksum = "c6e3183e88f659a862835db8f4b67dbeed3d93e44dd4927eef78edb1c149d784" dependencies = [ "rustc-std-workspace-core", ] @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -123,20 +123,21 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab7905ea95c6d9af62940f9d7dd9596d54c334ae2c15300c482051292d5637f" +checksum = "7668753748e445859e4e373c3d41117235d9feed578392f5a3a73efdc751ca4a" dependencies = [ "compiler_builtins", "libc", + "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 8682204f4fd..16cce83dd9c 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -205,7 +205,7 @@ fn build_clif_sysroot_for_triple( { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { - if ext == "rmeta" || ext == "d" || ext == "dSYM" { + if ext == "rmeta" || ext == "d" || ext == "dSYM" || ext == "clif" { continue; } } else { diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 489259d1a6b..8b6042a3d66 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -458,7 +458,7 @@ pub trait FnMut<Args>: FnOnce<Args> { #[lang = "panic"] #[track_caller] -pub fn panic(_msg: &str) -> ! { +pub fn panic(_msg: &'static str) -> ! { unsafe { libc::puts("Panicking\n\0" as *const str as *const i8); intrinsics::abort(); @@ -497,7 +497,7 @@ pub trait Deref { #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] -pub struct NonNull<T: ?Sized>(pub *mut T); +pub struct NonNull<T: ?Sized>(pub *const T); impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} @@ -521,7 +521,7 @@ impl<T: ?Sized> Drop for Box<T> { } } -impl<T> Deref for Box<T> { +impl<T: ?Sized> Deref for Box<T> { type Target = T; fn deref(&self) -> &Self::Target { diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 0f1245c2758..aa1f239bae2 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -124,6 +124,23 @@ fn call_return_u128_pair() { return_u128_pair(); } +#[repr(C)] +pub struct bool_11 { + field0: bool, + field1: bool, + field2: bool, + field3: bool, + field4: bool, + field5: bool, + field6: bool, + field7: bool, + field8: bool, + field9: bool, + field10: bool, +} + +extern "C" fn bool_struct_in_11(arg0: bool_11) {} + #[allow(unreachable_code)] // FIXME false positive fn main() { take_unique(Unique { @@ -134,6 +151,20 @@ fn main() { call_return_u128_pair(); + bool_struct_in_11(bool_11 { + field0: true, + field1: true, + field2: true, + field3: true, + field4: true, + field5: true, + field6: true, + field7: true, + field8: true, + field9: true, + field10: true, + }); + let slice = &[0, 1] as &[i32]; let slice_ptr = slice as *const [i32] as *const i32; @@ -299,6 +330,17 @@ fn main() { static REF1: &u8 = &42; static REF2: &u8 = REF1; assert_eq!(*REF1, *REF2); + + extern "C" { + type A; + } + + fn main() { + let x: &A = unsafe { &*(1usize as *const A) }; + + assert_eq!(unsafe { intrinsics::size_of_val(x) }, 0); + assert_eq!(unsafe { intrinsics::min_align_of_val(x) }, 1); +} } #[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))] diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 0a2bce2621d..0b5b6cd55d7 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -128,6 +128,25 @@ fn main() { 0 => loop {}, v => panic(v), }; + + if black_box(false) { + // Based on https://github.com/rust-lang/rust/blob/2f320a224e827b400be25966755a621779f797cc/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs + let _ = Foo::<dyn Send>::new(); + + #[allow(dead_code)] + struct Foo<T: ?Sized> { + base: Never, + value: T, + } + + impl<T: ?Sized> Foo<T> { + pub fn new() -> Box<Foo<T>> { + todo!() + } + } + + enum Never {} + } } fn panic(_: u128) { diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index e98e92e468e..3ab395d89d5 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-15" +channel = "nightly-2022-07-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 4d0dfa16c5e..091bfa1e992 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -29,14 +29,15 @@ diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/ru index 8431aa7b818..a3ff7e68ce5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs -@@ -3489,11 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S - .join("library"); - normalize_path(&src_dir, "$(echo '$SRC_DIR')"); +@@ -3489,12 +3489,7 @@ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> S + let compiler_src_dir = base_dir.join("compiler"); + normalize_path(&compiler_src_dir, "$(echo '$COMPILER_DIR')"); - if let Some(virtual_rust_source_base_dir) = - option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from) - { - normalize_path(&virtual_rust_source_base_dir.join("library"), "$(echo '$SRC_DIR')"); +- normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$(echo '$COMPILER_DIR')"); - } + normalize_path(&Path::new("$(cd ../build_sysroot/sysroot_src/library; pwd)"), "$(echo '$SRC_DIR')"); @@ -62,3 +63,6 @@ deny-warnings = false verbose-tests = false EOF popd + +# FIXME remove once inline asm is fully supported +export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 9bdb9f22c54..944787612d8 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -33,6 +33,7 @@ rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-typ rm src/test/ui/async-await/async-fn-size-moved-locals.rs # -Cpanic=abort shrinks some generator by one byte rm src/test/ui/async-await/async-fn-size-uninit-locals.rs # same rm src/test/ui/generator/size-moved-locals.rs # same +rm -r src/test/ui/macros/rfc-2011-nicer-assert-messages/ # vendor intrinsics rm src/test/ui/sse2.rs # cpuid not supported, so sse2 not detected @@ -65,11 +66,13 @@ rm src/test/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and n rm src/test/ui/target-feature/missing-plusminus.rs # error not implemented rm src/test/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment rm -r src/test/run-make/emit-named-files # requires full --emit support +rm src/test/ui/abi/stack-probes.rs # stack probes not yet implemented # optimization tests # ================== -rm src/test/ui/issues/issue-28950.rs # depends on stack size optimizations +rm src/test/ui/codegen/issue-28950.rs # depends on stack size optimizations rm src/test/ui/codegen/init-large-type.rs # same +rm src/test/ui/issues/issue-40883.rs # same rm -r src/test/run-make/fmt-write-bloat/ # tests an optimization # backend specific tests @@ -89,14 +92,13 @@ rm src/test/ui/consts/issue-33537.rs # same rm -r src/test/run-make/emit-shared-files # requires the rustdoc executable in build/bin/ rm -r src/test/run-make/unstable-flag-required # same rm -r src/test/run-make/rustdoc-* # same +rm -r src/test/run-make/issue-88756-default-output # same +rm -r src/test/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump # genuine bugs # ============ rm src/test/ui/allocator/no_std-alloc-error-handler-default.rs # missing rust_oom definition -rm -r src/test/ui/polymorphization/ # polymorphization not yet supported -rm src/test/codegen-units/polymorphization/unused_type_parameters.rs # same - rm src/test/incremental/spike-neg1.rs # errors out for some reason rm src/test/incremental/spike-neg2.rs # same rm src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs @@ -111,6 +113,8 @@ rm src/test/ui/backtrace.rs # TODO warning rm src/test/ui/empty_global_asm.rs # TODO add needs-asm-support rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout +# not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason +rm -r src/test/run-make/native-link-modifier-bundle echo "[TEST] rustc test suite" RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental} diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index ffa5d747b11..815450f689e 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -4,6 +4,7 @@ mod comments; mod pass_mode; mod returning; +use cranelift_module::ModuleError; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_target::abi::call::{Conv, FnAbi}; @@ -69,7 +70,17 @@ pub(crate) fn import_function<'tcx>( ) -> FuncId { let name = tcx.symbol_name(inst).name; let sig = get_function_sig(tcx, module.isa().triple(), inst); - module.declare_function(name, Linkage::Import, &sig).unwrap() + match module.declare_function(name, Linkage::Import, &sig) { + Ok(func_id) => func_id, + Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!( + "attempt to declare `{name}` as function, but it was already declared as static" + )), + Err(ModuleError::IncompatibleSignature(_, prev_sig, new_sig)) => tcx.sess.fatal(&format!( + "attempt to declare `{name}` with signature {new_sig:?}, \ + but it was already declared with signature {prev_sig:?}" + )), + Err(err) => Err::<_, _>(err).unwrap(), + } } impl<'tcx> FunctionCx<'_, '_, 'tcx> { @@ -182,6 +193,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ } let fn_abi = fx.fn_abi.take().unwrap(); + + // FIXME implement variadics in cranelift + if fn_abi.c_variadic { + fx.tcx.sess.span_fatal( + fx.mir.span, + "Defining variadic functions is not yet supported by Cranelift", + ); + } + let mut arg_abis_iter = fn_abi.args.iter(); let func_params = fx @@ -376,9 +396,15 @@ pub(crate) fn codegen_terminator_call<'tcx>( RevealAllLayoutCx(fx.tcx).fn_abi_of_fn_ptr(fn_ty.fn_sig(fx.tcx), extra_args) }; - let is_cold = instance - .map(|inst| fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)) - .unwrap_or(false); + let is_cold = if fn_sig.abi == Abi::RustCold { + true + } else { + instance + .map(|inst| { + fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD) + }) + .unwrap_or(false) + }; if is_cold { fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); if let Some(destination_block) = target { diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 9f0bd31e95f..6c10baa53d4 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -18,9 +18,9 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam { let clif_ty = match (reg.kind, reg.size.bytes()) { (RegKind::Integer, 1) => types::I8, (RegKind::Integer, 2) => types::I16, - (RegKind::Integer, 4) => types::I32, - (RegKind::Integer, 8) => types::I64, - (RegKind::Integer, 16) => types::I128, + (RegKind::Integer, 3..=4) => types::I32, + (RegKind::Integer, 5..=8) => types::I64, + (RegKind::Integer, 9..=16) => types::I128, (RegKind::Float, 4) => types::F32, (RegKind::Float, 8) => types::F64, (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), @@ -48,23 +48,9 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> { ) }; - if cast.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if cast.rest.total <= cast.rest.unit.size { - let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) { - (RegKind::Integer, 1) => types::I8, - (RegKind::Integer, 2) => types::I16, - (RegKind::Integer, 3..=4) => types::I32, - (RegKind::Integer, 5..=8) => types::I64, - (RegKind::Integer, 9..=16) => types::I128, - (RegKind::Float, 4) => types::F32, - (RegKind::Float, 8) => types::F64, - (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), - _ => unreachable!("{:?}", cast.rest.unit), - }; - return smallvec![AbiParam::new(clif_ty)]; - } - } + // Note: Unlike the LLVM equivalent of this code we don't have separate branches for when there + // is no prefix as a single unit, an array and a heterogeneous struct are not represented using + // different types in Cranelift IR. Instead a single array of primitive types is used. // Create list of fields in the main structure let mut args = cast @@ -230,7 +216,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>( arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, is_owned: bool, ) -> SmallVec<[Value; 2]> { - assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty); + assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty, 16); match arg_abi.mode { PassMode::Ignore => smallvec![], PassMode::Direct(_) => smallvec![arg.load_scalar(fx)], diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index c3b99b64263..6d321c7b298 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -24,7 +24,7 @@ pub(crate) fn codegen( unwind_context, kind, tcx.lang_items().oom().is_some(), - tcx.sess.opts.debugging_opts.oom, + tcx.sess.opts.unstable_opts.oom, ); true } else { diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 0812f930b5d..c92c1051139 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -86,7 +86,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { let mut entries = Vec::new(); - for (entry_name, entry) in self.entries { + for (mut entry_name, entry) in self.entries { // FIXME only read the symbol table of the object files to avoid having to keep all // object files in memory at once, or read them twice. let data = match entry { @@ -109,6 +109,23 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { }; if !self.no_builtin_ranlib { + if symbol_table.contains_key(&entry_name) { + // The ar crate can't handle creating a symbol table in case of multiple archive + // members with the same name. Work around this by prepending a number until we + // get a unique name. + for i in 1.. { + let new_name = format!("{}_", i) + .into_bytes() + .into_iter() + .chain(entry_name.iter().copied()) + .collect::<Vec<_>>(); + if !symbol_table.contains_key(&new_name) { + entry_name = new_name; + break; + } + } + } + match object::File::parse(&*data) { Ok(object) => { symbol_table.insert( @@ -204,12 +221,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { any_members } - fn inject_dll_import_lib( - &mut self, + fn sess(&self) -> &Session { + self.sess + } + + fn create_dll_import_lib( + _sess: &Session, _lib_name: &str, _dll_imports: &[rustc_session::cstore::DllImport], - _tmpdir: &rustc_data_structures::temp_dir::MaybeTempDir, - ) { - bug!("injecting dll imports is not supported"); + _tmpdir: &Path, + ) -> PathBuf { + bug!("creating dll imports is not supported"); } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 63cd4d6de4c..122e103ff62 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -175,10 +175,37 @@ fn compile_fn<'tcx>( ); }); + #[cfg(any())] // This is never true + let _clif_guard = { + use std::fmt::Write; + + let func_clone = context.func.clone(); + let clif_comments_clone = clif_comments.clone(); + let mut clif = String::new(); + for flag in module.isa().flags().iter() { + writeln!(clif, "set {}", flag).unwrap(); + } + write!(clif, "target {}", module.isa().triple().architecture.to_string()).unwrap(); + for isa_flag in module.isa().isa_flags().iter() { + write!(clif, " {}", isa_flag).unwrap(); + } + writeln!(clif, "\n").unwrap(); + crate::PrintOnPanic(move || { + let mut clif = clif.clone(); + ::cranelift_codegen::write::decorate_function( + &mut &clif_comments_clone, + &mut clif, + &func_clone, + ) + .unwrap(); + clif + }) + }; + // Define function tcx.sess.time("define function", || { context.want_disasm = crate::pretty_clif::should_write_ir(tcx); - module.define_function(func_id, context).unwrap() + module.define_function(func_id, context).unwrap(); }); // Write optimized function to file for debugging @@ -815,15 +842,7 @@ pub(crate) fn codegen_place<'tcx>( for elem in place.projection { match elem { PlaceElem::Deref => { - if cplace.layout().ty.is_box() { - cplace = cplace - .place_field(fx, Field::new(0)) // Box<T> -> Unique<T> - .place_field(fx, Field::new(0)) // Unique<T> -> NonNull<T> - .place_field(fx, Field::new(0)) // NonNull<T> -> *mut T - .place_deref(fx); - } else { - cplace = cplace.place_deref(fx); - } + cplace = cplace.place_deref(fx); } PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index e19070774c6..bad5d1f08a9 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -144,22 +144,13 @@ pub(crate) fn clif_int_or_float_cast( fx.bcx.ins().fcvt_to_uint_sat(to_ty, from) }; - if let Some(false) = fx.tcx.sess.opts.debugging_opts.saturating_float_casts { + if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts { return val; } let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from); - if to_ty == types::I128 { - // FIXME(bytecodealliance/wasmtime#3963): select.i128 on fcmp eq miscompiles - let (lsb, msb) = fx.bcx.ins().isplit(val); - let zero = fx.bcx.ins().iconst(types::I64, 0); - let lsb = fx.bcx.ins().select(is_not_nan, lsb, zero); - let msb = fx.bcx.ins().select(is_not_nan, msb, zero); - fx.bcx.ins().iconcat(lsb, msb) - } else { - let zero = fx.bcx.ins().iconst(to_ty, 0); - fx.bcx.ins().select(is_not_nan, val, zero) - } + let zero = fx.bcx.ins().iconst(to_ty, 0); + fx.bcx.ins().select(is_not_nan, val, zero) } else if from_ty.is_float() && to_ty.is_float() { // float -> float match (from_ty, to_ty) { diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 48972321a9f..7f7fd0e9c57 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -195,9 +195,8 @@ pub(crate) fn codegen_const_value<'tcx>( } Scalar::Ptr(ptr, _size) => { let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative - let alloc_kind = fx.tcx.get_global_alloc(alloc_id); - let base_addr = match alloc_kind { - Some(GlobalAlloc::Memory(alloc)) => { + let base_addr = match fx.tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { let data_id = data_id_for_alloc_id( &mut fx.constants_cx, fx.module, @@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>( } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - Some(GlobalAlloc::Function(instance)) => { + GlobalAlloc::Function(instance) => { let func_id = crate::abi::import_function(fx.tcx, fx.module, instance); let local_func_id = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } - Some(GlobalAlloc::Static(def_id)) => { + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref)); + let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory(); + // FIXME: factor this common code with the `Memory` arm into a function? + let data_id = data_id_for_alloc_id( + &mut fx.constants_cx, + fx.module, + alloc_id, + alloc.inner().mutability, + ); + let local_data_id = + fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) + } + GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); let local_data_id = @@ -227,7 +240,6 @@ pub(crate) fn codegen_const_value<'tcx>( } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - None => bug!("missing allocation {:?}", alloc_id), }; let val = if offset.bytes() != 0 { fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap()) @@ -316,14 +328,18 @@ fn data_id_for_static( let attrs = tcx.codegen_fn_attrs(def_id); - let data_id = module - .declare_data( - &*symbol_name, - linkage, - is_mutable, - attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL), - ) - .unwrap(); + let data_id = match module.declare_data( + &*symbol_name, + linkage, + is_mutable, + attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL), + ) { + Ok(data_id) => data_id, + Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!( + "attempt to declare `{symbol_name}` as static, but it was already declared as function" + )), + Err(err) => Err::<_, _>(err).unwrap(), + }; if rlinkage.is_some() { // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141 @@ -357,10 +373,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant while let Some(todo_item) = cx.todo.pop() { let (data_id, alloc, section_name) = match todo_item { TodoItem::Alloc(alloc_id) => { - //println!("alloc_id {}", alloc_id); - let alloc = match tcx.get_global_alloc(alloc_id).unwrap() { + let alloc = match tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => alloc, - GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(), + GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => { + unreachable!() + } }; let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| { module @@ -424,11 +441,12 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant read_target_uint(endianness, bytes).unwrap() }; - let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap(); + let reloc_target_alloc = tcx.global_alloc(alloc_id); let data_id = match reloc_target_alloc { GlobalAlloc::Function(instance) => { assert_eq!(addend, 0); - let func_id = crate::abi::import_function(tcx, module, instance); + let func_id = + crate::abi::import_function(tcx, module, instance.polymorphize(tcx)); let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx); data_ctx.write_function_addr(offset.bytes() as u32, local_func_id); continue; @@ -436,6 +454,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(target_alloc) => { data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) + } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index 476d6a54e12..bbcb9591373 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -140,7 +140,7 @@ impl<'tcx> DebugContext<'tcx> { // In order to have a good line stepping behavior in debugger, we overwrite debug // locations of macro expansions with that of the outermost expansion site // (unless the crate is being compiled with `-Z debug-macros`). - let span = if !span.from_expansion() || tcx.sess.opts.debugging_opts.debug_macros { + let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros { span } else { // Walk up the macro expansion chain until we reach a non-expanded span. diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 50d8fc30d7d..3cd1ef5639e 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -33,7 +33,7 @@ fn make_module(sess: &Session, isa: Box<dyn TargetIsa>, name: String) -> ObjectM // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections // can easily double the amount of time necessary to perform linking. - builder.per_function_section(sess.opts.debugging_opts.function_sections.unwrap_or(false)); + builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false)); ObjectModule::new(builder) } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index deac5dfd3ec..241de5e3653 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -18,86 +18,96 @@ pub(crate) fn codegen_inline_asm<'tcx>( ) { // FIXME add .eh_frame unwind info directives - if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { - let true_ = fx.bcx.ins().iconst(types::I32, 1); - fx.bcx.ins().trapnz(true_, TrapCode::User(1)); - return; - } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) - && matches!( - template[1], - InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ } - ) - && template[2] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string()) - && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) - && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string()) - && matches!( - template[6], - InlineAsmTemplatePiece::Placeholder { operand_idx: 0, modifier: Some('r'), span: _ } - ) - { - assert_eq!(operands.len(), 4); - let (leaf, eax_place) = match operands[1] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } - _ => unreachable!(), - }; - let ebx_place = match operands[0] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( - X86InlineAsmRegClass::reg - )) - ); - crate::base::codegen_place(fx, place.unwrap()) - } - _ => unreachable!(), - }; - let (sub_leaf, ecx_place) = match operands[2] { - InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)) - ); - ( - crate::base::codegen_operand(fx, in_value).load_scalar(fx), - crate::base::codegen_place(fx, out_place.unwrap()), - ) - } - _ => unreachable!(), - }; - let edx_place = match operands[3] { - InlineAsmOperand::Out { reg, late: true, place } => { - assert_eq!( - reg, - InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)) - ); - crate::base::codegen_place(fx, place.unwrap()) - } - _ => unreachable!(), - }; - - let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf); - - eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32))); - ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); - ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); - edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); - return; - } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { - // ___chkstk, ___chkstk_ms and __alloca are only used on Windows - crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); - } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" { - crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + if !template.is_empty() { + if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { + let true_ = fx.bcx.ins().iconst(types::I32, 1); + fx.bcx.ins().trapnz(true_, TrapCode::User(1)); + return; + } else if template[0] == InlineAsmTemplatePiece::String("movq %rbx, ".to_string()) + && matches!( + template[1], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + && template[2] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[3] == InlineAsmTemplatePiece::String("cpuid".to_string()) + && template[4] == InlineAsmTemplatePiece::String("\n".to_string()) + && template[5] == InlineAsmTemplatePiece::String("xchgq %rbx, ".to_string()) + && matches!( + template[6], + InlineAsmTemplatePiece::Placeholder { + operand_idx: 0, + modifier: Some('r'), + span: _ + } + ) + { + assert_eq!(operands.len(), 4); + let (leaf, eax_place) = match operands[1] { + InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)) + ); + ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place.unwrap()), + ) + } + _ => unreachable!(), + }; + let ebx_place = match operands[0] { + InlineAsmOperand::Out { reg, late: true, place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::RegClass(InlineAsmRegClass::X86( + X86InlineAsmRegClass::reg + )) + ); + crate::base::codegen_place(fx, place.unwrap()) + } + _ => unreachable!(), + }; + let (sub_leaf, ecx_place) = match operands[2] { + InlineAsmOperand::InOut { reg, late: true, ref in_value, out_place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::cx)) + ); + ( + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + crate::base::codegen_place(fx, out_place.unwrap()), + ) + } + _ => unreachable!(), + }; + let edx_place = match operands[3] { + InlineAsmOperand::Out { reg, late: true, place } => { + assert_eq!( + reg, + InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)) + ); + crate::base::codegen_place(fx, place.unwrap()) + } + _ => unreachable!(), + }; + + let (eax, ebx, ecx, edx) = crate::intrinsics::codegen_cpuid_call(fx, leaf, sub_leaf); + + eax_place.write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32))); + ebx_place.write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); + ecx_place.write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); + edx_place.write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); + return; + } else if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") { + // ___chkstk, ___chkstk_ms and __alloca are only used on Windows + crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); + } else if fx.tcx.symbol_name(fx.instance).name == "__alloca" { + crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + } } let mut inputs = Vec::new(); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 77ac46540a9..869670c8cfa 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -13,15 +13,11 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( ret: CPlace<'tcx>, target: Option<BasicBlock>, ) { - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); - crate::trap::trap_unimplemented(fx, intrinsic); - }; - + match intrinsic { // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` - "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd", (c a) { + "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd" => { + intrinsic_args!(fx, args => (a); intrinsic); + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); let lane_ty = fx.clif_type(lane_ty).unwrap(); assert!(lane_count <= 32); @@ -29,7 +25,8 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( let mut res = fx.bcx.ins().iconst(types::I32, 0); for lane in (0..lane_count).rev() { - let a_lane = a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); + let a_lane = + a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); // cast float to int let a_lane = match lane_ty { @@ -49,26 +46,29 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32)); ret.write_cvalue(fx, res); - }; - "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd", (c x, c y, o kind) { - let kind = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const"); - let flt_cc = match kind.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) { + } + "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => { + let (x, y, kind) = match args { + [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 = crate::constant::mir_operand_get_const_val(fx, kind) + .expect("llvm.x86.sse2.cmp.* kind not const"); + + let flt_cc = match kind + .try_to_bits(Size::from_bytes(1)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) + { 0 => FloatCC::Equal, 1 => FloatCC::LessThan, 2 => FloatCC::LessThanOrEqual, - 7 => { - unimplemented!("Compares corresponding elements in `a` and `b` to see if neither is `NaN`."); - } - 3 => { - unimplemented!("Compares corresponding elements in `a` and `b` to see if either is `NaN`."); - } + 7 => FloatCC::Ordered, + 3 => FloatCC::Unordered, 4 => FloatCC::NotEqual, - 5 => { - unimplemented!("not less than"); - } - 6 => { - unimplemented!("not less than or equal"); - } + 5 => FloatCC::UnorderedOrGreaterThanOrEqual, + 6 => FloatCC::UnorderedOrGreaterThan, kind => unreachable!("kind {:?}", kind), }; @@ -79,50 +79,67 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( }; bool_to_zero_or_max_uint(fx, res_lane_ty, res_lane) }); - }; - "llvm.x86.sse2.psrli.d", (c a, o imm8) { - let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { - imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), - _ => fx.bcx.ins().iconst(types::I32, 0), - } + } + "llvm.x86.sse2.psrli.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrli.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), }); - }; - "llvm.x86.sse2.pslli.d", (c a, o imm8) { - let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { - imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), - _ => fx.bcx.ins().iconst(types::I32, 0), - } + } + "llvm.x86.sse2.pslli.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrli.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), }); - }; - "llvm.x86.sse2.storeu.dq", (v mem_addr, c a) { + } + "llvm.x86.sse2.storeu.dq" => { + intrinsic_args!(fx, args => (mem_addr, a); intrinsic); + let mem_addr = mem_addr.load_scalar(fx); + // FIXME correctly handle the unalignment let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout()); dest.write_cvalue(fx, a); - }; - "llvm.x86.addcarry.64", (v c_in, c a, c b) { - llvm_add_sub( - fx, - BinOp::Add, - ret, - c_in, - a, - b - ); - }; - "llvm.x86.subborrow.64", (v b_in, c a, c b) { - llvm_add_sub( - fx, - BinOp::Sub, - ret, - b_in, - a, - b - ); - }; + } + "llvm.x86.addcarry.64" => { + intrinsic_args!(fx, args => (c_in, a, b); intrinsic); + let c_in = c_in.load_scalar(fx); + + llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b); + } + "llvm.x86.subborrow.64" => { + intrinsic_args!(fx, args => (b_in, a, b); intrinsic); + let b_in = b_in.load_scalar(fx); + + llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b); + } + _ => { + fx.tcx + .sess + .warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); + crate::trap::trap_unimplemented(fx, intrinsic); + } } let dest = target.expect("all llvm intrinsics used by stdlib should return"); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 6937e658ed5..b2a83e1d4eb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1,50 +1,14 @@ //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, `extern "platform-intrinsic"` //! and LLVM intrinsics that have symbol names starting with `llvm.`. -macro_rules! intrinsic_pat { - (_) => { - _ - }; - ($name:ident) => { - sym::$name - }; - (kw.$name:ident) => { - kw::$name - }; - ($name:literal) => { - $name - }; -} - -macro_rules! intrinsic_arg { - (o $fx:expr, $arg:ident) => {}; - (c $fx:expr, $arg:ident) => { - let $arg = codegen_operand($fx, $arg); - }; - (v $fx:expr, $arg:ident) => { - let $arg = codegen_operand($fx, $arg).load_scalar($fx); - }; -} - -macro_rules! intrinsic_match { - ($fx:expr, $intrinsic:expr, $args:expr, - _ => $unknown:block; - $( - $($($name:tt).*)|+ $(if $cond:expr)?, ($($a:ident $arg:ident),*) $content:block; - )*) => { - match $intrinsic { - $( - $(intrinsic_pat!($($name).*))|* $(if $cond)? => { - if let [$($arg),*] = $args { - $(intrinsic_arg!($a $fx, $arg);)* - $content - } else { - bug!("wrong number of args for intrinsic {:?}", $intrinsic); - } - } - )* - _ => $unknown, - } +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)),*) + } else { + $crate::intrinsics::bug_on_incorrect_arg_count($intrinsic); + }; } } @@ -58,11 +22,14 @@ pub(crate) use llvm::codegen_llvm_intrinsic_call; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::SubstsRef; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_target::abi::InitKind; use crate::prelude::*; use cranelift_codegen::ir::AtomicRmwOp; +fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! { + bug!("wrong number of args for intrinsic {}", intrinsic); +} + fn report_atomic_type_validation_error<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: Symbol, @@ -352,28 +319,31 @@ fn codegen_regular_intrinsic_call<'tcx>( ) { let usize_layout = fx.layout_of(fx.tcx.types.usize); - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic)); - }; + match intrinsic { + sym::assume => { + intrinsic_args!(fx, args => (_a); intrinsic); + } + sym::likely | sym::unlikely => { + intrinsic_args!(fx, args => (a); intrinsic); - assume, (c _a) {}; - likely | unlikely, (c a) { ret.write_cvalue(fx, a); - }; - breakpoint, () { + } + sym::breakpoint => { + intrinsic_args!(fx, args => (); intrinsic); + fx.bcx.ins().debugtrap(); - }; - copy | copy_nonoverlapping, (v src, v dst, v count) { + } + sym::copy | sym::copy_nonoverlapping => { + intrinsic_args!(fx, args => (src, dst, count); intrinsic); + let src = src.load_scalar(fx); + let dst = dst.load_scalar(fx); + let count = count.load_scalar(fx); + let elem_ty = substs.type_at(0); let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); assert_eq!(args.len(), 3); - let byte_amount = if elem_size != 1 { - fx.bcx.ins().imul_imm(count, elem_size as i64) - } else { - count - }; + let byte_amount = + if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; if intrinsic == sym::copy_nonoverlapping { // FIXME emit_small_memcpy @@ -382,17 +352,19 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME emit_small_memmove fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount); } - }; - // NOTE: the volatile variants have src and dst swapped - volatile_copy_memory | volatile_copy_nonoverlapping_memory, (v dst, v src, v count) { + } + sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => { + // NOTE: the volatile variants have src and dst swapped + intrinsic_args!(fx, args => (dst, src, count); intrinsic); + let dst = dst.load_scalar(fx); + let src = src.load_scalar(fx); + let count = count.load_scalar(fx); + let elem_ty = substs.type_at(0); let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); assert_eq!(args.len(), 3); - let byte_amount = if elem_size != 1 { - fx.bcx.ins().imul_imm(count, elem_size as i64) - } else { - count - }; + let byte_amount = + if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; // FIXME make the copy actually volatile when using emit_small_mem{cpy,move} if intrinsic == sym::volatile_copy_nonoverlapping_memory { @@ -402,38 +374,64 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME emit_small_memmove fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount); } - }; - size_of_val, (c ptr) { + } + sym::size_of_val => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); - let size = if layout.is_unsized() { + // Note: Can't use is_unsized here as truly unsized types need to take the fixed size + // branch + let size = if let Abi::ScalarPair(_, _) = ptr.layout().abi { let (_ptr, info) = ptr.load_scalar_pair(fx); let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info); size } else { - fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.size.bytes() as i64) + fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64) }; ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); - }; - min_align_of_val, (c ptr) { + } + sym::min_align_of_val => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); - let align = if layout.is_unsized() { + // Note: Can't use is_unsized here as truly unsized types need to take the fixed size + // branch + let align = if let Abi::ScalarPair(_, _) = ptr.layout().abi { let (_ptr, info) = ptr.load_scalar_pair(fx); let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info); align } else { - fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.align.abi.bytes() as i64) + fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64) }; ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); - }; + } + + sym::vtable_size => { + intrinsic_args!(fx, args => (vtable); intrinsic); + let vtable = vtable.load_scalar(fx); + + let size = crate::vtable::size_of_obj(fx, vtable); + ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); + } + + sym::vtable_align => { + intrinsic_args!(fx, args => (vtable); intrinsic); + let vtable = vtable.load_scalar(fx); + + let align = crate::vtable::min_align_of_obj(fx, vtable); + ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); + } + + sym::unchecked_add + | sym::unchecked_sub + | sym::unchecked_mul + | sym::unchecked_div + | sym::exact_div + | sym::unchecked_rem + | sym::unchecked_shl + | sym::unchecked_shr => { + intrinsic_args!(fx, args => (x, y); intrinsic); - unchecked_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem - | unchecked_shl | unchecked_shr, (c x, c y) { // FIXME trap on overflow let bin_op = match intrinsic { sym::unchecked_add => BinOp::Add, @@ -447,8 +445,10 @@ fn codegen_regular_intrinsic_call<'tcx>( }; let res = crate::num::codegen_int_binop(fx, bin_op, x, y); ret.write_cvalue(fx, res); - }; - add_with_overflow | sub_with_overflow | mul_with_overflow, (c x, c y) { + } + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { + intrinsic_args!(fx, args => (x, y); intrinsic); + assert_eq!(x.layout().ty, y.layout().ty); let bin_op = match intrinsic { sym::add_with_overflow => BinOp::Add, @@ -457,15 +457,12 @@ fn codegen_regular_intrinsic_call<'tcx>( _ => unreachable!(), }; - let res = crate::num::codegen_checked_int_binop( - fx, - bin_op, - x, - y, - ); + let res = crate::num::codegen_checked_int_binop(fx, bin_op, x, y); ret.write_cvalue(fx, res); - }; - saturating_add | saturating_sub, (c lhs, c rhs) { + } + sym::saturating_add | sym::saturating_sub => { + intrinsic_args!(fx, args => (lhs, rhs); intrinsic); + assert_eq!(lhs.layout().ty, rhs.layout().ty); let bin_op = match intrinsic { sym::saturating_add => BinOp::Add, @@ -475,12 +472,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let signed = type_sign(lhs.layout().ty); - let checked_res = crate::num::codegen_checked_int_binop( - fx, - bin_op, - lhs, - rhs, - ); + let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); let (val, has_overflow) = checked_res.load_scalar_pair(fx); let clif_ty = fx.clif_type(lhs.layout().ty).unwrap(); @@ -492,13 +484,15 @@ fn codegen_regular_intrinsic_call<'tcx>( (sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val), (sym::saturating_add, true) => { let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let rhs_ge_zero = + fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min); fx.bcx.ins().select(has_overflow, sat_val, val) } (sym::saturating_sub, true) => { let rhs = rhs.load_scalar(fx); - let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let rhs_ge_zero = + fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max); fx.bcx.ins().select(has_overflow, sat_val, val) } @@ -508,23 +502,32 @@ fn codegen_regular_intrinsic_call<'tcx>( let res = CValue::by_val(val, lhs.layout()); ret.write_cvalue(fx, res); - }; - rotate_left, (c x, v y) { + } + sym::rotate_left => { + intrinsic_args!(fx, args => (x, y); intrinsic); + let y = y.load_scalar(fx); + let layout = x.layout(); let x = x.load_scalar(fx); let res = fx.bcx.ins().rotl(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); - }; - rotate_right, (c x, v y) { + } + sym::rotate_right => { + intrinsic_args!(fx, args => (x, y); intrinsic); + let y = y.load_scalar(fx); + let layout = x.layout(); let x = x.load_scalar(fx); let res = fx.bcx.ins().rotr(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); - }; + } // The only difference between offset and arith_offset is regarding UB. Because Cranelift // doesn't have UB both are codegen'ed the same way - offset | arith_offset, (c base, v offset) { + sym::offset | sym::arith_offset => { + intrinsic_args!(fx, args => (base, offset); intrinsic); + let offset = offset.load_scalar(fx); + let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty; let pointee_size = fx.layout_of(pointee_ty).size.bytes(); let ptr_diff = if pointee_size != 1 { @@ -535,12 +538,18 @@ fn codegen_regular_intrinsic_call<'tcx>( let base_val = base.load_scalar(fx); let res = fx.bcx.ins().iadd(base_val, ptr_diff); ret.write_cvalue(fx, CValue::by_val(res, base.layout())); - }; + } + + sym::transmute => { + intrinsic_args!(fx, args => (from); intrinsic); - transmute, (c from) { ret.write_cvalue_transmute(fx, from); - }; - write_bytes | volatile_set_memory, (c dst, v val, v count) { + } + sym::write_bytes | sym::volatile_set_memory => { + intrinsic_args!(fx, args => (dst, val, count); intrinsic); + let val = val.load_scalar(fx); + let count = count.load_scalar(fx); + let pointee_ty = dst.layout().ty.builtin_deref(true).unwrap().ty; let pointee_size = fx.layout_of(pointee_ty).size.bytes(); let count = if pointee_size != 1 { @@ -552,34 +561,42 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME make the memset actually volatile when switching to emit_small_memset // FIXME use emit_small_memset fx.bcx.call_memset(fx.target_config, dst_ptr, val, count); - }; - ctlz | ctlz_nonzero, (c arg) { + } + sym::ctlz | sym::ctlz_nonzero => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + // FIXME trap on `ctlz_nonzero` with zero arg. let res = fx.bcx.ins().clz(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - cttz | cttz_nonzero, (c arg) { + } + sym::cttz | sym::cttz_nonzero => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + // FIXME trap on `cttz_nonzero` with zero arg. let res = fx.bcx.ins().ctz(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - ctpop, (c arg) { + } + sym::ctpop => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = fx.bcx.ins().popcnt(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - bitreverse, (c arg) { + } + sym::bitreverse => { + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = fx.bcx.ins().bitrev(val); let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); - }; - bswap, (c arg) { + } + sym::bswap => { // FIXME(CraneStation/cranelift#794) add bswap instruction to cranelift fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value { match bcx.func.dfg.value_type(v) { @@ -655,11 +672,15 @@ fn codegen_regular_intrinsic_call<'tcx>( ty => unreachable!("bswap {}", ty), } } + intrinsic_args!(fx, args => (arg); intrinsic); let val = arg.load_scalar(fx); + let res = CValue::by_val(swap(&mut fx.bcx, val), arg.layout()); ret.write_cvalue(fx, res); - }; - assert_inhabited | assert_zero_valid | assert_uninit_valid, () { + } + sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { + intrinsic_args!(fx, args => (); intrinsic); + let layout = fx.layout_of(substs.type_at(0)); if layout.abi.is_uninhabited() { with_no_trimmed_paths!({ @@ -672,66 +693,71 @@ fn codegen_regular_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_zero_valid - && !layout.might_permit_raw_init( - fx, - InitKind::Zero, - fx.tcx.sess.opts.debugging_opts.strict_init_checks) { - + if intrinsic == sym::assert_zero_valid && !fx.tcx.permits_zero_init(layout) { with_no_trimmed_paths!({ crate::base::codegen_panic( fx, - &format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty), + &format!( + "attempted to zero-initialize type `{}`, which is invalid", + layout.ty + ), source_info, ); }); return; } - if intrinsic == sym::assert_uninit_valid - && !layout.might_permit_raw_init( - fx, - InitKind::Uninit, - fx.tcx.sess.opts.debugging_opts.strict_init_checks) { - + if intrinsic == sym::assert_uninit_valid && !fx.tcx.permits_uninit_init(layout) { with_no_trimmed_paths!({ crate::base::codegen_panic( fx, - &format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty), + &format!( + "attempted to leave type `{}` uninitialized, which is invalid", + layout.ty + ), source_info, ) }); return; } - }; + } + + sym::volatile_load | sym::unaligned_volatile_load => { + intrinsic_args!(fx, args => (ptr); intrinsic); - volatile_load | unaligned_volatile_load, (c ptr) { // Cranelift treats loads as volatile by default // FIXME correctly handle unaligned_volatile_load - let inner_layout = - fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); + let inner_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); ret.write_cvalue(fx, val); - }; - volatile_store | unaligned_volatile_store, (v ptr, c val) { + } + sym::volatile_store | sym::unaligned_volatile_store => { + intrinsic_args!(fx, args => (ptr, val); intrinsic); + let ptr = ptr.load_scalar(fx); + // Cranelift treats stores as volatile by default // FIXME correctly handle unaligned_volatile_store let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); dest.write_cvalue(fx, val); - }; + } + + sym::pref_align_of + | sym::needs_drop + | sym::type_id + | sym::type_name + | sym::variant_count => { + intrinsic_args!(fx, args => (); intrinsic); - pref_align_of | needs_drop | type_id | type_name | variant_count, () { let const_val = fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); - let val = crate::constant::codegen_const_value( - fx, - const_val, - ret.layout().ty, - ); + let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); ret.write_cvalue(fx, val); - }; + } - ptr_offset_from | ptr_offset_from_unsigned, (v ptr, v base) { + sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { + intrinsic_args!(fx, args => (ptr, base); intrinsic); + let ptr = ptr.load_scalar(fx); + let base = base.load_scalar(fx); let ty = substs.type_at(0); let pointee_size: u64 = fx.layout_of(ty).size.bytes(); @@ -747,31 +773,44 @@ fn codegen_regular_intrinsic_call<'tcx>( CValue::by_val(fx.bcx.ins().sdiv_imm(diff_bytes, pointee_size as i64), isize_layout) }; ret.write_cvalue(fx, val); - }; + } + + sym::ptr_guaranteed_eq => { + intrinsic_args!(fx, args => (a, b); intrinsic); - ptr_guaranteed_eq, (c a, c b) { let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b); ret.write_cvalue(fx, val); - }; + } + + sym::ptr_guaranteed_ne => { + intrinsic_args!(fx, args => (a, b); intrinsic); - ptr_guaranteed_ne, (c a, c b) { let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b); ret.write_cvalue(fx, val); - }; + } + + sym::caller_location => { + intrinsic_args!(fx, args => (); intrinsic); - caller_location, () { let caller_location = fx.get_caller_location(source_info); ret.write_cvalue(fx, caller_location); - }; + } + + _ if intrinsic.as_str().starts_with("atomic_fence") => { + intrinsic_args!(fx, args => (); intrinsic); - _ if intrinsic.as_str().starts_with("atomic_fence"), () { fx.bcx.ins().fence(); - }; - _ if intrinsic.as_str().starts_with("atomic_singlethreadfence"), () { + } + _ if intrinsic.as_str().starts_with("atomic_singlethreadfence") => { + intrinsic_args!(fx, args => (); intrinsic); + // FIXME use a compiler fence once Cranelift supports it fx.bcx.ins().fence(); - }; - _ if intrinsic.as_str().starts_with("atomic_load"), (v ptr) { + } + _ if intrinsic.as_str().starts_with("atomic_load") => { + intrinsic_args!(fx, args => (ptr); intrinsic); + let ptr = ptr.load_scalar(fx); + let ty = substs.type_at(0); match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { @@ -783,7 +822,9 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported"); + fx.tcx + .sess + .span_fatal(source_info.span, "128bit atomics not yet supported"); } } ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -798,8 +839,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_val(val, fx.layout_of(ty)); ret.write_cvalue(fx, val); - }; - _ if intrinsic.as_str().starts_with("atomic_store"), (v ptr, c val) { + } + _ if intrinsic.as_str().starts_with("atomic_store") => { + intrinsic_args!(fx, args => (ptr, val); intrinsic); + let ptr = ptr.load_scalar(fx); + let ty = substs.type_at(0); match ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { @@ -811,7 +855,9 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.ins().jump(ret_block, &[]); return; } else { - fx.tcx.sess.span_fatal(source_info.span, "128bit atomics not yet supported"); + fx.tcx + .sess + .span_fatal(source_info.span, "128bit atomics not yet supported"); } } ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -824,8 +870,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = val.load_scalar(fx); fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr); - }; - _ if intrinsic.as_str().starts_with("atomic_xchg"), (v ptr, c new) { + } + _ if intrinsic.as_str().starts_with("atomic_xchg") => { + intrinsic_args!(fx, args => (ptr, new); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = new.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -842,8 +891,12 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_* + } + _ if intrinsic.as_str().starts_with("atomic_cxchg") => { + // both atomic_cxchg_* and atomic_cxchgweak_* + intrinsic_args!(fx, args => (ptr, test_old, new); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = new.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -859,11 +912,15 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = fx.bcx.ins().atomic_cas(MemFlags::trusted(), ptr, test_old, new); let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old); - let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); + let ret_val = + CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); ret.write_cvalue(fx, ret_val) - }; + } + + _ if intrinsic.as_str().starts_with("atomic_xadd") => { + intrinsic_args!(fx, args => (ptr, amount); intrinsic); + let ptr = ptr.load_scalar(fx); - _ if intrinsic.as_str().starts_with("atomic_xadd"), (v ptr, c amount) { let layout = amount.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -876,12 +933,16 @@ fn codegen_regular_intrinsic_call<'tcx>( let amount = amount.load_scalar(fx); - let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount); + let old = + fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount); let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_xsub"), (v ptr, c amount) { + } + _ if intrinsic.as_str().starts_with("atomic_xsub") => { + intrinsic_args!(fx, args => (ptr, amount); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = amount.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -894,12 +955,16 @@ fn codegen_regular_intrinsic_call<'tcx>( let amount = amount.load_scalar(fx); - let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount); + let old = + fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount); let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_and"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_and") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -916,8 +981,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_or"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_or") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -934,8 +1002,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_xor"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_xor") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -952,8 +1023,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_nand") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -970,8 +1044,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_max"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_max") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -988,8 +1065,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_umax"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_umax") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -1006,8 +1086,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_min"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_min") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -1024,8 +1107,11 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; - _ if intrinsic.as_str().starts_with("atomic_umin"), (v ptr, c src) { + } + _ if intrinsic.as_str().starts_with("atomic_umin") => { + intrinsic_args!(fx, args => (ptr, src); intrinsic); + let ptr = ptr.load_scalar(fx); + let layout = src.layout(); match layout.ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -1042,30 +1128,51 @@ fn codegen_regular_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); - }; + } + + sym::minnumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); - minnumf32, (v a, v b) { let val = crate::num::codegen_float_min(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); - }; - minnumf64, (v a, v b) { + } + sym::minnumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_min(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); - }; - maxnumf32, (v a, v b) { + } + sym::maxnumf32 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_max(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); - }; - maxnumf64, (v a, v b) { + } + sym::maxnumf64 => { + intrinsic_args!(fx, args => (a, b); intrinsic); + let a = a.load_scalar(fx); + let b = b.load_scalar(fx); + let val = crate::num::codegen_float_max(fx, a, b); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); - }; + } + + kw::Try => { + intrinsic_args!(fx, args => (f, data, catch_fn); intrinsic); + let f = f.load_scalar(fx); + let data = data.load_scalar(fx); + let _catch_fn = catch_fn.load_scalar(fx); - kw.Try, (v f, v data, v _catch_fn) { // FIXME once unwinding is supported, change this to actually catch panics let f_sig = fx.bcx.func.import_signature(Signature { call_conv: fx.target_config.default_call_conv, @@ -1078,20 +1185,30 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = ret.layout(); let ret_val = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size)); ret.write_cvalue(fx, ret_val); - }; + } - fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) { - let res = crate::num::codegen_float_binop(fx, match intrinsic { - sym::fadd_fast => BinOp::Add, - sym::fsub_fast => BinOp::Sub, - sym::fmul_fast => BinOp::Mul, - sym::fdiv_fast => BinOp::Div, - sym::frem_fast => BinOp::Rem, - _ => unreachable!(), - }, x, y); + sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { + intrinsic_args!(fx, args => (x, y); intrinsic); + + let res = crate::num::codegen_float_binop( + fx, + match intrinsic { + sym::fadd_fast => BinOp::Add, + sym::fsub_fast => BinOp::Sub, + sym::fmul_fast => BinOp::Mul, + sym::fdiv_fast => BinOp::Div, + sym::frem_fast => BinOp::Rem, + _ => unreachable!(), + }, + x, + y, + ); ret.write_cvalue(fx, res); - }; - float_to_int_unchecked, (v f) { + } + sym::float_to_int_unchecked => { + intrinsic_args!(fx, args => (f); intrinsic); + let f = f.load_scalar(fx); + let res = crate::cast::clif_int_or_float_cast( fx, f, @@ -1100,52 +1217,74 @@ fn codegen_regular_intrinsic_call<'tcx>( type_sign(ret.layout().ty), ); ret.write_cvalue(fx, CValue::by_val(res, ret.layout())); - }; + } + + sym::raw_eq => { + intrinsic_args!(fx, args => (lhs_ref, rhs_ref); intrinsic); + let lhs_ref = lhs_ref.load_scalar(fx); + let rhs_ref = rhs_ref.load_scalar(fx); - raw_eq, (v lhs_ref, v rhs_ref) { let size = fx.layout_of(substs.type_at(0)).layout.size(); // FIXME add and use emit_small_memcmp - let is_eq_value = - if size == Size::ZERO { - // No bytes means they're trivially equal - fx.bcx.ins().iconst(types::I8, 1) - } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) { - // Can't use `trusted` for these loads; they could be unaligned. - let mut flags = MemFlags::new(); - flags.set_notrap(); - let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); - let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); - let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val); - fx.bcx.ins().bint(types::I8, eq) - } else { - // Just call `memcmp` (like slices do in core) when the - // size is too large or it's not a power-of-two. - let signed_bytes = i64::try_from(size.bytes()).unwrap(); - let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes); - let params = vec![AbiParam::new(fx.pointer_type); 3]; - let returns = vec![AbiParam::new(types::I32)]; - let args = &[lhs_ref, rhs_ref, bytes_val]; - let cmp = fx.lib_call("memcmp", params, returns, args)[0]; - let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0); - fx.bcx.ins().bint(types::I8, eq) - }; + let is_eq_value = if size == Size::ZERO { + // No bytes means they're trivially equal + fx.bcx.ins().iconst(types::I8, 1) + } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) { + // Can't use `trusted` for these loads; they could be unaligned. + let mut flags = MemFlags::new(); + flags.set_notrap(); + let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); + let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); + let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val); + fx.bcx.ins().bint(types::I8, eq) + } else { + // Just call `memcmp` (like slices do in core) when the + // size is too large or it's not a power-of-two. + let signed_bytes = i64::try_from(size.bytes()).unwrap(); + let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes); + let params = vec![AbiParam::new(fx.pointer_type); 3]; + let returns = vec![AbiParam::new(types::I32)]; + let args = &[lhs_ref, rhs_ref, bytes_val]; + let cmp = fx.lib_call("memcmp", params, returns, args)[0]; + let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0); + fx.bcx.ins().bint(types::I8, eq) + }; ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); - }; + } + + sym::const_allocate => { + intrinsic_args!(fx, args => (_size, _align); intrinsic); - const_allocate, (c _size, c _align) { // returns a null pointer at runtime. let null = fx.bcx.ins().iconst(fx.pointer_type, 0); ret.write_cvalue(fx, CValue::by_val(null, ret.layout())); - }; + } - const_deallocate, (c _ptr, c _size, c _align) { + sym::const_deallocate => { + intrinsic_args!(fx, args => (_ptr, _size, _align); intrinsic); // nop at runtime. - }; + } + + sym::black_box => { + intrinsic_args!(fx, args => (a); intrinsic); - black_box, (c a) { // FIXME implement black_box semantics ret.write_cvalue(fx, a); - }; + } + + // FIXME implement variadics in cranelift + sym::va_copy | sym::va_arg | sym::va_end => { + fx.tcx.sess.span_fatal( + source_info.span, + "Defining variadic functions is not yet supported by Cranelift", + ); + } + + _ => { + fx.tcx + .sess + .span_fatal(source_info.span, &format!("unsupported intrinsic {}", intrinsic)); + } } let ret_block = fx.get_block(destination.unwrap()); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index d1ca9edf2e0..30e3d112594 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -25,13 +25,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ret: CPlace<'tcx>, span: Span, ) { - intrinsic_match! { - fx, intrinsic, args, - _ => { - fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); - }; + match intrinsic { + sym::simd_cast => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_cast, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -45,9 +42,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( clif_int_or_float_cast(fx, lane, from_signed, ret_lane_clif_ty, to_signed) }); - }; + } + + sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { + intrinsic_args!(fx, args => (x, y); intrinsic); - simd_eq | simd_ne | simd_lt | simd_le | simd_gt | simd_ge, (c x, c y) { if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -57,7 +56,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| { let res_lane = match (lane_ty.kind(), intrinsic) { (ty::Uint(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane), - (ty::Uint(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane), + (ty::Uint(_), sym::simd_ne) => { + fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane) + } (ty::Uint(_), sym::simd_lt) => { fx.bcx.ins().icmp(IntCC::UnsignedLessThan, x_lane, y_lane) } @@ -72,8 +73,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( } (ty::Int(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane), - (ty::Int(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane), - (ty::Int(_), sym::simd_lt) => fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane), + (ty::Int(_), sym::simd_ne) => { + fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane) + } + (ty::Int(_), sym::simd_lt) => { + fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane) + } (ty::Int(_), sym::simd_le) => { fx.bcx.ins().icmp(IntCC::SignedLessThanOrEqual, x_lane, y_lane) } @@ -84,13 +89,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().icmp(IntCC::SignedGreaterThanOrEqual, x_lane, y_lane) } - (ty::Float(_), sym::simd_eq) => fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane), - (ty::Float(_), sym::simd_ne) => fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane), - (ty::Float(_), sym::simd_lt) => fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane), + (ty::Float(_), sym::simd_eq) => { + fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane) + } + (ty::Float(_), sym::simd_ne) => { + fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane) + } + (ty::Float(_), sym::simd_lt) => { + fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane) + } (ty::Float(_), sym::simd_le) => { fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, x_lane, y_lane) } - (ty::Float(_), sym::simd_gt) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane), + (ty::Float(_), sym::simd_gt) => { + fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane) + } (ty::Float(_), sym::simd_ge) => { fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, x_lane, y_lane) } @@ -103,10 +116,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let res_lane = fx.bcx.ins().bint(ty, res_lane); fx.bcx.ins().ineg(res_lane) }); - }; + } // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U - _ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) { + _ if intrinsic.as_str().starts_with("simd_shuffle") => { + let (x, y, idx) = match args { + [x, y, idx] => (x, y, idx), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let x = codegen_operand(fx, x); + let y = codegen_operand(fx, y); + if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -119,11 +141,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // version of this intrinsic. let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx)); match idx_ty.kind() { - ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { - len.try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(|| { + ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len + .try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()) + .unwrap_or_else(|| { span_bug!(span, "could not evaluate shuffle index array length") - }).try_into().unwrap() - } + }) + .try_into() + .unwrap(), _ => { fx.tcx.sess.span_err( span, @@ -154,24 +178,30 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let indexes = { use rustc_middle::mir::interpret::*; - let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const"); + let idx_const = crate::constant::mir_operand_get_const_val(fx, idx) + .expect("simd_shuffle* idx not const"); let idx_bytes = match idx_const { ConstValue::ByRef { alloc, offset } => { - let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */); + let size = Size::from_bytes( + 4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */ + ); alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap() } _ => unreachable!("{:?}", idx_const), }; - (0..ret_lane_count).map(|i| { - let i = usize::try_from(i).unwrap(); - let idx = rustc_middle::mir::interpret::read_target_uint( - fx.tcx.data_layout.endian, - &idx_bytes[4*i.. 4*i + 4], - ).expect("read_target_uint"); - u16::try_from(idx).expect("try_from u32") - }).collect::<Vec<u16>>() + (0..ret_lane_count) + .map(|i| { + let i = usize::try_from(i).unwrap(); + let idx = rustc_middle::mir::interpret::read_target_uint( + fx.tcx.data_layout.endian, + &idx_bytes[4 * i..4 * i + 4], + ) + .expect("read_target_uint"); + u16::try_from(idx).expect("try_from u32") + }) + .collect::<Vec<u16>>() }; for &idx in &indexes { @@ -187,43 +217,63 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap()); out_lane.write_cvalue(fx, in_lane); } - }; + } + + sym::simd_insert => { + let (base, idx, val) = match args { + [base, idx, val] => (base, idx, val), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let base = codegen_operand(fx, base); + let val = codegen_operand(fx, val); - simd_insert, (c base, o idx, c val) { // FIXME validate - let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + let idx_const = if let Some(idx_const) = + crate::constant::mir_operand_get_const_val(fx, idx) + { idx_const } else { - fx.tcx.sess.span_fatal( - span, - "Index argument for `simd_insert` is not a constant", - ); + fx.tcx.sess.span_fatal(span, "Index argument for `simd_insert` is not a constant"); }; - let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const + .try_to_bits(Size::from_bytes(4 /* u32*/)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { - fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); + fx.tcx.sess.span_fatal( + fx.mir.span, + &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count), + ); } ret.write_cvalue(fx, base); let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap())); ret_lane.write_cvalue(fx, val); - }; + } + + sym::simd_extract => { + let (v, idx) = match args { + [v, idx] => (v, idx), + _ => { + bug!("wrong number of args for intrinsic {intrinsic}"); + } + }; + let v = codegen_operand(fx, v); - simd_extract, (c v, o idx) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } - let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + let idx_const = if let Some(idx_const) = + crate::constant::mir_operand_get_const_val(fx, idx) + { idx_const } else { - fx.tcx.sess.span_warn( - span, - "Index argument for `simd_extract` is not a constant", - ); + fx.tcx.sess.span_warn(span, "Index argument for `simd_extract` is not a constant"); let res = crate::trap::trap_unimplemented_ret_value( fx, ret.layout(), @@ -233,89 +283,105 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; }; - let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const + .try_to_bits(Size::from_bytes(4 /* u32*/)) + .unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { - fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); + fx.tcx.sess.span_fatal( + fx.mir.span, + &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count), + ); } let ret_lane = v.value_lane(fx, idx.try_into().unwrap()); ret.write_cvalue(fx, ret_lane); - }; + } + + sym::simd_neg => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_neg, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; } - simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { - match lane_ty.kind() { + simd_for_each_lane( + fx, + a, + ret, + &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() { ty::Int(_) => fx.bcx.ins().ineg(lane), ty::Float(_) => fx.bcx.ins().fneg(lane), _ => unreachable!(), - } - }); - }; - - simd_add | simd_sub | simd_mul | simd_div | simd_rem - | simd_shl | simd_shr | simd_and | simd_or | simd_xor, (c x, c y) { - if !x.layout().ty.is_simd() { - report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); - return; - } + }, + ); + } + + sym::simd_add + | sym::simd_sub + | sym::simd_mul + | sym::simd_div + | sym::simd_rem + | sym::simd_shl + | sym::simd_shr + | sym::simd_and + | sym::simd_or + | sym::simd_xor => { + intrinsic_args!(fx, args => (x, y); intrinsic); // FIXME use vector instructions when possible - simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| match ( - lane_ty.kind(), - intrinsic, - ) { - (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), - (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), - - (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), - (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), - (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), - (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), - (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), - - (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), - (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), - (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), - (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), - (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( - "fmodf", - vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], - vec![AbiParam::new(types::F32)], - &[x_lane, y_lane], - )[0], - (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( - "fmod", - vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], - vec![AbiParam::new(types::F64)], - &[x_lane, y_lane], - )[0], - - (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), - (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), - (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), - (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), - (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), - (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), - - _ => unreachable!(), + simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| { + match (lane_ty.kind(), intrinsic) { + (ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane), + (ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane), + + (ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane), + (ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane), + (ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane), + (ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane), + (ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane), + + (ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane), + (ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane), + (ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane), + (ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane), + (ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call( + "fmodf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[x_lane, y_lane], + )[0], + (ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call( + "fmod", + vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], + vec![AbiParam::new(types::F64)], + &[x_lane, y_lane], + )[0], + + (ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane), + (ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + (ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane), + (ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane), + (ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane), + (ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane), + (ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane), + + _ => unreachable!(), + } }); - }; + } + + sym::simd_fma => { + intrinsic_args!(fx, args => (a, b, c); intrinsic); - simd_fma, (c a, c b, c c) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -333,16 +399,22 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let c_lane = c.value_lane(fx, lane); let res_lane = match lane_ty.kind() { - ty::Float(FloatTy::F32) => fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty), - ty::Float(FloatTy::F64) => fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty), + ty::Float(FloatTy::F32) => { + fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty) + } + ty::Float(FloatTy::F64) => { + fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty) + } _ => unreachable!(), }; ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } - }; + } + + sym::simd_fmin | sym::simd_fmax => { + intrinsic_args!(fx, args => (x, y); intrinsic); - simd_fmin | simd_fmax, (c x, c y) { if !x.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty); return; @@ -351,7 +423,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // FIXME use vector instructions when possible simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| { match lane_ty.kind() { - ty::Float(_) => {}, + ty::Float(_) => {} _ => unreachable!("{:?}", lane_ty), } match intrinsic { @@ -360,16 +432,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => unreachable!(), } }); - }; + } + + sym::simd_round => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_round, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; } - simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { - match lane_ty.kind() { + simd_for_each_lane( + fx, + a, + ret, + &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() { ty::Float(FloatTy::F32) => fx.lib_call( "roundf", vec![AbiParam::new(types::F32)], @@ -383,11 +460,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( &[lane], )[0], _ => unreachable!("{:?}", lane_ty), - } - }); - }; + }, + ); + } + + sym::simd_fabs | sym::simd_fsqrt | sym::simd_ceil | sym::simd_floor | sym::simd_trunc => { + intrinsic_args!(fx, args => (a); intrinsic); - simd_fabs | simd_fsqrt | simd_ceil | simd_floor | simd_trunc, (c a) { if !a.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty); return; @@ -395,7 +474,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| { match lane_ty.kind() { - ty::Float(_) => {}, + ty::Float(_) => {} _ => unreachable!("{:?}", lane_ty), } match intrinsic { @@ -407,9 +486,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => unreachable!(), } }); - }; + } + + sym::simd_reduce_add_ordered | sym::simd_reduce_add_unordered => { + intrinsic_args!(fx, args => (v, acc); intrinsic); + let acc = acc.load_scalar(fx); - simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) { // FIXME there must be no acc param for integer vectors if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); @@ -423,9 +505,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().iadd(a, b) } }); - }; + } + + sym::simd_reduce_mul_ordered | sym::simd_reduce_mul_unordered => { + intrinsic_args!(fx, args => (v, acc); intrinsic); + let acc = acc.load_scalar(fx); - simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) { // FIXME there must be no acc param for integer vectors if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); @@ -439,54 +524,66 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.bcx.ins().imul(a, b) } }); - }; + } + + sym::simd_reduce_all => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_all, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().band(a, b)); - }; + } + + sym::simd_reduce_any => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_any, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().bor(a, b)); - }; + } + + sym::simd_reduce_and => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_and, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().band(a, b)); - }; + } + + sym::simd_reduce_or => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_or, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bor(a, b)); - }; + } + + sym::simd_reduce_xor => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_xor, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; } simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bxor(a, b)); - }; + } + + sym::simd_reduce_min => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_min, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; @@ -501,9 +598,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; fx.bcx.ins().select(lt, a, b) }); - }; + } + + sym::simd_reduce_max => { + intrinsic_args!(fx, args => (v); intrinsic); - simd_reduce_max, (c v) { if !v.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty); return; @@ -518,9 +617,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; fx.bcx.ins().select(gt, a, b) }); - }; + } + + sym::simd_select => { + intrinsic_args!(fx, args => (m, a, b); intrinsic); - simd_select, (c m, c a, c b) { if !m.layout().ty.is_simd() { report_simd_type_validation_error(fx, intrinsic, span, m.layout().ty); return; @@ -540,15 +641,19 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let b_lane = b.value_lane(fx, lane).load_scalar(fx); let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0); - let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); + let res_lane = + CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } - }; + } // simd_saturating_* // simd_bitmask // simd_scatter // simd_gather + _ => { + fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); + } } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index be2d3108c5f..568bb20a3f4 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -141,7 +141,11 @@ impl<'tcx> CodegenCx<'tcx> { let unwind_context = UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot)); - let debug_context = if debug_info { Some(DebugContext::new(tcx, isa)) } else { None }; + let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows { + Some(DebugContext::new(tcx, isa)) + } else { + None + }; CodegenCx { tcx, global_asm: String::new(), @@ -167,7 +171,7 @@ impl CodegenBackend for CraneliftCodegenBackend { } } - fn target_features(&self, _sess: &Session) -> Vec<rustc_span::Symbol> { + fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<rustc_span::Symbol> { vec![] } @@ -243,6 +247,7 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box<dyn isa::Tar flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided let enable_verifier = if backend_config.enable_verifier { "true" } else { "false" }; flags_builder.set("enable_verifier", enable_verifier).unwrap(); + flags_builder.set("regalloc_checker", enable_verifier).unwrap(); let tls_model = match target_triple.binary_format { BinaryFormat::Elf => "elf_gd", diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 2f71a70a449..c67b6e98b32 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -109,7 +109,8 @@ pub(crate) fn maybe_create_entry_wrapper( tcx.mk_substs([GenericArg::from(main_ret_ty)].iter()), ) .unwrap() - .unwrap(); + .unwrap() + .polymorphize(tcx); let report_name = tcx.symbol_name(report).name; let report_sig = get_function_sig(tcx, m.isa().triple(), report); diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index ca7116b887d..1d1ec21680e 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -66,7 +66,7 @@ use rustc_session::config::OutputType; use crate::prelude::*; -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct CommentWriter { enabled: bool, global_comments: Vec<String>, @@ -237,6 +237,7 @@ pub(crate) fn write_clif_file<'tcx>( func: &cranelift_codegen::ir::Function, mut clif_comments: &CommentWriter, ) { + // FIXME work around filename too long errors write_ir_file( tcx, || format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index fd63c3ecddb..052ca0a082b 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -153,11 +153,7 @@ pub(crate) fn size_and_align_of_dst<'tcx>( layout: TyAndLayout<'tcx>, info: Value, ) -> (Value, Value) { - if !layout.is_unsized() { - let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64); - let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64); - return (size, align); - } + assert!(layout.is_unsized() || layout.abi == Abi::Uninhabited); match layout.ty.kind() { ty::Dynamic(..) => { // load size/align from vtable diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index a68225de58b..45ae2bd8f07 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -324,6 +324,12 @@ impl<'tcx> CPlace<'tcx> { }; } + if layout.size.bytes() >= u64::from(u32::MAX - 16) { + fx.tcx + .sess + .fatal(&format!("values of type {} are too big to store on the stack", layout.ty)); + } + let stack_slot = fx.bcx.create_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to @@ -420,7 +426,7 @@ impl<'tcx> CPlace<'tcx> { } pub(crate) fn write_cvalue(self, fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>) { - assert_assignable(fx, from.layout().ty, self.layout().ty); + assert_assignable(fx, from.layout().ty, self.layout().ty, 16); self.write_cvalue_maybe_transmute(fx, from, "write_cvalue"); } @@ -774,18 +780,25 @@ pub(crate) fn assert_assignable<'tcx>( fx: &FunctionCx<'_, '_, 'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, + limit: usize, ) { + if limit == 0 { + // assert_assignable exists solely to catch bugs in cg_clif. it isn't necessary for + // soundness. don't attempt to check deep types to avoid exponential behavior in certain + // cases. + return; + } match (from_ty.kind(), to_ty.kind()) { (ty::Ref(_, a, _), ty::Ref(_, b, _)) | ( ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), ) => { - assert_assignable(fx, *a, *b); + assert_assignable(fx, *a, *b, limit - 1); } (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ })) | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => { - assert_assignable(fx, *a, *b); + assert_assignable(fx, *a, *b, limit - 1); } (ty::FnPtr(_), ty::FnPtr(_)) => { let from_sig = fx.tcx.normalize_erasing_late_bound_regions( @@ -815,6 +828,17 @@ pub(crate) fn assert_assignable<'tcx>( } // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed } + (&ty::Tuple(types_a), &ty::Tuple(types_b)) => { + let mut types_a = types_a.iter(); + let mut types_b = types_b.iter(); + loop { + match (types_a.next(), types_b.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), + (None, None) => return, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + } (&ty::Adt(adt_def_a, substs_a), &ty::Adt(adt_def_b, substs_b)) if adt_def_a.did() == adt_def_b.did() => { @@ -822,18 +846,37 @@ pub(crate) fn assert_assignable<'tcx>( let mut types_b = substs_b.types(); loop { match (types_a.next(), types_b.next()) { - (Some(a), Some(b)) => assert_assignable(fx, a, b), + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), (None, None) => return, (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), } } } - (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b), + (ty::Array(a, _), ty::Array(b, _)) => assert_assignable(fx, *a, *b, limit - 1), + (&ty::Closure(def_id_a, substs_a), &ty::Closure(def_id_b, substs_b)) + if def_id_a == def_id_b => + { + let mut types_a = substs_a.types(); + let mut types_b = substs_b.types(); + loop { + match (types_a.next(), types_b.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), + (None, None) => return, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + } + (ty::Param(_), _) | (_, ty::Param(_)) if fx.tcx.sess.opts.unstable_opts.polymorphize => { + // No way to check if it is correct or not with polymorphization enabled + } _ => { assert_eq!( - from_ty, to_ty, + from_ty, + to_ty, "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}", - from_ty, to_ty, fx, + from_ty.kind(), + to_ty.kind(), + fx, ); } } diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 2c796d0f69e..0ed3e1fbe93 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -1,6 +1,6 @@ use gccjit::{ToLValue, ToRValue, Type}; use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index c761e5aabd1..58efb81e800 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -117,7 +117,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam let name = OomStrategy::SYMBOL.to_string(); let global = context.new_global(None, GlobalKind::Exported, i8, name); - let value = tcx.sess.opts.debugging_opts.oom.should_panic(); + let value = tcx.sess.opts.unstable_opts.oom.should_panic(); let value = context.new_rvalue_from_int(i8, value as i32); global.global_set_initializer_rvalue(value); } diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 411ec27139e..21f62a6b009 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -4,7 +4,6 @@ use std::path::{Path, PathBuf}; use rustc_codegen_ssa::back::archive::ArchiveBuilder; use rustc_session::Session; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_session::cstore::DllImport; struct ArchiveConfig<'a> { @@ -177,7 +176,16 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { any_members } - fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) { + fn sess(&self) -> &Session { + self.config.sess + } + + fn create_dll_import_lib( + _sess: &Session, + _lib_name: &str, + _dll_imports: &[DllImport], + _tmpdir: &Path, + ) -> PathBuf { unimplemented!(); } } diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index e4ecbd46f0c..8f9f6f98faf 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -96,7 +96,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_ // NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292). context.add_command_line_option("-fno-strict-aliasing"); - if tcx.sess.opts.debugging_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) { + if tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) { context.add_command_line_option("-ffunction-sections"); context.add_command_line_option("-fdata-sections"); } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index fa490fe3f22..4d40dd0994d 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -30,7 +30,7 @@ use rustc_codegen_ssa::traits::{ OverflowOp, StaticBuilderMethods, }; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_span::Span; @@ -784,16 +784,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // TODO(antoyo) } - fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) { - // Unsupported. - } - - fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> { - // Unsupported. - self.context.new_rvalue_from_int(self.int_type, 0) - } - - fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { self.store_with_flags(val, ptr, align, MemFlags::empty()) } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index fc391f53f18..ccb6cbbc2c8 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { GlobalAlloc::Function(fn_instance) => { self.get_fn_addr(fn_instance) }, + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc = self.tcx.global_alloc(self.tcx.vtable_allocation((ty, trait_ref))).unwrap_memory(); + let init = const_alloc_to_gcc(self, alloc); + self.static_addr_of(init, alloc.inner().align, None) + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 399830de84c..c21e0c5a35b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -140,8 +140,8 @@ impl CodegenBackend for GccCodegenBackend { ) } - fn target_features(&self, sess: &Session) -> Vec<Symbol> { - target_features(sess) + fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features(sess, allow_unstable) } } @@ -298,12 +298,12 @@ pub fn target_cpu(sess: &Session) -> &str { } } -pub fn target_features(sess: &Session) -> Vec<Symbol> { +pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { supported_target_features(sess) .iter() .filter_map( |&(feature, gate)| { - if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } + if sess.is_nightly_build() || allow_unstable || gate.is_none() { Some(feature) } else { None } }, ) .filter(|_feature| { diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 002b95db36d..68bdb8d4e55 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use gccjit::{RValue, Struct, Type}; -use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods}; +use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods}; use rustc_codegen_ssa::common::TypeKind; use rustc_middle::{bug, ty}; use rustc_middle::ty::layout::TyAndLayout; @@ -290,3 +290,14 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout (result, packed) } + +impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) { + // Unsupported. + } + + fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> { + // Unsupported. + self.context.new_rvalue_from_int(self.int_type, 0) + } +} diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index d8fbd0a84fb..f9a5463efcd 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -18,7 +18,6 @@ rustc_middle = { path = "../rustc_middle" } rustc-demangle = "0.1.21" rustc_attr = { path = "../rustc_attr" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } -rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fs_util = { path = "../rustc_fs_util" } @@ -30,6 +29,7 @@ rustc_metadata = { path = "../rustc_metadata" } rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index cc8b3a1a4e4..9eb3574e77b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -37,7 +37,7 @@ fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool { // LLVM prior to version 12 had known miscompiles in the presence of // noalias attributes (see #54878), but we don't support earlier // versions at all anymore. We now enable mutable noalias by default. - cx.tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(true) + cx.tcx.sess.opts.unstable_opts.mutable_noalias.unwrap_or(true) } const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = @@ -569,6 +569,22 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { &[cmse_nonsecure_call], ); } + + // Some intrinsics require that an elementtype attribute (with the pointee type of a + // pointer argument) is added to the callsite. + let element_type_index = unsafe { llvm::LLVMRustGetElementTypeArgIndex(callsite) }; + if element_type_index >= 0 { + let arg_ty = self.args[element_type_index as usize].layout.ty; + let pointee_ty = arg_ty.builtin_deref(true).expect("Must be pointer argument").ty; + let element_type_attr = unsafe { + llvm::LLVMRustCreateElementTypeAttr(bx.llcx, bx.layout_of(pointee_ty).llvm_type(bx)) + }; + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Argument(element_type_index as u32), + &[element_type_attr], + ); + } } } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index f935acb1a7e..72961ae888e 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -145,7 +145,7 @@ pub(crate) unsafe fn codegen( if tcx.sess.target.default_hidden_visibility { llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden); } - let val = tcx.sess.opts.debugging_opts.oom.should_panic(); + let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); llvm::LLVMSetInitializer(ll_g, llval); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 9394d60134f..aabbe8ac276 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,7 +13,7 @@ use smallvec::SmallVec; use crate::attributes; use crate::llvm::AttributePlace::Function; -use crate::llvm::{self, Attribute, AttributeKind, AttributePlace}; +use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace}; use crate::llvm_util; pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; @@ -56,7 +56,7 @@ pub fn sanitize_attrs<'ll>( no_sanitize: SanitizerSet, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize; + let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; if enabled.contains(SanitizerSet::ADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -69,6 +69,9 @@ pub fn sanitize_attrs<'ll>( if enabled.contains(SanitizerSet::HWADDRESS) { attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx)); } + if enabled.contains(SanitizerSet::SHADOWCALLSTACK) { + attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx)); + } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. let features = cx.tcx.global_backend_features(()); @@ -136,7 +139,7 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { if cx .sess() .opts - .debugging_opts + .unstable_opts .sanitizer .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) { @@ -149,7 +152,7 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { } // probestack doesn't play nice either with gcov profiling. - if cx.sess().opts.debugging_opts.profile { + if cx.sess().opts.unstable_opts.profile { return None; } @@ -224,6 +227,10 @@ pub(crate) fn default_optimisation_attrs<'ll>( attrs } +fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { + llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") +} + /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub fn from_fn_attrs<'ll, 'tcx>( @@ -275,7 +282,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.push(uwtable_attr(cx.llcx)); } - if cx.sess().opts.debugging_opts.profile_sample_use.is_some() { + if cx.sess().opts.unstable_opts.profile_sample_use.is_some() { to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile")); } @@ -299,12 +306,61 @@ pub fn from_fn_attrs<'ll, 'tcx>( } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); + // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions. + // And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules. + // Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768 + to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx)); + // Need this for AArch64. + to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) + { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0)); + let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned; + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + flags |= AllocKindFlags::Uninitialized; + } else { + flags |= AllocKindFlags::Zeroed; + } + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); + } // apply to return place instead of function (unlike all other attributes applied in this function) let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr( + cx.llcx, + AllocKindFlags::Realloc | AllocKindFlags::Aligned, + )); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3)); + } + let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) { + if llvm_util::get_version() >= (15, 0, 0) { + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + } + } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); } diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index da9d8b5fb33..baa858709a0 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -11,7 +11,6 @@ use std::str; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use rustc_codegen_ssa::back::archive::ArchiveBuilder; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_session::cstore::{DllCallingConvention, DllImport}; use rustc_session::Session; @@ -96,19 +95,23 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { } } - fn inject_dll_import_lib( - &mut self, + fn sess(&self) -> &Session { + self.sess + } + + fn create_dll_import_lib( + sess: &Session, lib_name: &str, dll_imports: &[DllImport], - tmpdir: &MaybeTempDir, - ) { + tmpdir: &Path, + ) -> PathBuf { let output_path = { - let mut output_path: PathBuf = tmpdir.as_ref().to_path_buf(); + let mut output_path: PathBuf = tmpdir.to_path_buf(); output_path.push(format!("{}_imports", lib_name)); output_path.with_extension("lib") }; - let target = &self.sess.target; + let target = &sess.target; let mingw_gnu_toolchain = target.vendor == "pc" && target.os == "windows" && target.env == "gnu" @@ -117,7 +120,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports .iter() .map(|import: &DllImport| { - if self.sess.target.arch == "x86" { + if sess.target.arch == "x86" { ( LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), import.ordinal, @@ -134,8 +137,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = - tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + let def_file_path = tmpdir.join(format!("{}_imports", lib_name)).with_extension("def"); let def_file_content = format!( "EXPORTS\n{}", @@ -154,11 +156,11 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { match std::fs::write(&def_file_path, def_file_content) { Ok(_) => {} Err(e) => { - self.sess.fatal(&format!("Error writing .DEF file: {}", e)); + sess.fatal(&format!("Error writing .DEF file: {}", e)); } }; - let dlltool = find_binutils_dlltool(self.sess); + let dlltool = find_binutils_dlltool(sess); let result = std::process::Command::new(dlltool) .args([ "-d", @@ -172,9 +174,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { match result { Err(e) => { - self.sess.fatal(&format!("Error calling dlltool: {}", e)); + sess.fatal(&format!("Error calling dlltool: {}", e)); } - Ok(output) if !output.status.success() => self.sess.fatal(&format!( + Ok(output) if !output.status.success() => sess.fatal(&format!( "Dlltool could not create import library: {}\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) @@ -220,13 +222,13 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path_z.as_ptr(), ffi_exports.as_ptr(), ffi_exports.len(), - llvm_machine_type(&self.sess.target.arch) as u16, - !self.sess.target.is_like_msvc, + llvm_machine_type(&sess.target.arch) as u16, + !sess.target.is_like_msvc, ) }; if result == crate::llvm::LLVMRustResult::Failure { - self.sess.fatal(&format!( + sess.fatal(&format!( "Error creating import library for {}: {}", lib_name, llvm::last_error().unwrap_or("unknown LLVM error".to_string()) @@ -234,13 +236,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { } }; - self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { - self.sess.fatal(&format!( - "failed to add native library {}: {}", - output_path.display(), - e - )); - }); + output_path } } @@ -351,7 +347,7 @@ fn string_to_io_error(s: String) -> io::Error { fn find_binutils_dlltool(sess: &Session) -> OsString { assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); - if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool { + if let Some(dlltool_path) = &sess.opts.unstable_opts.dlltool { return dlltool_path.clone().into_os_string(); } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 38402e04313..3731c6bcfe7 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -199,7 +199,7 @@ pub(crate) fn run_thin( pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBuffer) { let name = module.name.clone(); - let buffer = ThinBuffer::new(module.module_llvm.llmod()); + let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); (name, buffer) } @@ -325,20 +325,6 @@ fn fat_lto( drop(linker); save_temp_bitcode(cgcx, &module, "lto.input"); - // Fat LTO also suffers from the invalid DWARF issue similar to Thin LTO. - // Here we rewrite all `DICompileUnit` pointers if there is only one `DICompileUnit`. - // This only works around the problem when codegen-units = 1. - // Refer to the comments in the `optimize_thin_module` function for more details. - let mut cu1 = ptr::null_mut(); - let mut cu2 = ptr::null_mut(); - unsafe { llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2) }; - if !cu2.is_null() { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_fat_lto_patch_debuginfo", &*module.name); - unsafe { llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1) }; - save_temp_bitcode(cgcx, &module, "fat-lto-after-patch"); - } - // Internalize everything below threshold to help strip out more modules and such. unsafe { let ptr = symbols_below_threshold.as_ptr(); @@ -709,9 +695,9 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: &llvm::Module) -> ThinBuffer { + pub fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m); + let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); ThinBuffer(buffer) } } @@ -769,7 +755,7 @@ pub unsafe fn optimize_thin_module( // an error. let mut cu1 = ptr::null_mut(); let mut cu2 = ptr::null_mut(); - llvm::LLVMRustLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); + llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); if !cu2.is_null() { let msg = "multiple source DICompileUnits found"; return Err(write::llvm_err(&diag_handler, msg)); @@ -858,7 +844,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_thin_lto_patch_debuginfo", thin_module.name()); - llvm::LLVMRustLTOPatchDICompileUnit(llmod, cu1); + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); save_temp_bitcode(cgcx, &module, "thin-lto-after-patch"); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ab8874d796d..534d32e8a90 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -107,7 +107,7 @@ pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut ll let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() { tcx.output_filenames(()).split_dwarf_path( tcx.sess.split_debuginfo(), - tcx.sess.opts.debugging_opts.split_dwarf_kind, + tcx.sess.opts.unstable_opts.split_dwarf_kind, Some(mod_name), ) } else { @@ -182,9 +182,9 @@ pub fn target_machine_factory( let use_softfp = sess.opts.cg.soft_float; let ffunction_sections = - sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections); + sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections); let fdata_sections = ffunction_sections; - let funique_section_names = !sess.opts.debugging_opts.no_unique_section_names; + let funique_section_names = !sess.opts.unstable_opts.no_unique_section_names; let code_model = to_llvm_code_model(sess.code_model()); @@ -202,15 +202,15 @@ pub fn target_machine_factory( let features = CString::new(target_features.join(",")).unwrap(); let abi = SmallCStr::new(&sess.target.llvm_abiname); let trap_unreachable = - sess.opts.debugging_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable); - let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes; + sess.opts.unstable_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable); + let emit_stack_size_section = sess.opts.unstable_opts.emit_stack_sizes; let asm_comments = sess.asm_comments(); let relax_elf_relocations = - sess.opts.debugging_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations); + sess.opts.unstable_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations); let use_init_array = - !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); + !sess.opts.unstable_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); let path_mapping = sess.source_map().path_mapping().clone(); @@ -790,7 +790,7 @@ pub(crate) unsafe fn codegen( let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name); - let thin = ThinBuffer::new(llmod); + let thin = ThinBuffer::new(llmod, config.emit_thin_lto); let data = thin.data(); if let Some(bitcode_filename) = bc_out.file_name() { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4a4cccb490d..d3096c73a8a 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -626,32 +626,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn type_metadata(&mut self, function: &'ll Value, typeid: String) { - let typeid_metadata = self.typeid_metadata(typeid); - let v = [self.const_usize(0), typeid_metadata]; - unsafe { - llvm::LLVMGlobalSetMetadata( - function, - llvm::MD_type as c_uint, - llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( - self.cx.llcx, - v.as_ptr(), - v.len() as c_uint, - )), - ) - } - } - - fn typeid_metadata(&mut self, typeid: String) -> Self::Value { - unsafe { - llvm::LLVMMDStringInContext( - self.cx.llcx, - typeid.as_ptr() as *const c_char, - typeid.as_bytes().len() as c_uint, - ) - } - } - fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value { self.store_with_flags(val, ptr, align, MemFlags::empty()) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 77cbbf4c6ca..fb4da9a5f33 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -257,6 +257,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.get_fn_addr(fn_instance.polymorphize(self.tcx)), self.data_layout().instruction_address_space, ), + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc = self + .tcx + .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .unwrap_memory(); + let init = const_alloc_to_llvm(self, alloc); + let value = self.static_addr_of(init, alloc.inner().align, None); + (value, AddressSpace::DATA) + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 5bbbfe9a4ab..18467e37082 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -64,7 +64,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation< // See https://github.com/rust-lang/rust/issues/84565. 1 } else { - cx.sess().opts.debugging_opts.uninit_const_chunk_threshold + cx.sess().opts.unstable_opts.uninit_const_chunk_threshold }; let allow_uninit_chunks = chunks.clone().take(max.saturating_add(1)).count() <= max; @@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation< let address_space = match cx.tcx.global_alloc(alloc_id) { GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space, - GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA, + GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { + AddressSpace::DATA + } }; llvals.push(cx.scalar_to_backend( @@ -535,10 +537,20 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { // The semantics of #[used] in Rust only require the symbol to make it into the // object file. It is explicitly allowed for the linker to strip the symbol if it - // is dead. As such, use llvm.compiler.used instead of llvm.used. + // is dead, which means we are allowed use `llvm.compiler.used` instead of + // `llvm.used` here. + // // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs - // in some versions of the gold linker. + // in the handling of `.init_array` (the static constructor list) in versions of + // the gold linker (prior to the one released with binutils 2.36). + // + // That said, we only ever emit these when compiling for ELF targets, unless + // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage + // on other targets, in particular MachO targets have *their* static constructor + // lists broken if `llvm.compiler.used` is emitted rather than llvm.used. However, + // that check happens when assigning the `CodegenFnAttrFlags` in `rustc_typeck`, + // so we don't need to take care of it here. self.add_compiler_used_global(g); } if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 55e4a4a7255..5857b83f6c9 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -275,7 +275,7 @@ pub unsafe fn create_module<'ll>( } } - if let Some(BranchProtection { bti, pac_ret }) = sess.opts.debugging_opts.branch_protection { + if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch != "aarch64" { sess.err("-Zbranch-protection is only supported on aarch64"); } else { @@ -308,7 +308,7 @@ pub unsafe fn create_module<'ll>( } // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). - if let CFProtection::Branch | CFProtection::Full = sess.opts.debugging_opts.cf_protection { + if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, @@ -316,7 +316,7 @@ pub unsafe fn create_module<'ll>( 1, ) } - if let CFProtection::Return | CFProtection::Full = sess.opts.debugging_opts.cf_protection { + if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, @@ -325,7 +325,7 @@ pub unsafe fn create_module<'ll>( ) } - if sess.opts.debugging_opts.virtual_function_elimination { + if sess.opts.unstable_opts.virtual_function_elimination { llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Error, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 5186aee57fb..80fd9726fc7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -55,7 +55,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' // The initial byte `4` instructs GDB that the following pretty printer // is defined inline as opposed to in a standalone file. section_contents.extend_from_slice(b"\x04"); - let vis_name = format!("pretty-printer-{}-{}\n", crate_name.as_str(), index); + let vis_name = format!("pretty-printer-{}-{}\n", crate_name, index); section_contents.extend_from_slice(vis_name.as_bytes()); section_contents.extend_from_slice(&visualizer.src); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index d5f39a45670..bd84100e0e8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -824,7 +824,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( output_filenames .split_dwarf_path( tcx.sess.split_debuginfo(), - tcx.sess.opts.debugging_opts.split_dwarf_kind, + tcx.sess.opts.unstable_opts.split_dwarf_kind, Some(codegen_unit_name), ) // We get a path relative to the working directory from split_dwarf_path @@ -881,15 +881,15 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( split_name.len(), kind, 0, - tcx.sess.opts.debugging_opts.split_dwarf_inlining, + tcx.sess.opts.unstable_opts.split_dwarf_inlining, ); - if tcx.sess.opts.debugging_opts.profile { + if tcx.sess.opts.unstable_opts.profile { let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata); let default_gcda_path = &output_filenames.with_extension("gcda"); let gcda_path = - tcx.sess.opts.debugging_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); + tcx.sess.opts.unstable_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); let gcov_cu_info = [ path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")), @@ -1420,7 +1420,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( cx, type_map::stub( cx, - Stub::VtableTy { vtable_holder }, + Stub::VTableTy { vtable_holder }, unique_type_id, &vtable_type_name, (size, pointer_align), @@ -1559,7 +1559,7 @@ pub fn create_vtable_di_node<'ll, 'tcx>( ) { // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in // LLVM at the moment. - if cx.sess().opts.debugging_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat { + if cx.sess().opts.unstable_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat { vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 8fc8118849b..ce2f419c4ac 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> { pub enum Stub<'ll> { Struct, Union, - VtableTy { vtable_holder: &'ll DIType }, + VTableTy { vtable_holder: &'ll DIType }, } pub struct StubInfo<'ll, 'tcx> { @@ -180,9 +180,9 @@ pub(super) fn stub<'ll, 'tcx>( let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); let metadata = match kind { - Stub::Struct | Stub::VtableTy { .. } => { + Stub::Struct | Stub::VTableTy { .. } => { let vtable_holder = match kind { - Stub::VtableTy { vtable_holder } => Some(vtable_holder), + Stub::VTableTy { vtable_holder } => Some(vtable_holder), _ => None, }; unsafe { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 730048d061b..cf591295b84 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -97,23 +97,26 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - let dwarf_version = - sess.opts.debugging_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); - llvm::LLVMRustAddModuleFlag( - self.llmod, - llvm::LLVMModFlagBehavior::Warning, - "Dwarf Version\0".as_ptr().cast(), - dwarf_version, - ); - - // Indicate that we want CodeView debug information on MSVC - if sess.target.is_like_msvc { + if !sess.target.is_like_msvc { + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + let dwarf_version = sess + .opts + .unstable_opts + .dwarf_version + .unwrap_or(sess.target.default_dwarf_version); + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "Dwarf Version\0".as_ptr().cast(), + dwarf_version, + ); + } else { + // Indicate that we want CodeView debug information on MSVC llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 5a5c4f7f860..fa0ecd18fc8 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -18,7 +18,9 @@ use crate::llvm; use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; +use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_middle::ty::Ty; +use rustc_symbol_mangling::typeid::typeid_for_fnabi; use smallvec::SmallVec; use tracing::debug; @@ -97,6 +99,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_abi.llvm_type(self), ); fn_abi.apply_attrs_llfn(self, llfn); + + if self.tcx.sess.is_sanitizer_cfi_enabled() { + let typeid = typeid_for_fnabi(self.tcx, fn_abi); + self.set_type_metadata(llfn, typeid); + } + llfn } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a7dd8e16d28..25ce1cef944 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -6,7 +6,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(hash_raw_entry)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(extern_types)] #![feature(once_cell)] @@ -324,8 +324,8 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::print_version(); } - fn target_features(&self, sess: &Session) -> Vec<Symbol> { - target_features(sess) + fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { + target_features(sess, allow_unstable) } fn codegen_crate<'tcx>( @@ -355,7 +355,7 @@ impl CodegenBackend for LlvmCodegenBackend { .join(sess); sess.time("llvm_dump_timing_file", || { - if sess.opts.debugging_opts.llvm_time_trace { + if sess.opts.unstable_opts.llvm_time_trace { let file_name = outputs.with_extension("llvm_timings.json"); llvm_util::time_trace_profiler_finish(&file_name); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs index e2fa5e488ed..45de284d22a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs @@ -20,19 +20,6 @@ pub enum OptimizationDiagnosticKind { OptimizationRemarkOther, } -impl OptimizationDiagnosticKind { - pub fn describe(self) -> &'static str { - match self { - OptimizationRemark | OptimizationRemarkOther => "remark", - OptimizationMissed => "missed", - OptimizationAnalysis => "analysis", - OptimizationAnalysisFPCommute => "floating-point", - OptimizationAnalysisAliasing => "aliasing", - OptimizationFailure => "failure", - } - } -} - pub struct OptimizationDiagnostic<'ll> { pub kind: OptimizationDiagnosticKind, pub pass_name: String, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 5ebc2d6139f..3139f93bfef 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -191,6 +191,11 @@ pub enum AttributeKind { StackProtect = 32, NoUndef = 33, SanitizeMemTag = 34, + NoCfCheck = 35, + ShadowCallStack = 36, + AllocSize = 37, + AllocatedPointer = 38, + AllocAlign = 39, } /// LLVMIntPredicate @@ -567,16 +572,6 @@ pub enum ArchiveKind { K_COFF, } -/// LLVMRustPassKind -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(C)] -#[allow(dead_code)] // Variants constructed by C++. -pub enum PassKind { - Other, - Function, - Module, -} - // LLVMRustThinLTOData extern "C" { pub type ThinLTOData; @@ -587,10 +582,6 @@ extern "C" { pub type ThinLTOBuffer; } -// LLVMRustModuleNameCallback -pub type ThinLTOModuleNameCallback = - unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char); - /// LLVMRustThinLTOModule #[repr(C)] pub struct ThinLTOModule { @@ -656,9 +647,6 @@ extern "C" { } #[repr(C)] pub struct Builder<'a>(InvariantOpaque<'a>); -extern "C" { - pub type MemoryBuffer; -} #[repr(C)] pub struct PassManager<'a>(InvariantOpaque<'a>); extern "C" { @@ -984,6 +972,22 @@ pub mod debuginfo { } } +use bitflags::bitflags; +// These values **must** match with LLVMRustAllocKindFlags +bitflags! { + #[repr(transparent)] + #[derive(Default)] + pub struct AllocKindFlags : u64 { + const Unknown = 0; + const Alloc = 1; + const Realloc = 1 << 1; + const Free = 1 << 2; + const Uninitialized = 1 << 3; + const Zeroed = 1 << 4; + const Aligned = 1 << 5; + } +} + extern "C" { pub type ModuleBuffer; } @@ -1011,7 +1015,6 @@ extern "C" { pub fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); /// See Module::setModuleInlineAsm. - pub fn LLVMSetModuleInlineAsm2(M: &Module, Asm: *const c_char, AsmLen: size_t); pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t); /// See llvm::LLVMTypeKind::getTypeID. @@ -1165,7 +1168,6 @@ extern "C" { pub fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; pub fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value); pub fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; - pub fn LLVMSetThreadLocal(GlobalVar: &Value, IsThreadLocal: Bool); pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); @@ -1190,7 +1192,10 @@ extern "C" { pub fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; pub fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; pub fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; + pub fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; + pub fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; // Operations on functions pub fn LLVMRustGetOrInsertFunction<'a>( @@ -2244,7 +2249,6 @@ extern "C" { pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - pub fn LLVMRustPassKind(Pass: &Pass) -> PassKind; pub fn LLVMRustFindAndCreatePass(Pass: *const c_char) -> Option<&'static mut Pass>; pub fn LLVMRustCreateAddressSanitizerFunctionPass(Recover: bool) -> &'static mut Pass; pub fn LLVMRustCreateModuleAddressSanitizerPass(Recover: bool) -> &'static mut Pass; @@ -2361,7 +2365,6 @@ extern "C" { ) -> LLVMRustResult; pub fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char); pub fn LLVMRustPrintPasses(); - pub fn LLVMRustGetInstructionCount(M: &Module) -> u32; pub fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); pub fn LLVMRustAddAlwaysInlinePass(P: &PassManagerBuilder, AddLifetimes: bool); pub fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); @@ -2459,7 +2462,6 @@ extern "C" { pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t); - pub fn LLVMRustUnsetComdat(V: &Value); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); @@ -2469,7 +2471,7 @@ extern "C" { pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub fn LLVMRustModuleCost(M: &Module) -> u64; - pub fn LLVMRustThinLTOBufferCreate(M: &Module) -> &'static mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; @@ -2491,11 +2493,6 @@ extern "C" { Module: &Module, Target: &TargetMachine, ) -> bool; - pub fn LLVMRustGetThinLTOModuleImports( - Data: *const ThinLTOData, - ModuleNameCallback: ThinLTOModuleNameCallback, - CallbackPayload: *mut c_void, - ); pub fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); pub fn LLVMRustParseBitcodeForLTO( Context: &Context, @@ -2508,8 +2505,12 @@ extern "C" { len: usize, out_len: &mut usize, ) -> *const u8; - pub fn LLVMRustLTOGetDICompileUnit(M: &Module, CU1: &mut *mut c_void, CU2: &mut *mut c_void); - pub fn LLVMRustLTOPatchDICompileUnit(M: &Module, CU: *mut c_void); + pub fn LLVMRustThinLTOGetDICompileUnit( + M: &Module, + CU1: &mut *mut c_void, + CU2: &mut *mut c_void, + ); + pub fn LLVMRustThinLTOPatchDICompileUnit(M: &Module, CU: *mut c_void); pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub fn LLVMRustLinkerAdd( @@ -2541,4 +2542,6 @@ extern "C" { #[allow(improper_ctypes)] pub fn LLVMRustGetMangledName(V: &Value, out: &RustString); + + pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 48fbc1de8ee..6602a4ab863 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -95,6 +95,14 @@ pub fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute { unsafe { LLVMRustCreateUWTableAttr(llcx, async_) } } +pub fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute { + unsafe { LLVMRustCreateAllocSizeAttr(llcx, size_arg) } +} + +pub fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute { + unsafe { LLVMRustCreateAllocKindAttr(llcx, kind_arg.bits()) } +} + #[derive(Copy, Clone)] pub enum AttributePlace { ReturnValue, @@ -158,12 +166,6 @@ pub fn SetUniqueComdat(llmod: &Module, val: &Value) { } } -pub fn UnsetComdat(val: &Value) { - unsafe { - LLVMRustUnsetComdat(val); - } -} - pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { unsafe { LLVMSetUnnamedAddress(global, unnamed); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index ce6c6e3215c..a0a640473eb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -87,7 +87,7 @@ unsafe fn configure_llvm(sess: &Session) { add("-debug-pass=Structure", false); } if sess.target.generate_arange_section - && !sess.opts.debugging_opts.no_generate_arange_section + && !sess.opts.unstable_opts.no_generate_arange_section { add("-generate-arange-section", false); } @@ -102,7 +102,7 @@ unsafe fn configure_llvm(sess: &Session) { add("-enable-machine-outliner=never", false); } - match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) { + match sess.opts.unstable_opts.merge_functions.unwrap_or(sess.target.merge_functions) { MergeFunctions::Disabled | MergeFunctions::Trampolines => {} MergeFunctions::Aliases => { add("-mergefunc-use-aliases", false); @@ -125,7 +125,7 @@ unsafe fn configure_llvm(sess: &Session) { } } - if sess.opts.debugging_opts.llvm_time_trace { + if sess.opts.unstable_opts.llvm_time_trace { llvm::LLVMTimeTraceProfilerInitialize(); } @@ -133,11 +133,11 @@ unsafe fn configure_llvm(sess: &Session) { // Use the legacy plugin registration if we don't use the new pass manager if !should_use_new_llvm_pass_manager( - &sess.opts.debugging_opts.new_llvm_pass_manager, + &sess.opts.unstable_opts.new_llvm_pass_manager, &sess.target.arch, ) { // Register LLVM plugins by loading them into the compiler process. - for plugin in &sess.opts.debugging_opts.llvm_plugins { + for plugin in &sess.opts.unstable_opts.llvm_plugins { let lib = Library::new(plugin).unwrap_or_else(|e| bug!("couldn't load plugin: {}", e)); debug!("LLVM plugin loaded successfully {:?} ({})", lib, plugin); @@ -233,26 +233,29 @@ pub fn check_tied_features( // Used to generate cfg variables and apply features // Must express features in the way Rust understands them -pub fn target_features(sess: &Session) -> Vec<Symbol> { +pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { let target_machine = create_informational_target_machine(sess); - let mut features: Vec<Symbol> = - supported_target_features(sess) - .iter() - .filter_map(|&(feature, gate)| { - if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } - }) - .filter(|feature| { - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { - return false; - } + let mut features: Vec<Symbol> = supported_target_features(sess) + .iter() + .filter_map(|&(feature, gate)| { + if sess.is_nightly_build() || allow_unstable || gate.is_none() { + Some(feature) + } else { + None + } + }) + .filter(|feature| { + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { + return false; } - true - }) - .map(|feature| Symbol::intern(feature)) - .collect(); + } + true + }) + .map(|feature| Symbol::intern(feature)) + .collect(); // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64 // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use @@ -538,7 +541,7 @@ fn backend_feature_name(s: &str) -> Option<&str> { } pub fn tune_cpu(sess: &Session) -> Option<&str> { - let name = sess.opts.debugging_opts.tune_cpu.as_ref()?; + let name = sess.opts.unstable_opts.tune_cpu.as_ref()?; Some(handle_native(name)) } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index cf2d3c423c3..eeb38d4ecf5 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -19,7 +19,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size}; use std::fmt; use std::ptr; -use libc::c_uint; +use libc::{c_char, c_uint}; impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { @@ -289,3 +289,31 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty.llvm_type(self) } } + +impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { + fn set_type_metadata(&self, function: &'ll Value, typeid: String) { + let typeid_metadata = self.typeid_metadata(typeid); + let v = [self.const_usize(0), typeid_metadata]; + unsafe { + llvm::LLVMGlobalSetMetadata( + function, + llvm::MD_type as c_uint, + llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( + self.llcx, + v.as_ptr(), + v.len() as c_uint, + )), + ) + } + } + + fn typeid_metadata(&self, typeid: String) -> &'ll Value { + unsafe { + llvm::LLVMMDStringInContext( + self.llcx, + typeid.as_ptr() as *const c_char, + typeid.len() as c_uint, + ) + } + } +} diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index faabea92f5a..46d6344dbb2 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -14,7 +14,7 @@ tracing = "0.1" libc = "0.2.50" jobserver = "0.1.22" tempfile = "3.2" -thorin-dwp = "0.2" +thorin-dwp = "0.3" pathdiff = "0.2.0" serde_json = "1.0.59" snap = "1" @@ -40,6 +40,7 @@ rustc_metadata = { path = "../rustc_metadata" } rustc_query_system = { path = "../rustc_query_system" } rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } +rustc_const_eval = { path = "../rustc_const_eval" } [dependencies.object] version = "0.29.0" diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 1cb8d342381..53550b049db 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -51,10 +51,37 @@ pub trait ArchiveBuilder<'a> { fn build(self) -> bool; + fn sess(&self) -> &Session; + + /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library>. + /// and returns the path on disk to that import library. + /// This functions doesn't take `self` so that it can be called from + /// `linker_with_args`, which is specialized on `ArchiveBuilder` but + /// doesn't take or create an instance of that type. + fn create_dll_import_lib( + sess: &Session, + lib_name: &str, + dll_imports: &[DllImport], + tmpdir: &Path, + ) -> PathBuf; + + /// Creates a DLL Import Library <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-creation#creating-an-import-library> + /// and adds it to the current compilation's set of archives. fn inject_dll_import_lib( &mut self, lib_name: &str, dll_imports: &[DllImport], tmpdir: &MaybeTempDir, - ); + ) { + let output_path = + Self::create_dll_import_lib(self.sess(), lib_name, dll_imports, tmpdir.as_ref()); + + self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { + self.sess().fatal(&format!( + "failed to add native library {}: {}", + output_path.display(), + e + )); + }); + } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 1628d580b88..f0d320c7c21 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -6,6 +6,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; +use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; @@ -28,10 +29,7 @@ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{create_rmeta_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; -use crate::{ - looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, - METADATA_FILENAME, -}; +use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib}; use cc::windows_registry; use regex::Regex; @@ -67,7 +65,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); for &crate_type in sess.crate_types().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. - if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) + if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) && !output_metadata && crate_type == CrateType::Executable { @@ -122,7 +120,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( &out_filename, codegen_results, path.as_ref(), - ); + )?; } } if sess.opts.json_artifact_notifications { @@ -241,22 +239,6 @@ pub fn each_linked_rlib( Ok(()) } -/// We use a temp directory here to avoid races between concurrent rustc processes, -/// such as builds in the same directory using the same filename for metadata while -/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a -/// directory being searched for `extern crate` (observing an incomplete file). -/// The returned path is the temporary file containing the complete metadata. -pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { - let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); - let result = fs::write(&out_filename, metadata); - - if let Err(e) = result { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - - out_filename -} - /// Create an 'rlib'. /// /// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains @@ -595,7 +577,7 @@ fn link_dwarf_object<'a>( let mut package = thorin::DwarfPackage::new(&thorin_sess); // Input objs contain .o/.dwo files from the current crate. - match sess.opts.debugging_opts.split_dwarf_kind { + match sess.opts.unstable_opts.split_dwarf_kind { SplitDwarfKind::Single => { for input_obj in cg_results.modules.iter().filter_map(|m| m.object.as_ref()) { package.add_input_object(input_obj)?; @@ -668,7 +650,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( out_filename: &Path, codegen_results: &CodegenResults, tmpdir: &Path, -) { +) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); let mut cmd = linker_with_args::<B>( @@ -679,7 +661,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( tmpdir, out_filename, codegen_results, - ); + )?; linker::disable_localization(&mut cmd); @@ -1018,11 +1000,13 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( (Strip::None, _) => {} } } + + Ok(()) } // Temporarily support both -Z strip and -C strip fn strip_value(sess: &Session) -> Strip { - match (sess.opts.debugging_opts.strip, sess.opts.cg.strip) { + match (sess.opts.unstable_opts.strip, sess.opts.cg.strip) { (s, Strip::None) => s, (_, s) => s, } @@ -1074,7 +1058,7 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d return; } - let sanitizer = sess.opts.debugging_opts.sanitizer; + let sanitizer = sess.opts.unstable_opts.sanitizer; if sanitizer.contains(SanitizerSet::ADDRESS) { link_sanitizer_runtime(sess, linker, "asan"); } @@ -1253,7 +1237,7 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { return (false, false); } - match (sess.split_debuginfo(), sess.opts.debugging_opts.split_dwarf_kind) { + match (sess.split_debuginfo(), sess.opts.unstable_opts.split_dwarf_kind) { // If there is no split debuginfo then do not preserve objects. (SplitDebuginfo::Off, _) => (false, false), // If there is packed split debuginfo, then the debuginfo in the objects @@ -1601,7 +1585,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) if let Some(args) = sess.target.pre_link_args.get(&flavor) { cmd.args(args.iter().map(Deref::deref)); } - cmd.args(&sess.opts.debugging_opts.pre_link_args); + cmd.args(&sess.opts.unstable_opts.pre_link_args); } /// Add a link script embedded in the target, if applicable. @@ -1809,7 +1793,7 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: /// Add options making relocation sections in the produced ELF files read-only /// and suppressing lazy binding. fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { - match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.relro_level) { + match sess.opts.unstable_opts.relro_level.unwrap_or(sess.target.relro_level) { RelroLevel::Full => cmd.full_relro(), RelroLevel::Partial => cmd.partial_relro(), RelroLevel::Off => cmd.no_relro(), @@ -1866,7 +1850,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, -) -> Command { +) -> Result<Command, ErrorGuaranteed> { let crt_objects_fallback = crt_objects_fallback(sess, crate_type); let cmd = &mut *super::linker::get_linker( sess, @@ -1957,7 +1941,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // (or alternatively link all native libraries after their respective crates). // This change is somewhat breaking in practice due to local static libraries being linked // as whole-archive (#85144), so removing whole-archive may be a pre-requisite. - if sess.opts.debugging_opts.link_native_libraries { + if sess.opts.unstable_opts.link_native_libraries { add_local_native_libraries(cmd, sess, codegen_results); } @@ -1969,10 +1953,22 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // If -Zlink-native-libraries=false is set, then the assumption is that an // external build system already has the native dependencies defined, and it // will provide them to the linker itself. - if sess.opts.debugging_opts.link_native_libraries { + if sess.opts.unstable_opts.link_native_libraries { add_upstream_native_libraries(cmd, sess, codegen_results); } + // Link with the import library generated for any raw-dylib functions. + for (raw_dylib_name, raw_dylib_imports) in + collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? + { + cmd.add_object(&B::create_dll_import_lib( + sess, + &raw_dylib_name, + &raw_dylib_imports, + tmpdir, + )); + } + // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make // command line shorter, reset it to default here before adding more libraries. cmd.reset_per_library_state(); @@ -2016,7 +2012,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // to it and remove the option. add_post_link_args(cmd, sess, flavor); - cmd.take_cmd() + Ok(cmd.take_cmd()) } fn add_order_independent_options( @@ -2037,7 +2033,7 @@ fn add_order_independent_options( add_link_script(cmd, sess, tmpdir, crate_type); if sess.target.os == "fuchsia" && crate_type == CrateType::Executable { - let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { + let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) { "asan/" } else { "" @@ -2100,7 +2096,12 @@ fn add_order_independent_options( // sections to ensure we have all the data for PGO. let keep_metadata = crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); - cmd.gc_sections(keep_metadata); + if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols + { + cmd.gc_sections(keep_metadata); + } else { + cmd.no_gc_sections(); + } } cmd.set_output_kind(link_output_kind, out_filename); @@ -2240,8 +2241,7 @@ fn add_local_native_libraries( } } NativeLibKind::RawDylib => { - // FIXME(#58713): Proper handling for raw dylibs. - bug!("raw_dylib feature not yet implemented"); + // Ignore RawDylib here, they are handled separately in linker_with_args(). } } } @@ -2362,7 +2362,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // If -Zlink-native-libraries=false is set, then the assumption is that an // external build system already has the native dependencies defined, and it // will provide them to the linker itself. - if sess.opts.debugging_opts.link_native_libraries { + if sess.opts.unstable_opts.link_native_libraries { let mut last = (None, NativeLibKind::Unspecified, None); for lib in &codegen_results.crate_info.native_libraries[&cnum] { let Some(name) = lib.name else { @@ -2709,7 +2709,7 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> { } fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - if let Some(ld_impl) = sess.opts.debugging_opts.gcc_ld { + if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld { if let LinkerFlavor::Gcc = flavor { match ld_impl { LdImpl::Lld => { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 955ee245b28..a4bbdefbb87 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -303,7 +303,7 @@ impl<'a> GccLinker<'a> { config::OptLevel::Aggressive => "O3", }; - if let Some(path) = &self.sess.opts.debugging_opts.profile_sample_use { + if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use { self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; self.linker_args(&[ @@ -325,7 +325,7 @@ impl<'a> GccLinker<'a> { // purely to support rustbuild right now, we should get a more // principled solution at some point to force the compiler to pass // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { + if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name { let mut rpath = OsString::from("@rpath/"); rpath.push(out_filename.file_name().unwrap()); self.linker_args(&[OsString::from("-install_name"), rpath]); @@ -566,9 +566,7 @@ impl<'a> Linker for GccLinker<'a> { } fn no_gc_sections(&mut self) { - if self.sess.target.is_like_osx { - self.linker_arg("-no_dead_strip"); - } else if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm { + if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm { self.linker_arg("--no-gc-sections"); } } @@ -640,9 +638,14 @@ impl<'a> Linker for GccLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility in object files typically takes care of this. - if crate_type == CrateType::Executable && self.sess.target.override_export_symbols.is_none() - { - return; + if crate_type == CrateType::Executable { + let should_export_executable_symbols = + self.sess.opts.unstable_opts.export_executable_symbols; + if self.sess.target.override_export_symbols.is_none() + && !should_export_executable_symbols + { + return; + } } // We manually create a list of exported symbols to ensure we don't expose any more. @@ -969,7 +972,11 @@ impl<'a> Linker for MsvcLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility takes care of this typically if crate_type == CrateType::Executable { - return; + let should_export_executable_symbols = + self.sess.opts.unstable_opts.export_executable_symbols; + if !should_export_executable_symbols { + return; + } } let path = tmpdir.join("lib.def"); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 3dd607adee5..0302c28815a 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -16,14 +16,13 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::rustc_erase_owner; use rustc_data_structures::sync::MetadataRef; +use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; use rustc_session::cstore::MetadataLoader; use rustc_session::Session; use rustc_target::abi::Endian; use rustc_target::spec::{RelocModel, Target}; -use crate::METADATA_FILENAME; - /// The default metadata loader. This is used by cg_llvm and cg_clif. /// /// # Metadata location diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index f651814be7e..d1a267f7643 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -232,15 +232,15 @@ fn exported_symbols_provider_local<'tcx>( })); } - if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) { + if tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) { let mut msan_weak_symbols = Vec::new(); // Similar to profiling, preserve weak msan symbol during LTO. - if tcx.sess.opts.debugging_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) { + if tcx.sess.opts.unstable_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) { msan_weak_symbols.push("__msan_keep_going"); } - if tcx.sess.opts.debugging_opts.sanitizer_memory_track_origins != 0 { + if tcx.sess.opts.unstable_opts.sanitizer_memory_track_origins != 0 { msan_weak_symbols.push("__msan_track_origins"); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index f4a5cac872e..1b5ad87107a 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -37,6 +37,7 @@ use rustc_target::spec::{MergeFunctions, SanitizerSet}; use std::any::Any; use std::fs; use std::io; +use std::marker::PhantomData; use std::mem; use std::path::{Path, PathBuf}; use std::str; @@ -99,6 +100,7 @@ pub struct ModuleConfig { pub emit_ir: bool, pub emit_asm: bool, pub emit_obj: EmitObj, + pub emit_thin_lto: bool, pub bc_cmdline: String, // Miscellaneous flags. These are mostly copied from command-line @@ -179,24 +181,24 @@ impl ModuleConfig { SwitchWithOptPath::Disabled ), pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None), - pgo_sample_use: if_regular!(sess.opts.debugging_opts.profile_sample_use.clone(), None), - debug_info_for_profiling: sess.opts.debugging_opts.debug_info_for_profiling, + pgo_sample_use: if_regular!(sess.opts.unstable_opts.profile_sample_use.clone(), None), + debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling, instrument_coverage: if_regular!(sess.instrument_coverage(), false), instrument_gcov: if_regular!( // compiler_builtins overrides the codegen-units settings, // which is incompatible with -Zprofile which requires that // only a single codegen unit is used per crate. - sess.opts.debugging_opts.profile && !is_compiler_builtins, + sess.opts.unstable_opts.profile && !is_compiler_builtins, false ), - sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()), + sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()), sanitizer_recover: if_regular!( - sess.opts.debugging_opts.sanitizer_recover, + sess.opts.unstable_opts.sanitizer_recover, SanitizerSet::empty() ), sanitizer_memory_track_origins: if_regular!( - sess.opts.debugging_opts.sanitizer_memory_track_origins, + sess.opts.unstable_opts.sanitizer_memory_track_origins, 0 ), @@ -218,6 +220,7 @@ impl ModuleConfig { false ), emit_obj, + emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto, bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(), verify_llvm_ir: sess.verify_llvm_ir(), @@ -247,7 +250,7 @@ impl ModuleConfig { // O2 and O3) since it can be useful for reducing code size. merge_functions: match sess .opts - .debugging_opts + .unstable_opts .merge_functions .unwrap_or(sess.target.merge_functions) { @@ -259,9 +262,9 @@ impl ModuleConfig { }, inline_threshold: sess.opts.cg.inline_threshold, - new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager, + new_llvm_pass_manager: sess.opts.unstable_opts.new_llvm_pass_manager, emit_lifetime_markers: sess.emit_lifetime_markers(), - llvm_plugins: if_regular!(sess.opts.debugging_opts.llvm_plugins.clone(), vec![]), + llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]), } } @@ -473,10 +476,13 @@ pub fn start_async_codegen<B: ExtraBackendMethods>( metadata_module, crate_info, - coordinator_send, codegen_worker_receive, shared_emitter_main, - future: coordinator_thread, + coordinator: Coordinator { + sender: coordinator_send, + future: Some(coordinator_thread), + phantom: PhantomData, + }, output_filenames: tcx.output_filenames(()).clone(), } } @@ -926,7 +932,7 @@ fn finish_intra_module_work<B: ExtraBackendMethods>( ) -> Result<WorkItemResult<B>, FatalError> { let diag_handler = cgcx.create_diag_handler(); - if !cgcx.opts.debugging_opts.combine_cgu + if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Metadata || module.kind == ModuleKind::Allocator { @@ -1048,14 +1054,13 @@ fn start_executing_work<B: ExtraBackendMethods>( each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); - let ol = if tcx.sess.opts.debugging_opts.no_codegen - || !tcx.sess.opts.output_types.should_codegen() - { - // If we know that we won’t be doing codegen, create target machines without optimisation. - config::OptLevel::No - } else { - tcx.backend_optimization_level(()) - }; + let ol = + if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { + // If we know that we won’t be doing codegen, create target machines without optimisation. + config::OptLevel::No + } else { + tcx.backend_optimization_level(()) + }; let backend_features = tcx.global_backend_features(()); let cgcx = CodegenContext::<B> { backend: backend.clone(), @@ -1064,7 +1069,7 @@ fn start_executing_work<B: ExtraBackendMethods>( lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, - time_trace: sess.opts.debugging_opts.llvm_time_trace, + time_trace: sess.opts.unstable_opts.llvm_time_trace, opts: Arc::new(sess.opts.clone()), prof: sess.prof.clone(), exported_symbols, @@ -1087,7 +1092,7 @@ fn start_executing_work<B: ExtraBackendMethods>( target_arch: tcx.sess.target.arch.to_string(), debuginfo: tcx.sess.opts.debuginfo, split_debuginfo: tcx.sess.split_debuginfo(), - split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf_kind, + split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1272,6 +1277,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // work to be done. while !codegen_done || running > 0 + || main_thread_worker_state == MainThreadWorkerState::LLVMing || (!codegen_aborted && !(work_items.is_empty() && needs_fat_lto.is_empty() @@ -1346,7 +1352,7 @@ fn start_executing_work<B: ExtraBackendMethods>( .binary_search_by_key(&cost, |&(_, cost)| cost) .unwrap_or_else(|e| e); work_items.insert(insertion_index, (work, cost)); - if !cgcx.opts.debugging_opts.no_parallel_llvm { + if !cgcx.opts.unstable_opts.no_parallel_llvm { helper.request_token(); } } @@ -1466,17 +1472,15 @@ fn start_executing_work<B: ExtraBackendMethods>( }; work_items.insert(insertion_index, (llvm_work_item, cost)); - if !cgcx.opts.debugging_opts.no_parallel_llvm { + if !cgcx.opts.unstable_opts.no_parallel_llvm { helper.request_token(); } - assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; } Message::CodegenComplete => { codegen_done = true; - assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); main_thread_worker_state = MainThreadWorkerState::Idle; } @@ -1488,10 +1492,8 @@ fn start_executing_work<B: ExtraBackendMethods>( // then conditions above will ensure no more work is spawned but // we'll keep executing this loop until `running` hits 0. Message::CodegenAborted => { - assert!(!codegen_aborted); codegen_done = true; codegen_aborted = true; - assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); } Message::Done { result: Ok(compiled_module), worker_id } => { free_worker(worker_id); @@ -1531,13 +1533,20 @@ fn start_executing_work<B: ExtraBackendMethods>( Message::Done { result: Err(None), worker_id: _ } => { bug!("worker thread panicked"); } - Message::Done { result: Err(Some(WorkerFatalError)), worker_id: _ } => { - return Err(()); + Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => { + // Similar to CodegenAborted, wait for remaining work to finish. + free_worker(worker_id); + codegen_done = true; + codegen_aborted = true; } Message::CodegenItem => bug!("the coordinator should not receive codegen requests"), } } + if codegen_aborted { + return Err(()); + } + let needs_link = mem::take(&mut needs_link); if !needs_link.is_empty() { assert!(compiled_modules.is_empty()); @@ -1827,16 +1836,39 @@ impl SharedEmitterMain { } } +pub struct Coordinator<B: ExtraBackendMethods> { + pub sender: Sender<Box<dyn Any + Send>>, + future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>, + // Only used for the Message type. + phantom: PhantomData<B>, +} + +impl<B: ExtraBackendMethods> Coordinator<B> { + fn join(mut self) -> std::thread::Result<Result<CompiledModules, ()>> { + self.future.take().unwrap().join() + } +} + +impl<B: ExtraBackendMethods> Drop for Coordinator<B> { + fn drop(&mut self) { + if let Some(future) = self.future.take() { + // If we haven't joined yet, signal to the coordinator that it should spawn no more + // work, and wait for worker threads to finish. + drop(self.sender.send(Box::new(Message::CodegenAborted::<B>))); + drop(future.join()); + } + } +} + pub struct OngoingCodegen<B: ExtraBackendMethods> { pub backend: B, pub metadata: EncodedMetadata, pub metadata_module: Option<CompiledModule>, pub crate_info: CrateInfo, - pub coordinator_send: Sender<Box<dyn Any + Send>>, pub codegen_worker_receive: Receiver<Message<B>>, pub shared_emitter_main: SharedEmitterMain, - pub future: thread::JoinHandle<Result<CompiledModules, ()>>, pub output_filenames: Arc<OutputFilenames>, + pub coordinator: Coordinator<B>, } impl<B: ExtraBackendMethods> OngoingCodegen<B> { @@ -1844,8 +1876,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { let _timer = sess.timer("finish_ongoing_codegen"); self.shared_emitter_main.check(sess, true); - let future = self.future; - let compiled_modules = sess.time("join_worker_thread", || match future.join() { + let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() { Ok(Ok(compiled_modules)) => compiled_modules, Ok(Err(())) => { sess.abort_if_errors(); @@ -1893,26 +1924,13 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { // These are generally cheap and won't throw off scheduling. let cost = 0; - submit_codegened_module_to_llvm(&self.backend, &self.coordinator_send, module, cost); + submit_codegened_module_to_llvm(&self.backend, &self.coordinator.sender, module, cost); } pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); - drop(self.coordinator_send.send(Box::new(Message::CodegenComplete::<B>))); - } - - /// Consumes this context indicating that codegen was entirely aborted, and - /// we need to exit as quickly as possible. - /// - /// This method blocks the current thread until all worker threads have - /// finished, and all worker threads should have exited or be real close to - /// exiting at this point. - pub fn codegen_aborted(self) { - // Signal to the coordinator it should spawn no more work and start - // shutdown. - drop(self.coordinator_send.send(Box::new(Message::CodegenAborted::<B>))); - drop(self.future.join()); + drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>))); } pub fn check_for_errors(&self, sess: &Session) { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 7e2e85ead54..a840b270974 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -39,7 +39,6 @@ use rustc_target::abi::{Align, VariantIdx}; use std::collections::BTreeSet; use std::convert::TryFrom; -use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; use itertools::Itertools; @@ -171,7 +170,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ); let new_vptr = bx.load(ptr_ty, gep, ptr_align); bx.nonnull_metadata(new_vptr); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(new_vptr); new_vptr } else { @@ -521,7 +520,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( need_metadata_module: bool, ) -> OngoingCodegen<B> { // Skip crate items and just output metadata in -Z no-codegen mode. - if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { + if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None, 1); ongoing_codegen.codegen_finished(tcx); @@ -583,7 +582,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( metadata_module, codegen_units.len(), ); - let ongoing_codegen = AbortCodegenOnDrop::<B>(Some(ongoing_codegen)); // Codegen an allocator shim, if necessary. // @@ -704,7 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( submit_codegened_module_to_llvm( &backend, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, module, cost, ); @@ -714,7 +712,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( submit_pre_lto_module_to_llvm( &backend, tcx, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -725,7 +723,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( CguReuse::PostLto => { submit_post_lto_module_to_llvm( &backend, - &ongoing_codegen.coordinator_send, + &ongoing_codegen.coordinator.sender, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -752,55 +750,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } ongoing_codegen.check_for_errors(tcx.sess); - - ongoing_codegen.into_inner() -} - -/// A curious wrapper structure whose only purpose is to call `codegen_aborted` -/// when it's dropped abnormally. -/// -/// In the process of working on rust-lang/rust#55238 a mysterious segfault was -/// stumbled upon. The segfault was never reproduced locally, but it was -/// suspected to be related to the fact that codegen worker threads were -/// sticking around by the time the main thread was exiting, causing issues. -/// -/// This structure is an attempt to fix that issue where the `codegen_aborted` -/// message will block until all workers have finished. This should ensure that -/// even if the main codegen thread panics we'll wait for pending work to -/// complete before returning from the main thread, hopefully avoiding -/// segfaults. -/// -/// If you see this comment in the code, then it means that this workaround -/// worked! We may yet one day track down the mysterious cause of that -/// segfault... -struct AbortCodegenOnDrop<B: ExtraBackendMethods>(Option<OngoingCodegen<B>>); - -impl<B: ExtraBackendMethods> AbortCodegenOnDrop<B> { - fn into_inner(mut self) -> OngoingCodegen<B> { - self.0.take().unwrap() - } -} - -impl<B: ExtraBackendMethods> Deref for AbortCodegenOnDrop<B> { - type Target = OngoingCodegen<B>; - - fn deref(&self) -> &OngoingCodegen<B> { - self.0.as_ref().unwrap() - } -} - -impl<B: ExtraBackendMethods> DerefMut for AbortCodegenOnDrop<B> { - fn deref_mut(&mut self) -> &mut OngoingCodegen<B> { - self.0.as_mut().unwrap() - } -} - -impl<B: ExtraBackendMethods> Drop for AbortCodegenOnDrop<B> { - fn drop(&mut self) { - if let Some(codegen) = self.0.take() { - codegen.codegen_aborted(); - } - } + ongoing_codegen } impl CrateInfo { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4be3ae11e4e..1802eedf193 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -64,9 +64,6 @@ pub struct ModuleCodegen<M> { pub kind: ModuleKind, } -// FIXME(eddyb) maybe include the crate name in this? -pub const METADATA_FILENAME: &str = "lib.rmeta"; - impl<M> ModuleCodegen<M> { pub fn into_compiled_module( self, diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 5203ebfad75..27d791d90a5 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -25,7 +25,7 @@ impl<'a, 'tcx> VirtualIndex { let llty = bx.fn_ptr_backend_type(fn_abi); let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty)); - if bx.cx().sess().opts.debugging_opts.virtual_function_elimination + if bx.cx().sess().opts.unstable_opts.virtual_function_elimination && bx.cx().sess().lto() == Lto::Fat { let typeid = @@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex { let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); let ptr = bx.load(llty, gep, ptr_align); bx.nonnull_metadata(ptr); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(ptr); ptr } @@ -58,7 +58,7 @@ impl<'a, 'tcx> VirtualIndex { let usize_align = bx.tcx().data_layout.pointer_align.abi; let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); let ptr = bx.load(llty, gep, usize_align); - // Vtable loads are invariant. + // VTable loads are invariant. bx.set_invariant_load(ptr); ptr } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b8e3cb32ef6..3eee58d9d1c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -20,9 +20,9 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitable}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; -use rustc_symbol_mangling::typeid_for_fnabi; +use rustc_symbol_mangling::typeid::typeid_for_fnabi; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; -use rustc_target::abi::{self, HasDataLayout, InitKind, WrappingRange}; +use rustc_target::abi::{self, HasDataLayout, WrappingRange}; use rustc_target::spec::abi::Abi; /// Used by `FunctionCx::codegen_terminator` for emitting common patterns @@ -528,7 +528,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info: mir::SourceInfo, target: Option<mir::BasicBlock>, cleanup: Option<mir::BasicBlock>, - strict_validity: bool, ) -> bool { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message @@ -547,12 +546,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }); if let Some(intrinsic) = panic_intrinsic { use AssertIntrinsic::*; + let ty = instance.unwrap().substs.type_at(0); let layout = bx.layout_of(ty); let do_panic = match intrinsic { Inhabited => layout.abi.is_uninhabited(), - ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity), - UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity), + ZeroValid => !bx.tcx().permits_zero_init(layout), + UninitValid => !bx.tcx().permits_uninit_init(layout), }; if do_panic { let msg_str = with_no_visible_paths!({ @@ -687,7 +687,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info, target, cleanup, - self.cx.tcx().sess.opts.debugging_opts.strict_init_checks, ) { return; } @@ -919,7 +918,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(rcvalle): Add support for generalized identifiers. // FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers. let typeid = typeid_for_fnabi(bx.tcx(), fn_abi); - let typeid_metadata = bx.typeid_metadata(typeid); + let typeid_metadata = self.cx.typeid_metadata(typeid); // Test whether the function pointer is associated with the type identifier. let cond = bx.type_test(fn_ptr, typeid_metadata); diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index a283bf1de76..f1fe495282a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -9,11 +9,8 @@ use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. - let scope_data = &self.mir.source_scopes[scope]; - let instance = if let Some((inlined_instance, _)) = scope_data.inlined { - self.monomorphize(inlined_instance) - } else if let Some(inlined_scope) = scope_data.inlined_parent_scope { - self.monomorphize(self.mir.source_scopes[inlined_scope].inlined.unwrap().0) + let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { + self.monomorphize(inlined) } else { self.instance }; diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index f2d1827c792..8c3186efc63 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return span; } - if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros { + if span.from_expansion() && !self.cx.sess().opts.unstable_opts.debug_macros { // Walk up the macro expansion chain until we reach a non-expanded span. // We also stop at the function body level because no line stepping can occur // at the level above that. diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 645afae30d8..94ac71a4dd2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -3,12 +3,16 @@ use super::place::PlaceRef; use super::FunctionCx; use crate::common::{span_invalid_monomorphization_error, IntPredicate}; use crate::glue; +use crate::meth; use crate::traits::*; use crate::MemFlags; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{sym, Span}; -use rustc_target::abi::call::{FnAbi, PassMode}; +use rustc_target::abi::{ + call::{FnAbi, PassMode}, + WrappingRange, +}; fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, @@ -102,6 +106,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes()) } } + sym::vtable_size | sym::vtable_align => { + let vtable = args[0].immediate(); + let idx = match name { + sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE, + sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN, + _ => bug!(), + }; + let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable); + if name == sym::vtable_align { + // Alignment is always nonzero. + bx.range_metadata(value, WrappingRange { start: 1, end: !0 }); + }; + value + } sym::pref_align_of | sym::needs_drop | sym::type_id diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index ec3f7a2156a..8ee375fa9e3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -3,7 +3,6 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable}; -use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; @@ -247,13 +246,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for (bb, _) in traversal::reverse_postorder(&mir) { fx.codegen_block(bb); } - - // For backends that support CFI using type membership (i.e., testing whether a given pointer - // is associated with a type identifier). - if cx.tcx().sess.is_sanitizer_cfi_enabled() { - let typeid = typeid_for_fnabi(cx.tcx(), fn_abi); - bx.type_metadata(llfn, typeid); - } } /// Produces, for each argument, a `Value` pointing at the diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 58cee0c8bb0..268c4d76503 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -435,18 +435,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Place(place) => place, LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx), LocalRef::Operand(..) => { - if let Some(elem) = place_ref - .projection - .iter() - .enumerate() - .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref)) - { - base = elem.0 + 1; + if place_ref.has_deref() { + base = 1; let cg_base = self.codegen_consume( bx, - mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref }, + mir::PlaceRef { projection: &place_ref.projection[..0], ..place_ref }, ); - cg_base.deref(bx.cx()) } else { bug!("using operand local {:?} as place", place_ref); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index bfdef2dc0e8..ecad0518533 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -249,6 +249,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("bulk-memory", Some(sym::wasm_target_feature)), ("mutable-globals", Some(sym::wasm_target_feature)), ("reference-types", Some(sym::wasm_target_feature)), + ("sign-ext", Some(sym::wasm_target_feature)), ]; const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))]; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 1e53c73d1bb..779bd3ea278 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -59,7 +59,7 @@ impl<'tcx, T> Backend<'tcx> for T where pub trait CodegenBackend { fn init(&self, _sess: &Session) {} fn print(&self, _req: PrintRequest, _sess: &Session) {} - fn target_features(&self, _sess: &Session) -> Vec<Symbol> { + fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } fn print_passes(&self) {} diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 37f2bfd3c4f..9f49749bb41 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -160,8 +160,6 @@ pub trait BuilderMethods<'a, 'tcx>: fn range_metadata(&mut self, load: Self::Value, range: WrappingRange); fn nonnull_metadata(&mut self, load: Self::Value); - fn type_metadata(&mut self, function: Self::Function, typeid: String); - fn typeid_metadata(&mut self, typeid: String) -> Self::Value; fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value; fn store_with_flags( @@ -221,7 +219,7 @@ pub trait BuilderMethods<'a, 'tcx>: assert!(matches!(self.cx().type_kind(float_ty), TypeKind::Float | TypeKind::Double)); assert_eq!(self.cx().type_kind(int_ty), TypeKind::Integer); - if let Some(false) = self.cx().sess().opts.debugging_opts.saturating_float_casts { + if let Some(false) = self.cx().sess().opts.unstable_opts.saturating_float_casts { return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) }; } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 396768e0a42..782fdadbfb8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -40,7 +40,8 @@ pub use self::intrinsic::IntrinsicCallMethods; pub use self::misc::MiscMethods; pub use self::statics::{StaticBuilderMethods, StaticMethods}; pub use self::type_::{ - ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods, + ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods, + TypeMethods, }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index a2a3cb56c78..413d31bb942 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -13,7 +13,9 @@ pub trait StaticMethods: BackendTypes { /// Same as add_used_global(), but only prevent the compiler from potentially removing an /// otherwise unused symbol. The linker is still permitted to drop it. /// - /// This corresponds to the semantics of the `#[used]` attribute. + /// This corresponds to the documented semantics of the `#[used]` attribute, although + /// on some targets (non-ELF), we may use `add_used_global` for `#[used]` statics + /// instead. fn add_compiler_used_global(&self, global: Self::Value); } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 5d3f07317a3..8158e8dd011 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -117,6 +117,13 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { ) -> Self::Type; } +// For backends that support CFI using type membership (i.e., testing whether a given pointer is +// associated with a type identifier). +pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> { + fn set_type_metadata(&self, function: Self::Function, typeid: String); + fn typeid_metadata(&self, typeid: String) -> Self::Value; +} + pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { fn store_fn_arg( &mut self, @@ -133,6 +140,12 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type; } -pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} +pub trait TypeMethods<'tcx>: + DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> +{ +} -impl<'tcx, T> TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {} +impl<'tcx, T> TypeMethods<'tcx> for T where + Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> +{ +} 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 b18976302b4..ba8222dc152 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -165,6 +165,7 @@ pub(super) fn op_to_const<'tcx>( Ok(ref mplace) => to_const_value(mplace), // see comment on `let try_as_immediate` above Err(imm) => match *imm { + _ if imm.layout.is_zst() => ConstValue::ZeroSized, Immediate::Scalar(x) => match x { ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()), @@ -172,18 +173,17 @@ pub(super) fn op_to_const<'tcx>( Immediate::ScalarPair(a, b) => { debug!("ScalarPair(a: {:?}, b: {:?})", a, b); // We know `offset` is relative to the allocation, so we can use `into_parts`. - let (data, start) = - match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() { - (Some(alloc_id), offset) => { - (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) - } - (None, _offset) => ( - ecx.tcx.intern_const_alloc( - Allocation::from_bytes_byte_aligned_immutable(b"" as &[u8]), - ), - 0, - ), - }; + let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() { + (Some(alloc_id), offset) => { + (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) + } + (None, _offset) => ( + ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable( + b"" as &[u8], + )), + 0, + ), + }; let len = b.to_machine_usize(ecx).unwrap(); let start = start.try_into().unwrap(); let len: usize = len.try_into().unwrap(); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 29ab1d18771..fc2e6652a3d 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -104,7 +104,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { } impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { - pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self { + pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self { CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new(), @@ -309,7 +309,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &PlaceTy<'tcx, Self::PointerTag>, + dest: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, _unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -369,7 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // we don't deallocate it. let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?; let is_allocated_in_another_const = matches!( - ecx.tcx.get_global_alloc(alloc_id), + ecx.tcx.try_get_global_alloc(alloc_id), Some(interpret::GlobalAlloc::Memory(_)) ); @@ -470,14 +470,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, #[inline(always)] fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { &ecx.machine.stack } #[inline(always)] fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { &mut ecx.machine.stack } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index bf65fdc54ca..948c3349498 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>( let mplace = ecx.deref_operand(&op).unwrap(); if let Some(alloc_id) = mplace.ptr.provenance { assert_eq!( - tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability, + tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability, Mutability::Not, "deref_mir_constant cannot be used with mutable allocations as \ that could allow pattern matching to observe mutable statics", @@ -147,7 +147,6 @@ pub(crate) fn deref_mir_constant<'tcx>( let ty = match mplace.meta { MemPlaceMeta::None => mplace.layout.ty, - MemPlaceMeta::Poison => bug!("poison metadata in `deref_mir_constant`: {:#?}", mplace), // In case of unsized types, figure out the real type behind. MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { ty::Str => bug!("there's no sized equivalent of a `str`"), diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 2288a4e7b6c..8fff4571d12 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>( let offset = place_adjusted.layout.fields.offset(i); place - .offset( + .offset_with_meta( offset, MemPlaceMeta::Meta(Scalar::from_machine_usize( num_elems as u64, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 5d598b65c72..c97c31eb9da 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -18,10 +18,10 @@ use super::{ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn cast( &mut self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, cast_kind: CastKind, cast_ty: Ty<'tcx>, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { use rustc_middle::mir::CastKind::*; // FIXME: In which cases should we trigger UB when the source is uninit? @@ -114,9 +114,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn misc_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { use rustc_type_ir::sty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); @@ -173,14 +173,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn pointer_expose_address_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_)); assert!(cast_ty.is_integral()); let scalar = src.to_scalar()?; - let ptr = self.scalar_to_ptr(scalar)?; + let ptr = scalar.to_pointer(self)?; match ptr.into_pointer_or_addr() { Ok(ptr) => M::expose_ptr(self, ptr)?, Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP. @@ -190,9 +190,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn pointer_from_exposed_address_cast( &mut self, - src: &ImmTy<'tcx, M::PointerTag>, + src: &ImmTy<'tcx, M::Provenance>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { assert!(src.layout.ty.is_integral()); assert_matches!(cast_ty.kind(), ty::RawPtr(_)); @@ -208,10 +208,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn cast_from_int_like( &self, - scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout) + scalar: Scalar<M::Provenance>, // input value (there is no ScalarTy so we separate data+layout) src_layout: TyAndLayout<'tcx>, cast_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. @@ -245,9 +245,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }) } - fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag> + fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance> where - F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>, + F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>, { use rustc_type_ir::sty::TyKind::*; match *dest_ty.kind() { @@ -279,8 +279,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn unsize_into_ptr( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, // The pointee types source_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, @@ -298,30 +298,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_immediate(val, dest) } (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - let val = self.read_immediate(src)?; - if data_a.principal_def_id() == data_b.principal_def_id() { - return self.write_immediate(*val, dest); - } - // trait upcasting coercion - let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - src_pointee_ty, - dest_pointee_ty, - )); - - if let Some(entry_idx) = vptr_entry_idx { - let entry_idx = u64::try_from(entry_idx).unwrap(); - let (old_data, old_vptr) = val.to_scalar_pair()?; - let old_vptr = self.scalar_to_ptr(old_vptr)?; - let new_vptr = self - .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?; - self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) - } else { - self.write_immediate(*val, dest) + let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?; + let old_vptr = old_vptr.to_pointer(self)?; + let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; + if old_trait != data_a.principal() { + throw_ub_format!("upcast on a pointer whose vtable does not match its type"); } + let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; + self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } (_, &ty::Dynamic(ref data, _)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; let ptr = self.read_immediate(src)?.to_scalar()?; let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) @@ -335,9 +323,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn unsize_into( &mut self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, cast_ty: TyAndLayout<'tcx>, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 2e47cf89210..150d6589b08 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,10 +2,8 @@ use std::cell::Cell; use std::fmt; use std::mem; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_index::vec::IndexVec; -use rustc_macros::HashStable; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo}; use rustc_middle::ty::layout::{ @@ -16,7 +14,6 @@ use rustc_middle::ty::{ self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, }; use rustc_mir_dataflow::storage::always_storage_live_locals; -use rustc_query_system::ich::StableHashingContext; use rustc_session::Limit; use rustc_span::{Pos, Span}; use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout}; @@ -84,7 +81,7 @@ impl Drop for SpanGuard { } /// A stack frame. -pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { +pub struct Frame<'mir, 'tcx, Prov: Provenance = AllocId, Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// @@ -105,7 +102,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { /// The location where the result of the current stack frame should be written to, /// and its layout in the caller. - pub return_place: PlaceTy<'tcx, Tag>, + pub return_place: PlaceTy<'tcx, Prov>, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. @@ -114,7 +111,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { /// can either directly contain `Scalar` or refer to some part of an `Allocation`. /// /// Do *not* access this directly; always go through the machine hook! - pub locals: IndexVec<mir::Local, LocalState<'tcx, Tag>>, + pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>, /// The span of the `tracing` crate is stored here. /// When the guard is dropped, the span is exited. This gives us @@ -142,7 +139,7 @@ pub struct FrameInfo<'tcx> { } /// Unwind information. -#[derive(Clone, Copy, Eq, PartialEq, Debug, HashStable)] +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum StackPopUnwind { /// The cleanup block. Cleanup(mir::BasicBlock), @@ -152,7 +149,7 @@ pub enum StackPopUnwind { NotAllowed, } -#[derive(Clone, Copy, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these +#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these pub enum StackPopCleanup { /// Jump to the next block in the caller, or cause UB if None (that's a function /// that may never return). Also store layout of return place so @@ -168,17 +165,16 @@ pub enum StackPopCleanup { } /// State of a local variable including a memoized layout -#[derive(Clone, Debug, PartialEq, Eq, HashStable)] -pub struct LocalState<'tcx, Tag: Provenance = AllocId> { - pub value: LocalValue<Tag>, +#[derive(Clone, Debug)] +pub struct LocalState<'tcx, Prov: Provenance = AllocId> { + pub value: LocalValue<Prov>, /// Don't modify if `Some`, this is only used to prevent computing the layout twice - #[stable_hasher(ignore)] pub layout: Cell<Option<TyAndLayout<'tcx>>>, } /// Current value of a local variable -#[derive(Copy, Clone, PartialEq, Eq, HashStable, Debug)] // Miri debug-prints these -pub enum LocalValue<Tag: Provenance = AllocId> { +#[derive(Copy, Clone, Debug)] // Miri debug-prints these +pub enum LocalValue<Prov: Provenance = AllocId> { /// This local is not currently alive, and cannot be used at all. Dead, /// A normal, live local. @@ -186,16 +182,16 @@ pub enum LocalValue<Tag: Provenance = AllocId> { /// This is an optimization over just always having a pointer here; /// we can thus avoid doing an allocation when the local just stores /// immediate values *and* never has its address taken. - Live(Operand<Tag>), + Live(Operand<Prov>), } -impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { +impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> { /// Read the local's value or error if the local is not yet live or not live anymore. /// /// Note: This may only be invoked from the `Machine::access_local` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! #[inline] - pub fn access(&self) -> InterpResult<'tcx, &Operand<Tag>> { + pub fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> { match &self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? LocalValue::Live(val) => Ok(val), @@ -208,7 +204,7 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { /// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from /// anywhere else. You may be invalidating machine invariants if you do! #[inline] - pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Tag>> { + pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> { match &mut self.value { LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? LocalValue::Live(val) => Ok(val), @@ -216,8 +212,8 @@ impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { } } -impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> { - pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Tag, Extra> { +impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> { + pub fn with_extra<Extra>(self, extra: Extra) -> Frame<'mir, 'tcx, Prov, Extra> { Frame { body: self.body, instance: self.instance, @@ -231,7 +227,7 @@ impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> { } } -impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> { +impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { /// Get the current location within the Frame. /// /// If this is `Err`, we are not currently executing any particular statement in @@ -426,14 +422,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { + pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] { M::stack(self) } #[inline(always)] pub(crate) fn stack_mut( &mut self, - ) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> { + ) -> &mut Vec<Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>> { M::stack_mut(self) } @@ -445,12 +441,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { + pub fn frame(&self) -> &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { self.stack().last().expect("no call frames exist") } #[inline(always)] - pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> { + pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { self.stack_mut().last_mut().expect("no call frames exist") } @@ -507,7 +503,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// stack frame), to bring it into the proper environment for this interpreter. pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, value: T, ) -> Result<T, InterpError<'tcx>> { frame @@ -544,7 +540,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn layout_of_local( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { @@ -573,7 +569,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This can fail to provide an answer for extern types. pub(super) fn size_and_align_of( &self, - metadata: &MemPlaceMeta<M::PointerTag>, + metadata: &MemPlaceMeta<M::Provenance>, layout: &TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { if !layout.is_unsized() { @@ -633,9 +629,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(Some((size, align))) } ty::Dynamic(..) => { - let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?; + let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.read_size_and_align_from_vtable(vtable)?)) + Ok(Some(self.get_vtable_size_and_align(vtable)?)) } ty::Slice(_) | ty::Str => { @@ -659,7 +655,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline] pub fn size_and_align_of_mplace( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, + mplace: &MPlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Option<(Size, Align)>> { self.size_and_align_of(&mplace.meta, &mplace.layout) } @@ -669,7 +665,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, instance: ty::Instance<'tcx>, body: &'mir mir::Body<'tcx>, - return_place: &PlaceTy<'tcx, M::PointerTag>, + return_place: &PlaceTy<'tcx, M::Provenance>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); @@ -678,7 +674,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { body, loc: Err(body.span), // Span used for errors caused during preamble. return_to_block, - return_place: *return_place, + return_place: return_place.clone(), // empty local array, we fill it in below, after we are inside the stack frame and // all methods actually know about the frame locals: IndexVec::new(), @@ -799,7 +795,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let op = self .local_to_op(self.frame(), mir::RETURN_PLACE, None) .expect("return place should always be live"); - let dest = self.frame().return_place; + let dest = self.frame().return_place.clone(); let err = self.copy_op(&op, &dest, /*allow_transmute*/ true); trace!("return value: {:?}", self.dump_place(*dest)); // We delay actually short-circuiting on this error until *after* the stack frame is @@ -895,7 +891,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[instrument(skip(self), level = "debug")] - fn deallocate_local(&mut self, local: LocalValue<M::PointerTag>) -> InterpResult<'tcx> { + fn deallocate_local(&mut self, local: LocalValue<M::Provenance>) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. Hence we can `unwrap`. @@ -913,7 +909,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_to_allocation( &self, gid: GlobalId<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics // and thus don't care about the parameter environment. While we could just use // `self.param_env`, that would mean we invoke the query to evaluate the static @@ -931,7 +927,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[must_use] - pub fn dump_place(&self, place: Place<M::PointerTag>) -> PlacePrinter<'_, 'mir, 'tcx, M> { + pub fn dump_place(&self, place: Place<M::Provenance>) -> PlacePrinter<'_, 'mir, 'tcx, M> { PlacePrinter { ecx: self, place } } @@ -960,7 +956,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Helper struct for the `dump_place` function. pub struct PlacePrinter<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { ecx: &'a InterpCx<'mir, 'tcx, M>, - place: Place<M::PointerTag>, + place: Place<M::Provenance>, } impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug @@ -987,7 +983,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug " by {} ref {:?}:", match mplace.meta { MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), - MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(), + MemPlaceMeta::None => String::new(), }, mplace.ptr, )?; @@ -1021,31 +1017,3 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug } } } - -impl<'ctx, 'mir, 'tcx, Tag: Provenance, Extra> HashStable<StableHashingContext<'ctx>> - for Frame<'mir, 'tcx, Tag, Extra> -where - Extra: HashStable<StableHashingContext<'ctx>>, - Tag: HashStable<StableHashingContext<'ctx>>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) { - // Exhaustive match on fields to make sure we forget no field. - let Frame { - body, - instance, - return_to_block, - return_place, - locals, - loc, - extra, - tracing_span: _, - } = self; - body.hash_stable(hcx, hasher); - instance.hash_stable(hcx, hasher); - return_to_block.hash_stable(hcx, hasher); - return_place.hash_stable(hcx, hasher); - locals.hash_stable(hcx, hasher); - loc.hash_stable(hcx, hasher); - extra.hash_stable(hcx, hasher); - } -} diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 9dfdafcb38e..376b8872c90 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -33,7 +33,7 @@ pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< 'mir, 'tcx, MemoryKind = T, - PointerTag = AllocId, + Provenance = AllocId, ExtraFnVal = !, FrameExtra = (), AllocExtra = (), @@ -94,7 +94,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval: // to validation to error -- it has the much better error messages, pointing out where // in the value the dangling reference lies. // The `delay_span_bug` ensures that we don't forget such a check in validation. - if tcx.get_global_alloc(alloc_id).is_none() { + if tcx.try_get_global_alloc(alloc_id).is_none() { tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer"); } // treat dangling pointers like other statics @@ -217,7 +217,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } if let Some(def) = mplace.layout.ty.ty_adt_def() { - if Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() { + if def.is_unsafe_cell() { // We are crossing over an `UnsafeCell`, we can mutate again. This means that // References we encounter inside here are interned as pointing to mutable // allocations. @@ -245,7 +245,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - let ptr = self.ecx.scalar_to_ptr(mplace.meta.unwrap_meta())?; + let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?; if let Some(alloc_id) = ptr.provenance { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. @@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive< .sess .span_err(ecx.tcx.span, "encountered dangling pointer in final constant"); return Err(reported); - } else if ecx.tcx.get_global_alloc(alloc_id).is_none() { + } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() { // We have hit an `AllocId` that is neither in local or global memory and isn't // marked as dangling by local memory. That should be impossible. span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); @@ -474,7 +474,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> layout: TyAndLayout<'tcx>, f: impl FnOnce( &mut InterpCx<'mir, 'tcx, M>, - &PlaceTy<'tcx, M::PointerTag>, + &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, ConstAllocation<'tcx>> { let dest = self.allocate(layout, MemoryKind::Stack)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 93b64d9d37a..08209eb7932 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar}, BinOp, }; use rustc_middle::ty; @@ -15,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Abi, Align, InitKind, Primitive, Size}; +use rustc_target::abi::{Abi, Align, Primitive, Size}; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, @@ -25,7 +25,7 @@ use super::{ mod caller_location; mod type_name; -fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> { +fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> { let size = match kind { Primitive::Int(integer, _) => integer.size(), _ => bug!("invalid `{}` argument: {:?}", name, bits), @@ -114,8 +114,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn emulate_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, M::PointerTag>], - dest: &PlaceTy<'tcx, M::PointerTag>, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, ret: Option<mir::BasicBlock>, ) -> InterpResult<'tcx, bool> { let substs = instance.substs; @@ -132,8 +132,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Some(p) => p, }; - // Keep the patterns in this match ordered the same as the list in - // `src/librustc_middle/ty/constness.rs` match intrinsic_name { sym::caller_location => { let span = self.find_closest_untracked_caller_location(); @@ -330,7 +328,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We managed to find a valid allocation for one pointer, but not the other. // That means they are definitely not pointing to the same allocation. throw_ub_format!( - "{} called on pointers into different allocations", + "`{}` called on pointers into different allocations", intrinsic_name ); } @@ -338,7 +336,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Found allocation for both. They must be into the same allocation. if a_alloc_id != b_alloc_id { throw_ub_format!( - "{} called on pointers into different allocations", + "`{}` called on pointers into different allocations", intrinsic_name ); } @@ -348,47 +346,71 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Compute distance. - let distance = { - // The subtraction is always done in `isize` to enforce - // the "no more than `isize::MAX` apart" requirement. - let a_offset = ImmTy::from_uint(a_offset, isize_layout); - let b_offset = ImmTy::from_uint(b_offset, isize_layout); - let (val, overflowed, _ty) = - self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; + let dist = { + // Addresses are unsigned, so this is a `usize` computation. We have to do the + // overflow check separately anyway. + let (val, overflowed, _ty) = { + let a_offset = ImmTy::from_uint(a_offset, usize_layout); + let b_offset = ImmTy::from_uint(b_offset, usize_layout); + self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)? + }; if overflowed { - throw_ub_format!("pointers were too far apart for {}", intrinsic_name); + // a < b + if intrinsic_name == sym::ptr_offset_from_unsigned { + throw_ub_format!( + "`{}` called when first pointer has smaller offset than second: {} < {}", + intrinsic_name, + a_offset, + b_offset, + ); + } + // The signed form of the intrinsic allows this. If we interpret the + // difference as isize, we'll get the proper signed difference. If that + // seems *positive*, they were more than isize::MAX apart. + let dist = val.to_machine_isize(self)?; + if dist >= 0 { + throw_ub_format!( + "`{}` called when first pointer is too far before second", + intrinsic_name + ); + } + dist + } else { + // b >= a + let dist = val.to_machine_isize(self)?; + // If converting to isize produced a *negative* result, we had an overflow + // because they were more than isize::MAX apart. + if dist < 0 { + throw_ub_format!( + "`{}` called when first pointer is too far ahead of second", + intrinsic_name + ); + } + dist } - val.to_machine_isize(self)? }; // Check that the range between them is dereferenceable ("in-bounds or one past the // end of the same allocation"). This is like the check in ptr_offset_inbounds. - let min_ptr = if distance >= 0 { b } else { a }; + let min_ptr = if dist >= 0 { b } else { a }; self.check_ptr_access_align( min_ptr, - Size::from_bytes(distance.unsigned_abs()), + Size::from_bytes(dist.unsigned_abs()), Align::ONE, CheckInAllocMsg::OffsetFromTest, )?; - if intrinsic_name == sym::ptr_offset_from_unsigned && distance < 0 { - throw_ub_format!( - "{} called when first pointer has smaller offset than second: {} < {}", - intrinsic_name, - a_offset, - b_offset, - ); - } - // Perform division by size to compute return value. let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned { + assert!(0 <= dist && dist <= self.machine_isize_max()); usize_layout } else { + assert!(self.machine_isize_min() <= dist && dist <= self.machine_isize_max()); isize_layout }; let pointee_layout = self.layout_of(substs.type_at(0))?; // If ret_layout is unsigned, we checked that so is the distance, so we are good. - let val = ImmTy::from_int(distance, ret_layout); + let val = ImmTy::from_int(dist, ret_layout); let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout); self.exact_div(&val, &size, dest)?; } @@ -413,35 +435,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ), )?; } - if intrinsic_name == sym::assert_zero_valid - && !layout.might_permit_raw_init( - self, - InitKind::Zero, - self.tcx.sess.opts.debugging_opts.strict_init_checks, - ) - { - M::abort( - self, - format!( - "aborted execution: attempted to zero-initialize type `{}`, which is invalid", - ty - ), - )?; + + if intrinsic_name == sym::assert_zero_valid { + let should_panic = !self.tcx.permits_zero_init(layout); + + if should_panic { + M::abort( + self, + format!( + "aborted execution: attempted to zero-initialize type `{}`, which is invalid", + ty + ), + )?; + } } - if intrinsic_name == sym::assert_uninit_valid - && !layout.might_permit_raw_init( - self, - InitKind::Uninit, - self.tcx.sess.opts.debugging_opts.strict_init_checks, - ) - { - M::abort( - self, - format!( - "aborted execution: attempted to leave type `{}` uninitialized, which is invalid", - ty - ), - )?; + + if intrinsic_name == sym::assert_uninit_valid { + let should_panic = !self.tcx.permits_uninit_init(layout); + + if should_panic { + M::abort( + self, + format!( + "aborted execution: attempted to leave type `{}` uninitialized, which is invalid", + ty + ), + )?; + } } } sym::simd_insert => { @@ -459,8 +479,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { for i in 0..dest_len { let place = self.mplace_index(&dest, i)?; - let value = - if i == index { *elem } else { self.mplace_index(&input, i)?.into() }; + let value = if i == index { + elem.clone() + } else { + self.mplace_index(&input, i)?.into() + }; self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?; } } @@ -493,6 +516,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = self.raw_eq_intrinsic(&args[0], &args[1])?; self.write_scalar(result, dest)?; } + + sym::vtable_size => { + let ptr = self.read_pointer(&args[0])?; + let (size, _align) = self.get_vtable_size_and_align(ptr)?; + self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?; + } + sym::vtable_align => { + let ptr = self.read_pointer(&args[0])?; + let (_size, align) = self.get_vtable_size_and_align(ptr)?; + self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?; + } + _ => return Ok(false), } @@ -503,9 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn exact_div( &mut self, - a: &ImmTy<'tcx, M::PointerTag>, - b: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + a: &ImmTy<'tcx, M::Provenance>, + b: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { // Performs an exact division, resulting in undefined behavior where // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. @@ -522,9 +557,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn saturating_arith( &self, mir_op: BinOp, - l: &ImmTy<'tcx, M::PointerTag>, - r: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + l: &ImmTy<'tcx, M::Provenance>, + r: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { assert!(matches!(mir_op, BinOp::Add | BinOp::Sub)); let (val, overflowed, _ty) = self.overflowing_binary_op(mir_op, l, r)?; Ok(if overflowed { @@ -567,10 +602,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value. pub fn ptr_offset_inbounds( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, pointee_ty: Ty<'tcx>, offset_count: i64, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); // The computed offset, in bytes, must not overflow an isize. @@ -598,9 +633,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`. pub(crate) fn copy_intrinsic( &mut self, - src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, + src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, nonoverlapping: bool, ) -> InterpResult<'tcx> { let count = self.read_scalar(&count)?.to_machine_usize(self)?; @@ -623,9 +658,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn write_bytes_intrinsic( &mut self, - dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, + dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, ) -> InterpResult<'tcx> { let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?; @@ -646,9 +681,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn raw_eq_intrinsic( &mut self, - lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>, - ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { + lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; assert!(!layout.is_unsized()); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index 23ae2db6438..5864b921552 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -79,8 +79,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { filename: Symbol, line: u32, col: u32, - ) -> MPlaceTy<'tcx, M::PointerTag> { - let loc_details = &self.tcx.sess.opts.debugging_opts.location_detail; + ) -> MPlaceTy<'tcx, M::Provenance> { + let loc_details = &self.tcx.sess.opts.unstable_opts.location_detail; let file = if loc_details.file { self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not) } else { @@ -123,7 +123,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } - pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> { + pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> { let (file, line, column) = self.location_triple_for_span(span); self.alloc_caller_location(file, line, column) } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 7f8eea94aee..71ccd1799fa 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -85,11 +85,11 @@ pub trait Machine<'mir, 'tcx>: Sized { type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static; /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. - type PointerTag: Provenance + Eq + Hash + 'static; + type Provenance: Provenance + Eq + Hash + 'static; - /// When getting the AllocId of a pointer, some extra data is also obtained from the tag + /// When getting the AllocId of a pointer, some extra data is also obtained from the provenance /// that is passed to memory access hooks so they can do things with it. - type TagExtra: Copy + 'static; + type ProvenanceExtra: Copy + 'static; /// Machines can define extra (non-instance) things that represent values of function pointers. /// For example, Miri uses this to return a function pointer from `dlsym` @@ -105,7 +105,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Memory's allocation map type MemoryMap: AllocMap< AllocId, - (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>), + (MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra>), > + Default + Clone; @@ -113,7 +113,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// or None if such memory should not be mutated and thus any such attempt will cause /// a `ModifiedStatic` error to be raised. /// Statics are copied under two circumstances: When they are mutated, and when - /// `tag_allocation` (see below) returns an owned allocation + /// `adjust_allocation` (see below) returns an owned allocation /// that is added to the memory so that the work is not done twice. const GLOBAL_KIND: Option<Self::MemoryKind>; @@ -126,7 +126,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether, when checking alignment, we should `force_int` and thus support /// custom alignment logic based on whatever the integer address happens to be. /// - /// Requires PointerTag::OFFSET_IS_ADDR to be true. + /// Requires Provenance::OFFSET_IS_ADDR to be true. fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; /// Whether to enforce the validity invariant @@ -135,9 +135,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce integers and floats being initialized. fn enforce_number_init(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Whether to enforce integers and floats not having provenance. - fn enforce_number_no_provenance(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Whether function calls should be [ABI](CallAbi)-checked. fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { true @@ -170,8 +167,8 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, abi: CallAbi, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>; @@ -182,8 +179,8 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, abi: CallAbi, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx>; @@ -193,8 +190,8 @@ pub trait Machine<'mir, 'tcx>: Sized { fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Self::PointerTag>], - destination: &PlaceTy<'tcx, Self::PointerTag>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option<mir::BasicBlock>, unwind: StackPopUnwind, ) -> InterpResult<'tcx>; @@ -217,18 +214,18 @@ pub trait Machine<'mir, 'tcx>: Sized { fn binary_ptr_op( ecx: &InterpCx<'mir, 'tcx, Self>, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Self::PointerTag>, - right: &ImmTy<'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>; + left: &ImmTy<'tcx, Self::Provenance>, + right: &ImmTy<'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)>; /// Called to read the specified `local` from the `frame`. /// Since reading a ZST is not actually accessing memory or locals, this is never invoked /// for ZST reads. #[inline] fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, local: mir::Local, - ) -> InterpResult<'tcx, &'a Operand<Self::PointerTag>> + ) -> InterpResult<'tcx, &'a Operand<Self::Provenance>> where 'tcx: 'mir, { @@ -243,7 +240,7 @@ pub trait Machine<'mir, 'tcx>: Sized { ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: mir::Local, - ) -> InterpResult<'tcx, &'a mut Operand<Self::PointerTag>> + ) -> InterpResult<'tcx, &'a mut Operand<Self::Provenance>> where 'tcx: 'mir, { @@ -275,7 +272,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn thread_local_static_base_pointer( _ecx: &mut InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<Self::Provenance>> { throw_unsup!(ThreadLocalStatic(def_id)) } @@ -283,35 +280,28 @@ pub trait Machine<'mir, 'tcx>: Sized { fn extern_static_base_pointer( ecx: &InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer<Self::PointerTag>>; + ) -> InterpResult<'tcx, Pointer<Self::Provenance>>; /// Return a "base" pointer for the given allocation: the one that is used for direct /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. /// /// Not called on `extern` or thread-local statics (those use the methods above). - fn tag_alloc_base_pointer( + fn adjust_alloc_base_pointer( ecx: &InterpCx<'mir, 'tcx, Self>, ptr: Pointer, - ) -> Pointer<Self::PointerTag>; + ) -> Pointer<Self::Provenance>; /// "Int-to-pointer cast" fn ptr_from_addr_cast( ecx: &InterpCx<'mir, 'tcx, Self>, addr: u64, - ) -> InterpResult<'tcx, Pointer<Option<Self::PointerTag>>>; - - /// Hook for returning a pointer from a transmute-like operation on an addr. - /// This is only needed to support Miri's (unsound) "allow-ptr-int-transmute" flag. - fn ptr_from_addr_transmute( - ecx: &InterpCx<'mir, 'tcx, Self>, - addr: u64, - ) -> Pointer<Option<Self::PointerTag>>; + ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>; /// Marks a pointer as exposed, allowing it's provenance /// to be recovered. "Pointer-to-int cast" fn expose_ptr( ecx: &mut InterpCx<'mir, 'tcx, Self>, - ptr: Pointer<Self::PointerTag>, + ptr: Pointer<Self::Provenance>, ) -> InterpResult<'tcx>; /// Convert a pointer with provenance into an allocation-offset pair @@ -322,30 +312,30 @@ pub trait Machine<'mir, 'tcx>: Sized { /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( ecx: &InterpCx<'mir, 'tcx, Self>, - ptr: Pointer<Self::PointerTag>, - ) -> Option<(AllocId, Size, Self::TagExtra)>; - - /// Called to initialize the "extra" state of an allocation and make the pointers - /// it contains (in relocations) tagged. The way we construct allocations is - /// to always first construct it without extra and then add the extra. - /// This keeps uniform code paths for handling both allocations created by CTFE - /// for globals, and allocations created by Miri during evaluation. + ptr: Pointer<Self::Provenance>, + ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>; + + /// Called to adjust allocations to the Provenance and AllocExtra of this machine. + /// + /// The way we construct allocations is to always first construct it without extra and then add + /// the extra. This keeps uniform code paths for handling both allocations created by CTFE for + /// globals, and allocations created by Miri during evaluation. /// - /// `kind` is the kind of the allocation being tagged; it can be `None` when + /// `kind` is the kind of the allocation being adjusted; it can be `None` when /// it's a global and `GLOBAL_KIND` is `None`. /// /// This should avoid copying if no work has to be done! If this returns an owned - /// allocation (because a copy had to be done to add tags or metadata), machine memory will + /// allocation (because a copy had to be done to adjust things), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) /// /// This must only fail if `alloc` contains relocations. - fn init_allocation_extra<'b>( + fn adjust_allocation<'b>( ecx: &InterpCx<'mir, 'tcx, Self>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option<MemoryKind<Self::MemoryKind>>, - ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>>; + ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>; /// Hook for performing extra checks on a memory read access. /// @@ -357,7 +347,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &Self, _alloc_extra: &Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -369,7 +359,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -381,7 +371,7 @@ pub trait Machine<'mir, 'tcx>: Sized { _tcx: TyCtxt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, - _tag: (AllocId, Self::TagExtra), + _prov: (AllocId, Self::ProvenanceExtra), _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) @@ -392,7 +382,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn retag( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _kind: mir::RetagKind, - _place: &PlaceTy<'tcx, Self::PointerTag>, + _place: &PlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { Ok(()) } @@ -400,18 +390,18 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Called immediately before a new stack frame gets pushed. fn init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Self::PointerTag>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + frame: Frame<'mir, 'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; /// Borrow the current thread's stack. fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>]; + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]; /// Mutably borrow the current thread's stack. fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>; + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; /// Called immediately after a stack frame got pushed and its locals got initialized. fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { @@ -422,7 +412,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// The `locals` have already been destroyed! fn after_stack_pop( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + _frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics @@ -434,8 +424,8 @@ pub trait Machine<'mir, 'tcx>: Sized { // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines // (CTFE and ConstProp) use the same instance. Here, we share that code. pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { - type PointerTag = AllocId; - type TagExtra = (); + type Provenance = AllocId; + type ProvenanceExtra = (); type ExtraFnVal = !; @@ -470,11 +460,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn enforce_number_no_provenance(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { - true - } - - #[inline(always)] fn checked_binop_checks_overflow(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { true } @@ -485,7 +470,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn_val: !, _abi: CallAbi, _args: &[OpTy<$tcx>], - _destination: &PlaceTy<$tcx, Self::PointerTag>, + _destination: &PlaceTy<$tcx, Self::Provenance>, _target: Option<mir::BasicBlock>, _unwind: StackPopUnwind, ) -> InterpResult<$tcx> { @@ -493,13 +478,12 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn init_allocation_extra<'b>( + fn adjust_allocation<'b>( _ecx: &InterpCx<$mir, $tcx, Self>, _id: AllocId, alloc: Cow<'b, Allocation>, _kind: Option<MemoryKind<Self::MemoryKind>>, - ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::PointerTag>>> { - // We do not use a tag so we can just cheaply forward the allocation + ) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> { Ok(alloc) } @@ -512,7 +496,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn tag_alloc_base_pointer( + fn adjust_alloc_base_pointer( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>, ) -> Pointer<AllocId> { @@ -520,14 +504,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn ptr_from_addr_transmute( - _ecx: &InterpCx<$mir, $tcx, Self>, - addr: u64, - ) -> Pointer<Option<AllocId>> { - Pointer::from_addr(addr) - } - - #[inline(always)] fn ptr_from_addr_cast( _ecx: &InterpCx<$mir, $tcx, Self>, addr: u64, @@ -541,7 +517,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn ptr_get_alloc( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>, - ) -> Option<(AllocId, Size, Self::TagExtra)> { + ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); Some((alloc_id, offset, ())) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 509fe576893..ed2c4edf9dd 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -9,14 +9,13 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::collections::VecDeque; -use std::convert::TryFrom; use std::fmt; use std::ptr; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::display_allocation; -use rustc_middle::ty::{Instance, ParamEnv, TyCtxt}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ @@ -62,6 +61,8 @@ pub enum AllocKind { LiveData, /// A function allocation (that fn ptrs point to). Function, + /// A (symbolic) vtable allocation. + VTable, /// A dead allocation. Dead, } @@ -112,16 +113,16 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. #[derive(Copy, Clone)] -pub struct AllocRef<'a, 'tcx, Tag, Extra> { - alloc: &'a Allocation<Tag, Extra>, +pub struct AllocRef<'a, 'tcx, Prov, Extra> { + alloc: &'a Allocation<Prov, Extra>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, } /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. -pub struct AllocRefMut<'a, 'tcx, Tag, Extra> { - alloc: &'a mut Allocation<Tag, Extra>, +pub struct AllocRefMut<'a, 'tcx, Prov, Extra> { + alloc: &'a mut Allocation<Prov, Extra>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, @@ -156,10 +157,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn global_base_pointer( &self, ptr: Pointer<AllocId>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let alloc_id = ptr.provenance; // We need to handle `extern static`. - match self.tcx.get_global_alloc(alloc_id) { + match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => { bug!("global memory cannot point to thread-local static") } @@ -168,14 +169,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => {} } - // And we need to get the tag. - Ok(M::tag_alloc_base_pointer(self, ptr)) + // And we need to get the provenance. + Ok(M::adjust_alloc_base_pointer(self, ptr)) } pub fn create_fn_alloc_ptr( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, - ) -> Pointer<M::PointerTag> { + ) -> Pointer<M::Provenance> { let id = match fn_val { FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance), FnVal::Other(extra) => { @@ -196,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { size: Size, align: Align, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?; // We can `unwrap` since `alloc` contains no pointers. Ok(self.allocate_raw_ptr(alloc, kind).unwrap()) @@ -208,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { align: Align, kind: MemoryKind<M::MemoryKind>, mutability: Mutability, - ) -> Pointer<M::PointerTag> { + ) -> Pointer<M::Provenance> { let alloc = Allocation::from_bytes(bytes, align, mutability); // We can `unwrap` since `alloc` contains no pointers. self.allocate_raw_ptr(alloc, kind).unwrap() @@ -219,27 +220,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, alloc: Allocation, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { let id = self.tcx.reserve_alloc_id(); debug_assert_ne!( Some(kind), M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - let alloc = M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind))?; + let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?; self.memory.alloc_map.insert(id, (kind, alloc.into_owned())); - Ok(M::tag_alloc_base_pointer(self, Pointer::from(id))) + Ok(M::adjust_alloc_base_pointer(self, Pointer::from(id))) } pub fn reallocate_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, old_size_and_align: Option<(Size, Align)>, new_size: Size, new_align: Align, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, Pointer<M::PointerTag>> { - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + ) -> InterpResult<'tcx, Pointer<M::Provenance>> { + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub_format!( "reallocating {:?} which does not point to the beginning of an object", @@ -271,11 +272,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[instrument(skip(self), level = "debug")] pub fn deallocate_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, old_size_and_align: Option<(Size, Align)>, kind: MemoryKind<M::MemoryKind>, ) -> InterpResult<'tcx> { - let (alloc_id, offset, tag) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr)?; trace!("deallocating: {alloc_id:?}"); if offset.bytes() != 0 { @@ -287,10 +288,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else { // Deallocating global memory -- always an error - return Err(match self.tcx.get_global_alloc(alloc_id) { + return Err(match self.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Function(..)) => { err_ub_format!("deallocating {alloc_id:?}, which is a function") } + Some(GlobalAlloc::VTable(..)) => { + err_ub_format!("deallocating {alloc_id:?}, which is a vtable") + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_format!("deallocating {alloc_id:?}, which is static memory") } @@ -327,7 +331,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { *self.tcx, &mut self.machine, &mut alloc.extra, - (alloc_id, tag), + (alloc_id, prov), alloc_range(Size::ZERO, size), )?; @@ -344,19 +348,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] fn get_ptr_access( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<(AllocId, Size, M::TagExtra)>> { + ) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> { let align = M::enforce_alignment(&self).then_some(align); self.check_and_deref_ptr( ptr, size, align, CheckInAllocMsg::MemoryAccessTest, - |alloc_id, offset, tag| { + |alloc_id, offset, prov| { let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; - Ok((size, align, (alloc_id, offset, tag))) + Ok((size, align, (alloc_id, offset, prov))) }, ) } @@ -367,7 +371,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn check_ptr_access_align( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, msg: CheckInAllocMsg, @@ -385,11 +389,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned. fn check_and_deref_ptr<T>( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Option<Align>, msg: CheckInAllocMsg, - alloc_size: impl FnOnce(AllocId, Size, M::TagExtra) -> InterpResult<'tcx, (Size, Align, T)>, + alloc_size: impl FnOnce( + AllocId, + Size, + M::ProvenanceExtra, + ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option<T>> { fn check_offset_align<'tcx>(offset: u64, align: Align) -> InterpResult<'tcx> { if offset % align.bytes() == 0 { @@ -417,8 +425,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } None } - Ok((alloc_id, offset, tag)) => { - let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, tag)?; + Ok((alloc_id, offset, prov)) => { + let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; // Test bounds. This also ensures non-null. // It is sufficient to check this for the end pointer. Also check for overflow! if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { @@ -431,7 +439,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }) } // Ensure we never consider the null pointer dereferencable. - if M::PointerTag::OFFSET_IS_ADDR { + if M::Provenance::OFFSET_IS_ADDR { assert_ne!(ptr.addr(), Size::ZERO); } // Test align. Check this last; if both bounds and alignment are violated @@ -462,19 +470,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Helper function to obtain a global (tcx) allocation. /// This attempts to return a reference to an existing allocation if /// one can be found in `tcx`. That, however, is only possible if `tcx` and - /// this machine use the same pointer tag, so it is indirected through - /// `M::tag_allocation`. + /// this machine use the same pointer provenance, so it is indirected through + /// `M::adjust_allocation`. fn get_global_alloc( &self, id: AllocId, is_write: bool, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> { - let (alloc, def_id) = match self.tcx.get_global_alloc(id) { + ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> { + let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Memory(mem)) => { // Memory of a constant or promoted or anonymous memory referenced by a static. (mem, None) } Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), + Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -490,6 +499,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // contains a reference to memory that was created during its evaluation (i.e., not // to another static), those inner references only exist in "resolved" form. if self.tcx.is_foreign_item(def_id) { + // This is unreachable in Miri, but can happen in CTFE where we actually *do* support + // referencing arbitrary (declared) extern statics. throw_unsup!(ReadExternStatic(def_id)); } @@ -499,7 +510,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?; // We got tcx memory. Let the machine initialize its "extra" stuff. - M::init_allocation_extra( + M::adjust_allocation( self, id, // always use the ID we got as input, not the "hidden" one. Cow::Borrowed(alloc.inner()), @@ -512,11 +523,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw( &self, id: AllocId, - ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> { + ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> { // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from // `get_global_alloc` that we can actually use directly without inserting anything anywhere. - // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`. + // So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`. let a = self.memory.alloc_map.get_or(id, || { let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?; match alloc { @@ -545,24 +556,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get_ptr_alloc<'a>( &'a self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::PointerTag, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra>>> { let align = M::enforce_alignment(self).then_some(align); let ptr_and_alloc = self.check_and_deref_ptr( ptr, size, align, CheckInAllocMsg::MemoryAccessTest, - |alloc_id, offset, tag| { + |alloc_id, offset, prov| { let alloc = self.get_alloc_raw(alloc_id)?; - Ok((alloc.size(), alloc.align, (alloc_id, offset, tag, alloc))) + Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; - if let Some((alloc_id, offset, tag, alloc)) = ptr_and_alloc { + if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); - M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, tag), range)?; + M::memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?; Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) } else { // Even in this branch we have to be sure that we actually access the allocation, in @@ -586,7 +597,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw_mut( &mut self, id: AllocId, - ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M)> { + ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra>, &mut M)> { // We have "NLL problem case #3" here, which cannot be worked around without loss of // efficiency even for the common case where the key is in the map. // <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions> @@ -612,18 +623,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get_ptr_alloc_mut<'a>( &'a mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra>>> { let parts = self.get_ptr_access(ptr, size, align)?; - if let Some((alloc_id, offset, tag)) = parts { + if let Some((alloc_id, offset, prov)) = parts { let tcx = *self.tcx; // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?; let range = alloc_range(offset, size); - M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, tag), range)?; + M::memory_written(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id })) } else { Ok(None) @@ -659,12 +670,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // # Statics // Can't do this in the match argument, we may get cycle errors since the lock would // be held throughout the match. - match self.tcx.get_global_alloc(id) { - Some(GlobalAlloc::Static(did)) => { - assert!(!self.tcx.is_thread_local_static(did)); + match self.tcx.try_get_global_alloc(id) { + Some(GlobalAlloc::Static(def_id)) => { + assert!(self.tcx.is_static(def_id)); + assert!(!self.tcx.is_thread_local_static(def_id)); // Use size and align of the type. - let ty = self.tcx.type_of(did); + let ty = self.tcx.type_of(def_id); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); + assert!(!layout.is_unsized()); (layout.size, layout.align.abi, AllocKind::LiveData) } Some(GlobalAlloc::Memory(alloc)) => { @@ -674,6 +687,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (alloc.size(), alloc.align, AllocKind::LiveData) } Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"), + Some(GlobalAlloc::VTable(..)) => { + // No data to be accessed here. But vtables are pointer-aligned. + return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable); + } // The rest must be dead. None => { // Deallocated pointers are allowed, we should be able to find @@ -701,7 +718,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) { Some(FnVal::Other(*extra)) } else { - match self.tcx.get_global_alloc(id) { + match self.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)), _ => None, } @@ -710,10 +727,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn get_ptr_fn( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - trace!("get_fn({:?})", ptr); - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + trace!("get_ptr_fn({:?})", ptr); + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } @@ -721,6 +738,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) } + pub fn get_ptr_vtable( + &self, + ptr: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)> { + trace!("get_ptr_vtable({:?})", ptr); + let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + if offset.bytes() != 0 { + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + } + match self.tcx.try_get_global_alloc(alloc_id) { + Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)), + _ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))), + } + } + pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not; Ok(()) @@ -759,7 +791,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This is a new allocation, add its relocations to `todo`. if let Some((_, alloc)) = self.memory.alloc_map.get(id) { todo.extend( - alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()), + alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()), ); } } @@ -788,14 +820,14 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // Cannot be a closure because it is generic in `Tag`, `Extra`. - fn write_allocation_track_relocs<'tcx, Tag: Provenance, Extra>( + // Cannot be a closure because it is generic in `Prov`, `Extra`. + fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra>( fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'tcx>, allocs_to_print: &mut VecDeque<AllocId>, - alloc: &Allocation<Tag, Extra>, + alloc: &Allocation<Prov, Extra>, ) -> std::fmt::Result { - for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) { + for alloc_id in alloc.relocations().values().filter_map(|prov| prov.get_alloc_id()) { allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", display_allocation(tcx, alloc)) @@ -825,7 +857,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, } None => { // global alloc - match self.ecx.tcx.get_global_alloc(id) { + match self.ecx.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Memory(alloc)) => { write!(fmt, " (unchanged global, ")?; write_allocation_track_relocs( @@ -836,7 +868,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, )?; } Some(GlobalAlloc::Function(func)) => { - write!(fmt, " (fn: {})", func)?; + write!(fmt, " (fn: {func})")?; + } + Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { + write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; + } + Some(GlobalAlloc::VTable(ty, None)) => { + write!(fmt, " (vtable: impl <auto trait> for {ty})")?; } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; @@ -854,12 +892,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, } /// Reading and writing. -impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> { /// `range` is relative to this allocation reference, not the base of the allocation. pub fn write_scalar( &mut self, range: AllocRange, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> InterpResult<'tcx> { let range = self.range.subrange(range); debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id); @@ -873,7 +911,7 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { pub fn write_ptr_sized( &mut self, offset: Size, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> InterpResult<'tcx> { self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val) } @@ -887,13 +925,13 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> { } } -impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> { /// `range` is relative to this allocation reference, not the base of the allocation. pub fn read_scalar( &self, range: AllocRange, read_provenance: bool, - ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + ) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { let range = self.range.subrange(range); let res = self .alloc @@ -904,12 +942,12 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> { } /// `range` is relative to this allocation reference, not the base of the allocation. - pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { self.read_scalar(range, /*read_provenance*/ false) } /// `offset` is relative to this allocation reference, not the base of the allocation. - pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> { + pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> { self.read_scalar( alloc_range(offset, self.tcx.data_layout().pointer_size), /*read_provenance*/ true, @@ -941,7 +979,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Performs appropriate bounds checks. pub fn read_bytes_ptr( &self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, size: Size, ) -> InterpResult<'tcx, &[u8]> { let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else { @@ -961,7 +999,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Performs appropriate bounds checks. pub fn write_bytes_ptr( &mut self, - ptr: Pointer<Option<M::PointerTag>>, + ptr: Pointer<Option<M::Provenance>>, src: impl IntoIterator<Item = u8>, ) -> InterpResult<'tcx> { let mut src = src.into_iter(); @@ -998,9 +1036,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn mem_copy( &mut self, - src: Pointer<Option<M::PointerTag>>, + src: Pointer<Option<M::Provenance>>, src_align: Align, - dest: Pointer<Option<M::PointerTag>>, + dest: Pointer<Option<M::Provenance>>, dest_align: Align, size: Size, nonoverlapping: bool, @@ -1010,9 +1048,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn mem_copy_repeatedly( &mut self, - src: Pointer<Option<M::PointerTag>>, + src: Pointer<Option<M::Provenance>>, src_align: Align, - dest: Pointer<Option<M::PointerTag>>, + dest: Pointer<Option<M::Provenance>>, dest_align: Align, size: Size, num_copies: u64, @@ -1027,16 +1065,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // and once below to get the underlying `&[mut] Allocation`. // Source alloc preparations and access hooks. - let Some((src_alloc_id, src_offset, src_tag)) = src_parts else { + let Some((src_alloc_id, src_offset, src_prov)) = src_parts else { // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do. return Ok(()); }; let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); - M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_tag), src_range)?; + M::memory_read(*tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_prov), src_range)?; // We need the `dest` ptr for the next operation, so we get it now. // We already did the source checks and called the hooks so we are good to return early. - let Some((dest_alloc_id, dest_offset, dest_tag)) = dest_parts else { + let Some((dest_alloc_id, dest_offset, dest_prov)) = dest_parts else { // Zero-sized *destination*. return Ok(()); }; @@ -1062,7 +1100,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { *tcx, extra, &mut dest_alloc.extra, - (dest_alloc_id, dest_tag), + (dest_alloc_id, dest_prov), dest_range, )?; let dest_bytes = dest_alloc @@ -1133,34 +1171,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - pub fn scalar_to_ptr( - &self, - scalar: Scalar<M::PointerTag>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - // We use `to_bits_or_ptr_internal` since we are just implementing the method people need to - // call to force getting out a pointer. - Ok( - match scalar - .to_bits_or_ptr_internal(self.pointer_size()) - .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? - { - Err(ptr) => ptr.into(), - Ok(bits) => { - let addr = u64::try_from(bits).unwrap(); - M::ptr_from_addr_transmute(&self, addr) - } - }, - ) - } - /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. - pub fn scalar_may_be_null(&self, scalar: Scalar<M::PointerTag>) -> InterpResult<'tcx, bool> { + pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> { Ok(match scalar.try_to_int() { Ok(int) => int.is_null(), Err(_) => { // Can only happen during CTFE. - let ptr = self.scalar_to_ptr(scalar)?; + let ptr = scalar.to_pointer(self)?; match self.ptr_try_get_alloc_id(ptr) { Ok((alloc_id, offset, _)) => { let (size, _align, _kind) = self.get_alloc_info(alloc_id); @@ -1178,13 +1196,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// about where it points), or an absolute address. pub fn ptr_try_get_alloc_id( &self, - ptr: Pointer<Option<M::PointerTag>>, - ) -> Result<(AllocId, Size, M::TagExtra), u64> { + ptr: Pointer<Option<M::Provenance>>, + ) -> Result<(AllocId, Size, M::ProvenanceExtra), u64> { match ptr.into_pointer_or_addr() { Ok(ptr) => match M::ptr_get_alloc(self, ptr) { Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)), None => { - assert!(M::PointerTag::OFFSET_IS_ADDR); + assert!(M::Provenance::OFFSET_IS_ADDR); let (_, addr) = ptr.into_parts(); Err(addr.bytes()) } @@ -1197,8 +1215,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn ptr_get_alloc_id( &self, - ptr: Pointer<Option<M::PointerTag>>, - ) -> InterpResult<'tcx, (AllocId, Size, M::TagExtra)> { + ptr: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { self.ptr_try_get_alloc_id(ptr).map_err(|offset| { err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into() }) diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 92f0a7498e3..2e356f67bf3 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -9,6 +9,7 @@ mod memory; mod operand; mod operator; mod place; +mod projection; mod step; mod terminator; mod traits; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d11ae7b4925..de284bd3bae 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -1,11 +1,9 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. -use std::convert::TryFrom; use std::fmt::Write; use rustc_hir::def::Namespace; -use rustc_macros::HashStable; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; use rustc_middle::ty::{ConstInt, DelaySpanBugEmitted, Ty}; @@ -15,8 +13,8 @@ use rustc_target::abi::{VariantIdx, Variants}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, - InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, - PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, + Provenance, Scalar, ScalarMaybeUninit, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -26,15 +24,15 @@ use super::{ /// operations and wide pointers. This idea was taken from rustc's codegen. /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. -#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)] -pub enum Immediate<Tag: Provenance = AllocId> { +#[derive(Copy, Clone, Debug)] +pub enum Immediate<Prov: Provenance = AllocId> { /// A single scalar value (must have *initialized* `Scalar` ABI). /// FIXME: we also currently often use this for ZST. /// `ScalarMaybeUninit` should reject ZST, and we should use `Uninit` for them instead. - Scalar(ScalarMaybeUninit<Tag>), + Scalar(ScalarMaybeUninit<Prov>), /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are /// `Scalar::Initialized`). - ScalarPair(ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>), + ScalarPair(ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>), /// A value of fully uninitialized memory. Can have and size and layout. Uninit, } @@ -42,36 +40,36 @@ pub enum Immediate<Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Immediate, 56); -impl<Tag: Provenance> From<ScalarMaybeUninit<Tag>> for Immediate<Tag> { +impl<Prov: Provenance> From<ScalarMaybeUninit<Prov>> for Immediate<Prov> { #[inline(always)] - fn from(val: ScalarMaybeUninit<Tag>) -> Self { + fn from(val: ScalarMaybeUninit<Prov>) -> Self { Immediate::Scalar(val) } } -impl<Tag: Provenance> From<Scalar<Tag>> for Immediate<Tag> { +impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> { #[inline(always)] - fn from(val: Scalar<Tag>) -> Self { + fn from(val: Scalar<Prov>) -> Self { Immediate::Scalar(val.into()) } } -impl<'tcx, Tag: Provenance> Immediate<Tag> { - pub fn from_pointer(p: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { +impl<'tcx, Prov: Provenance> Immediate<Prov> { + pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx)) } - pub fn from_maybe_pointer(p: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { Immediate::Scalar(ScalarMaybeUninit::from_maybe_pointer(p, cx)) } - pub fn new_slice(val: Scalar<Tag>, len: u64, cx: &impl HasDataLayout) -> Self { + pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self { Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) } pub fn new_dyn_trait( - val: Scalar<Tag>, - vtable: Pointer<Option<Tag>>, + val: Scalar<Prov>, + vtable: Pointer<Option<Prov>>, cx: &impl HasDataLayout, ) -> Self { Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx)) @@ -79,7 +77,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> { + pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Prov> { match self { Immediate::Scalar(val) => val, Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), @@ -89,13 +87,13 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> { + pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Prov>> { self.to_scalar_or_uninit().check_init() } #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) { + pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>) { match self { Immediate::ScalarPair(val1, val2) => (val1, val2), Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"), @@ -105,7 +103,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> { + pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Prov>, Scalar<Prov>)> { let (val1, val2) = self.to_scalar_or_uninit_pair(); Ok((val1.check_init()?, val2.check_init()?)) } @@ -113,21 +111,21 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { // ScalarPair needs a type to interpret, so we often have an immediate and a type together // as input for binary and cast operations. -#[derive(Copy, Clone, Debug)] -pub struct ImmTy<'tcx, Tag: Provenance = AllocId> { - imm: Immediate<Tag>, +#[derive(Clone, Debug)] +pub struct ImmTy<'tcx, Prov: Provenance = AllocId> { + imm: Immediate<Prov>, pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); -impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> { +impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Helper function for printing a scalar to a FmtPrinter - fn p<'a, 'tcx, Tag: Provenance>( + fn p<'a, 'tcx, Prov: Provenance>( cx: FmtPrinter<'a, 'tcx>, - s: ScalarMaybeUninit<Tag>, + s: ScalarMaybeUninit<Prov>, ty: Ty<'tcx>, ) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> { match s { @@ -172,10 +170,10 @@ impl<Tag: Provenance> std::fmt::Display for ImmTy<'_, Tag> { } } -impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> { - type Target = Immediate<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> { + type Target = Immediate<Prov>; #[inline(always)] - fn deref(&self) -> &Immediate<Tag> { + fn deref(&self) -> &Immediate<Prov> { &self.imm } } @@ -183,15 +181,18 @@ impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> { /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. -#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)] -pub enum Operand<Tag: Provenance = AllocId> { - Immediate(Immediate<Tag>), - Indirect(MemPlace<Tag>), +#[derive(Copy, Clone, Debug)] +pub enum Operand<Prov: Provenance = AllocId> { + Immediate(Immediate<Prov>), + Indirect(MemPlace<Prov>), } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct OpTy<'tcx, Tag: Provenance = AllocId> { - op: Operand<Tag>, // Keep this private; it helps enforce invariants. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(Operand, 64); + +#[derive(Clone, Debug)] +pub struct OpTy<'tcx, Prov: Provenance = AllocId> { + op: Operand<Prov>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: /// it needs to have a different alignment than the field type would usually have. @@ -206,54 +207,59 @@ pub struct OpTy<'tcx, Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(OpTy<'_>, 88); -impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> { - type Target = Operand<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for OpTy<'tcx, Prov> { + type Target = Operand<Prov>; #[inline(always)] - fn deref(&self) -> &Operand<Tag> { + fn deref(&self) -> &Operand<Prov> { &self.op } } -impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } } } -impl<'tcx, Tag: Provenance> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] - fn from(val: ImmTy<'tcx, Tag>) -> Self { + fn from(val: ImmTy<'tcx, Prov>) -> Self { OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } } } -impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] - pub fn from_scalar(val: Scalar<Tag>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm: val.into(), layout } } #[inline] - pub fn from_immediate(imm: Immediate<Tag>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm, layout } } #[inline] + pub fn uninit(layout: TyAndLayout<'tcx>) -> Self { + ImmTy { imm: Immediate::Uninit, layout } + } + + #[inline] pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> { Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) } @@ -280,6 +286,51 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { } } +impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { + pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { + if self.layout.is_unsized() { + // There are no unsized immediates. + self.assert_mem_place().len(cx) + } else { + match self.layout.fields { + abi::FieldsShape::Array { count, .. } => Ok(count), + _ => bug!("len not supported on sized type {:?}", self.layout.ty), + } + } + } + + pub fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta<Prov>, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + match self.try_as_mplace() { + Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), + Err(imm) => { + assert!( + matches!(*imm, Immediate::Uninit), + "Scalar/ScalarPair cannot be offset into" + ); + assert!(!meta.has_meta()); // no place to store metadata here + // Every part of an uninit is uninit. + Ok(ImmTy::uninit(layout).into()) + } + } + } + + pub fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(!layout.is_unsized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } +} + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. @@ -287,20 +338,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This is an internal function; call `read_immediate` instead. fn read_immediate_from_mplace_raw( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, + mplace: &MPlaceTy<'tcx, M::Provenance>, force: bool, - ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> { if mplace.layout.is_unsized() { // Don't touch unsized return Ok(None); } let Some(alloc) = self.get_place_alloc(mplace)? else { - return Ok(Some(ImmTy { - // zero-sized type can be left uninit - imm: Immediate::Uninit, - layout: mplace.layout, - })); + // zero-sized type can be left uninit + return Ok(Some(ImmTy::uninit(mplace.layout))); }; // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. @@ -315,17 +363,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Abi::Scalar(s) if force => Some(s.primitive()), _ => None, }; - let read_provenance = |s: abi::Primitive, size| { - // Should be just `s.is_ptr()`, but we support a Miri flag that accepts more - // questionable ptr-int transmutes. - let number_may_have_provenance = !M::enforce_number_no_provenance(self); - s.is_ptr() || (number_may_have_provenance && size == self.pointer_size()) - }; if let Some(s) = scalar_layout { let size = s.size(self); assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); - let scalar = - alloc.read_scalar(alloc_range(Size::ZERO, size), read_provenance(s, size))?; + let scalar = alloc + .read_scalar(alloc_range(Size::ZERO, size), /*read_provenance*/ s.is_ptr())?; return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); } let scalar_pair_layout = match mplace.layout.abi { @@ -343,10 +385,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (a_size, b_size) = (a.size(self), b.size(self)); let b_offset = a_size.align_to(b.align(self).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields - let a_val = - alloc.read_scalar(alloc_range(Size::ZERO, a_size), read_provenance(a, a_size))?; - let b_val = - alloc.read_scalar(alloc_range(b_offset, b_size), read_provenance(b, b_size))?; + let a_val = alloc.read_scalar( + alloc_range(Size::ZERO, a_size), + /*read_provenance*/ a.is_ptr(), + )?; + let b_val = alloc + .read_scalar(alloc_range(b_offset, b_size), /*read_provenance*/ b.is_ptr())?; return Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout, @@ -367,11 +411,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This flag exists only for validity checking. /// /// This is an internal function that should not usually be used; call `read_immediate` instead. + /// ConstProp needs it, though. pub fn read_immediate_raw( &self, - src: &OpTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, force: bool, - ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> { Ok(match src.try_as_mplace() { Ok(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? { @@ -388,8 +433,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn read_immediate( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? { Ok(imm) } else { @@ -400,144 +445,49 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read a scalar from a place pub fn read_scalar( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> { Ok(self.read_immediate(op)?.to_scalar_or_uninit()) } /// Read a pointer from a place. pub fn read_pointer( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - self.scalar_to_ptr(self.read_scalar(op)?.check_init()?) + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { + self.read_scalar(op)?.to_pointer(self) } /// Turn the wide MPlace into a string (must already be dereferenced!) - pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { + pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?; let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; Ok(str) } - /// Projection functions - pub fn operand_field( - &self, - op: &OpTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let base = match op.try_as_mplace() { - Ok(ref mplace) => { - // We can reuse the mplace field computation logic for indirect operands. - let field = self.mplace_field(mplace, field)?; - return Ok(field.into()); - } - Err(value) => value, - }; - - let field_layout = base.layout.field(self, field); - let offset = base.layout.fields.offset(field); - // This makes several assumptions about what layouts we will encounter; we match what - // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). - let field_val: Immediate<_> = match (*base, base.layout.abi) { - // the field contains no information, can be left uninit - _ if field_layout.is_zst() => Immediate::Uninit, - // the field covers the entire type - _ if field_layout.size == base.layout.size => { - assert!(match (base.layout.abi, field_layout.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => true, - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, - _ => false, - }); - assert!(offset.bytes() == 0); - *base - } - // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert!(matches!(field_layout.abi, Abi::Scalar(..))); - Immediate::from(if offset.bytes() == 0 { - debug_assert_eq!(field_layout.size, a.size(self)); - a_val - } else { - debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); - debug_assert_eq!(field_layout.size, b.size(self)); - b_val - }) - } - _ => span_bug!( - self.cur_span(), - "invalid field access on immediate {}, layout {:#?}", - base, - base.layout - ), - }; - - Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None }) - } - - pub fn operand_index( - &self, - op: &OpTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - if let Ok(index) = usize::try_from(index) { - // We can just treat this as a field. - self.operand_field(op, index) - } else { - // Indexing into a big array. This must be an mplace. - let mplace = op.assert_mem_place(); - Ok(self.mplace_index(&mplace, index)?.into()) - } - } - - pub fn operand_downcast( - &self, - op: &OpTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - Ok(match op.try_as_mplace() { - Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(), - Err(..) => { - // Downcasts only change the layout. - // (In particular, no check about whether this is even the active variant -- that's by design, - // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - let layout = op.layout.for_variant(self, variant); - OpTy { layout, ..*op } - } - }) - } - - #[instrument(skip(self), level = "debug")] - pub fn operand_projection( - &self, - base: &OpTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.operand_field(base, field.index())?, - Downcast(_, variant) => self.operand_downcast(base, variant)?, - Deref => self.deref_operand(base)?.into(), - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - // The rest should only occur as mplace, we do not use Immediates for types - // allowing such operations. This matches place_projection forcing an allocation. - let mplace = base.assert_mem_place(); - self.mplace_projection(&mplace, proj_elem)?.into() - } - }) - } - /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements. /// Also returns the number of elements. + /// + /// Can (but does not always) trigger UB if `op` is uninitialized. pub fn operand_to_simd( &self, - base: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // This only works in memory, but repr(simd) types should never be immediates anyway. - assert!(base.layout.ty.is_simd()); - self.mplace_to_simd(&base.assert_mem_place()) + assert!(op.layout.ty.is_simd()); + match op.try_as_mplace() { + Ok(mplace) => self.mplace_to_simd(&mplace), + Err(imm) => match *imm { + Immediate::Uninit => { + throw_ub!(InvalidUninitBytes(None)) + } + Immediate::Scalar(..) | Immediate::ScalarPair(..) => { + bug!("arrays/slices can never have Scalar/ScalarPair layout") + } + }, + } } /// Read from a local. Will not actually access the local if reading from a ZST. @@ -547,10 +497,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// OpTy from a local. pub fn local_to_op( &self, - frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, + frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(frame, local, layout)?; let op = if layout.is_zst() { // Bypass `access_local` (helps in ConstProp) @@ -567,8 +517,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn place_to_op( &self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { let op = match **place { Place::Ptr(mplace) => Operand::Indirect(mplace), Place::Local { frame, local } => { @@ -582,30 +532,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// avoid allocations. pub fn eval_place_to_op( &self, - place: mir::Place<'tcx>, + mir_place: mir::Place<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Do not use the layout passed in as argument if the base we are looking at // here is not the entire place. - let layout = if place.projection.is_empty() { layout } else { None }; - - let base_op = self.local_to_op(self.frame(), place.local, layout)?; + let layout = if mir_place.projection.is_empty() { layout } else { None }; - let op = place - .projection - .iter() - .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?; + let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; + // Using `try_fold` turned out to be bad for performance, hence the loop. + for elem in mir_place.projection.iter() { + op = self.operand_projection(&op, elem)? + } trace!("eval_place_to_op: got {:?}", *op); // Sanity-check the type we ended up with. - debug_assert!(mir_assign_valid_types( - *self.tcx, - self.param_env, - self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty - )?)?, - op.layout, - )); + debug_assert!( + mir_assign_valid_types( + *self.tcx, + self.param_env, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty + )?)?, + op.layout, + ), + "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}", + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty, + op.layout.ty, + ); Ok(op) } @@ -617,7 +571,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, mir_op: &mir::Operand<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { use rustc_middle::mir::Operand::*; let op = match *mir_op { // FIXME: do some more logic on `move` to invalidate the old location @@ -642,7 +596,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(super) fn eval_operands( &self, ops: &[mir::Operand<'tcx>], - ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::PointerTag>>> { + ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::Provenance>>> { ops.iter().map(|op| self.eval_operand(op, None)).collect() } @@ -654,7 +608,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, c: ty::Const<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match c.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => { @@ -679,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, val: &mir::ConstantKind<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match val { mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout), mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout), @@ -691,9 +645,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { val_val: ConstValue<'tcx>, ty: Ty<'tcx>, layout: Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { // Other cases need layout. - let tag_scalar = |scalar| -> InterpResult<'tcx, _> { + let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { Ok(match scalar { Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), @@ -708,7 +662,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr = self.global_base_pointer(Pointer::new(id, offset))?; Operand::Indirect(MemPlace::from_ptr(ptr.into())) } - ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()), + ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()), ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit), ConstValue::Slice { data, start, end } => { // We rely on mutability being set correctly in `data` to prevent writes @@ -731,8 +685,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)! pub fn read_discriminant( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> { trace!("read_discriminant_value {:#?}", op.layout); // Get type and layout of the discriminant. let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index f0c113376ea..f9912d706fb 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -19,9 +19,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, op: mir::BinOp, force_overflow_checks: bool, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?; debug_assert_eq!( @@ -41,7 +41,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let pair = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); self.write_immediate(pair, dest)?; } else { - assert!(self.tcx.sess.opts.debugging_opts.randomize_layout); + assert!(self.tcx.sess.opts.unstable_opts.randomize_layout); // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to // do a component-wise write here. This code path is slower than the above because // `place_field` will have to `force_allocate` locals here. @@ -58,9 +58,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn binop_ignore_overflow( &mut self, op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?; assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op); @@ -74,7 +74,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, l: char, r: char, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let res = match bin_op { @@ -94,7 +94,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, l: bool, r: bool, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let res = match bin_op { @@ -112,13 +112,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (Scalar::from_bool(res), false, self.tcx.types.bool) } - fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>( + fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>( &self, bin_op: mir::BinOp, ty: Ty<'tcx>, l: F, r: F, - ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { + ) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; let (val, ty) = match bin_op { @@ -146,7 +146,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { left_layout: TyAndLayout<'tcx>, r: u128, right_layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { use rustc_middle::mir::BinOp::*; // Shift ops can have an RHS with a different numeric type. @@ -314,9 +314,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn overflowing_binary_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { trace!( "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, @@ -393,9 +393,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn binary_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, M::PointerTag>, - right: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?; Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } @@ -405,8 +405,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn overflowing_unary_op( &self, un_op: mir::UnOp, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> { use rustc_middle::mir::UnOp::*; let layout = val.layout; @@ -455,8 +455,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn unary_op( &self, un_op: mir::UnOp, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 57ecad07b42..473da71a0ab 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -2,17 +2,13 @@ //! into a place. //! All high-level functions to write to memory work on places as destinations. -use std::convert::TryFrom; use std::hash::Hash; use rustc_ast::Mutability; -use rustc_macros::HashStable; use rustc_middle::mir; +use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; -use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{ - Abi, Align, FieldsShape, HasDataLayout, Size, TagEncoding, VariantIdx, Variants, -}; +use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx}; use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, @@ -20,57 +16,68 @@ use super::{ Pointer, Provenance, Scalar, ScalarMaybeUninit, }; -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] /// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta<Tag: Provenance = AllocId> { +pub enum MemPlaceMeta<Prov: Provenance = AllocId> { /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). - Meta(Scalar<Tag>), + Meta(Scalar<Prov>), /// `Sized` types or unsized `extern type` None, - /// The address of this place may not be taken. This protects the `MemPlace` from coming from - /// a ZST Operand without a backing allocation and being converted to an integer address. This - /// should be impossible, because you can't take the address of an operand, but this is a second - /// protection layer ensuring that we don't mess up. - Poison, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); -impl<Tag: Provenance> MemPlaceMeta<Tag> { - pub fn unwrap_meta(self) -> Scalar<Tag> { +impl<Prov: Provenance> MemPlaceMeta<Prov> { + pub fn unwrap_meta(self) -> Scalar<Prov> { match self { Self::Meta(s) => s, - Self::None | Self::Poison => { + Self::None => { bug!("expected wide pointer extra data (e.g. slice length or trait object vtable)") } } } - fn has_meta(self) -> bool { + + pub fn has_meta(self) -> bool { match self { Self::Meta(_) => true, - Self::None | Self::Poison => false, + Self::None => false, } } } -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] -pub struct MemPlace<Tag: Provenance = AllocId> { - /// The pointer can be a pure integer, with the `None` tag. - pub ptr: Pointer<Option<Tag>>, +#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] +pub struct MemPlace<Prov: Provenance = AllocId> { + /// The pointer can be a pure integer, with the `None` provenance. + pub ptr: Pointer<Option<Prov>>, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types /// (e.g., `extern type`). - pub meta: MemPlaceMeta<Tag>, + pub meta: MemPlaceMeta<Prov>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlace, 40); -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] -pub enum Place<Tag: Provenance = AllocId> { +/// A MemPlace with its layout. Constructing it is only possible in this module. +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> { + mplace: MemPlace<Prov>, + pub layout: TyAndLayout<'tcx>, + /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: + /// it needs to have a different alignment than the field type would usually have. + /// So we represent this here with a separate field that "overwrites" `layout.align`. + /// This means `layout.align` should never be used for a `MPlaceTy`! + pub align: Align, +} + +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64); + +#[derive(Copy, Clone, Debug)] +pub enum Place<Prov: Provenance = AllocId> { /// A place referring to a value allocated in the `Memory` system. - Ptr(MemPlace<Tag>), + Ptr(MemPlace<Prov>), /// To support alloc-free locals, we are able to write directly to a local. /// (Without that optimization, we'd just always be a `MemPlace`.) @@ -80,9 +87,9 @@ pub enum Place<Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Place, 48); -#[derive(Copy, Clone, Debug)] -pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { - place: Place<Tag>, // Keep this private; it helps enforce invariants. +#[derive(Clone, Debug)] +pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> { + place: Place<Prov>, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: /// it needs to have a different alignment than the field type would usually have. @@ -94,100 +101,82 @@ pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PlaceTy<'_>, 72); -impl<'tcx, Tag: Provenance> std::ops::Deref for PlaceTy<'tcx, Tag> { - type Target = Place<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> { + type Target = Place<Prov>; #[inline(always)] - fn deref(&self) -> &Place<Tag> { + fn deref(&self) -> &Place<Prov> { &self.place } } -/// A MemPlace with its layout. Constructing it is only possible in this module. -#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] -pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> { - mplace: MemPlace<Tag>, - pub layout: TyAndLayout<'tcx>, - /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: - /// it needs to have a different alignment than the field type would usually have. - /// So we represent this here with a separate field that "overwrites" `layout.align`. - /// This means `layout.align` should never be used for a `MPlaceTy`! - pub align: Align, -} - -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64); - -impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { - type Target = MemPlace<Tag>; +impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> { + type Target = MemPlace<Prov>; #[inline(always)] - fn deref(&self) -> &MemPlace<Tag> { + fn deref(&self) -> &MemPlace<Prov> { &self.mplace } } -impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align } } } -impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } -impl<'tcx, Tag: Provenance> From<&'_ mut MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Tag>) -> Self { + fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } } } -impl<Tag: Provenance> MemPlace<Tag> { +impl<Prov: Provenance> MemPlace<Prov> { #[inline(always)] - pub fn from_ptr(ptr: Pointer<Option<Tag>>) -> Self { + pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self { MemPlace { ptr, meta: MemPlaceMeta::None } } /// Adjust the provenance of the main pointer (metadata is unaffected). - pub fn map_provenance(self, f: impl FnOnce(Option<Tag>) -> Option<Tag>) -> Self { + pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self { MemPlace { ptr: self.ptr.map_provenance(f), ..self } } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. /// This is the inverse of `ref_to_mplace`. #[inline(always)] - pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Tag> { + pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> { match self.meta { MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)), MemPlaceMeta::Meta(meta) => { Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into()) } - MemPlaceMeta::Poison => bug!( - "MPlaceTy::dangling may never be used to produce a \ - place that will have the address of its pointee taken" - ), } } #[inline] - pub fn offset<'tcx>( + pub fn offset_with_meta<'tcx>( self, offset: Size, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) } } -impl<Tag: Provenance> Place<Tag> { +impl<Prov: Provenance> Place<Prov> { /// Asserts that this points to some local variable. /// Returns the frame idx and the variable idx. #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn assert_local(&self) -> (usize, mir::Local) { match self { Place::Local { frame, local } => (*frame, *local), @@ -196,41 +185,53 @@ impl<Tag: Provenance> Place<Tag> { } } -impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { - /// Produces a MemPlace that works for ZST but nothing else +impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { + /// Produces a MemPlace that works for ZST but nothing else. + /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you + /// don't need to worry about memory leaks. #[inline] - pub fn dangling(layout: TyAndLayout<'tcx>) -> Self { + pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self { + assert!(layout.is_zst()); let align = layout.align.abi; let ptr = Pointer::from_addr(align.bytes()); // no provenance, absolute address - // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. - MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::Poison }, layout, align } + MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align } } #[inline] - pub fn offset( + pub fn offset_with_meta( &self, offset: Size, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { - mplace: self.mplace.offset(offset, meta, cx)?, + mplace: self.mplace.offset_with_meta(offset, meta, cx)?, align: self.align.restrict_for_offset(offset), layout, }) } + pub fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(!layout.is_unsized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } + #[inline] - pub fn from_aligned_ptr(ptr: Pointer<Option<Tag>>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } } #[inline] pub fn from_aligned_ptr_with_meta( - ptr: Pointer<Option<Tag>>, + ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>, - meta: MemPlaceMeta<Tag>, + meta: MemPlaceMeta<Prov>, ) -> Self { let mut mplace = MemPlace::from_ptr(ptr); mplace.meta = meta; @@ -250,14 +251,14 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) match self.layout.fields { - FieldsShape::Array { count, .. } => Ok(count), + abi::FieldsShape::Array { count, .. } => Ok(count), _ => bug!("len not supported on sized type {:?}", self.layout.ty), } } } #[inline] - pub(super) fn vtable(&self) -> Scalar<Tag> { + pub(super) fn vtable(&self) -> Scalar<Prov> { match self.layout.ty.kind() { ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), _ => bug!("vtable not supported on type {:?}", self.layout.ty), @@ -266,50 +267,50 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } // These are defined here because they produce a place. -impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> { + pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> { match **self { Operand::Indirect(mplace) => { Ok(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() }) } - Operand::Immediate(_) if self.layout.is_zst() => Ok(MPlaceTy::dangling(self.layout)), Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), } } #[inline(always)] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { self.try_as_mplace().unwrap() } } -impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { +impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { /// A place is either an mplace or some local. #[inline] - pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Tag>, (usize, mir::Local)> { + pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> { match **self { Place::Ptr(mplace) => Ok(MPlaceTy { mplace, layout: self.layout, align: self.align }), Place::Local { frame, local } => Err((frame, local)), } } - #[inline] - pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { + #[inline(always)] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) + pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Prov> { self.try_as_mplace().unwrap() } } -// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> +// FIXME: Working around https://github.com/rust-lang/rust/issues/54385 +impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> where - // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 - Tag: Provenance + Eq + Hash + 'static, - M: Machine<'mir, 'tcx, PointerTag = Tag>, + Prov: Provenance + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, Provenance = Prov>, { /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. @@ -319,8 +320,8 @@ where /// Generally prefer `deref_operand`. pub fn ref_to_mplace( &self, - val: &ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + val: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let pointee_type = val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty; let layout = self.layout_of(pointee_type)?; @@ -330,7 +331,7 @@ where Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; - let mplace = MemPlace { ptr: self.scalar_to_ptr(ptr.check_init()?)?, meta }; + let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta }; // When deref'ing a pointer, the *static* alignment given by the type is what matters. let align = layout.align.abi; Ok(MPlaceTy { mplace, layout, align }) @@ -341,8 +342,8 @@ where #[instrument(skip(self), level = "debug")] pub fn deref_operand( &self, - src: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + src: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); @@ -358,8 +359,8 @@ where #[inline] pub(super) fn get_place_alloc( &self, - place: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::PointerTag, M::AllocExtra>>> { + place: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> { assert!(!place.layout.is_unsized()); assert!(!place.meta.has_meta()); let size = place.layout.size; @@ -369,8 +370,8 @@ where #[inline] pub(super) fn get_place_alloc_mut( &mut self, - place: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::PointerTag, M::AllocExtra>>> { + place: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> { assert!(!place.layout.is_unsized()); assert!(!place.meta.has_meta()); let size = place.layout.size; @@ -380,7 +381,7 @@ where /// Check if this mplace is dereferenceable and sufficiently aligned. fn check_mplace_access( &self, - mplace: MPlaceTy<'tcx, M::PointerTag>, + mplace: MPlaceTy<'tcx, M::Provenance>, msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { let (size, align) = self @@ -392,276 +393,29 @@ where Ok(()) } - /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is - /// always possible without allocating, so it can take `&self`. Also return the field's layout. - /// This supports both struct and array fields. - /// - /// This also works for arrays, but then the `usize` index type is restricting. - /// For indexing into arrays, use `mplace_index`. - #[inline(always)] - pub fn mplace_field( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let offset = base.layout.fields.offset(field); - let field_layout = base.layout.field(self, field); - - // Offset may need adjustment for unsized fields. - let (meta, offset) = if field_layout.is_unsized() { - // Re-use parent metadata to determine dynamic field layout. - // With custom DSTS, this *will* execute user-defined code, but the same - // happens at run-time so that's okay. - match self.size_and_align_of(&base.meta, &field_layout)? { - Some((_, align)) => (base.meta, offset.align_to(align)), - None => { - // For unsized types with an extern type tail we perform no adjustments. - // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. - assert!(matches!(base.meta, MemPlaceMeta::None)); - (base.meta, offset) - } - } - } else { - // base.meta could be present; we might be accessing a sized field of an unsized - // struct. - (MemPlaceMeta::None, offset) - }; - - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that - base.offset(offset, meta, field_layout, self) - } - - /// Index into an array. - #[inline(always)] - pub fn mplace_index( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Not using the layout method because we want to compute on u64 - match base.layout.fields { - FieldsShape::Array { stride, .. } => { - let len = base.len(self)?; - if index >= len { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len, index }); - } - let offset = stride * index; // `Size` multiplication - // All fields have the same layout. - let field_layout = base.layout.field(self, 0); - - assert!(!field_layout.is_unsized()); - base.offset(offset, MemPlaceMeta::None, field_layout, self) - } - _ => span_bug!( - self.cur_span(), - "`mplace_index` called on non-array type {:?}", - base.layout.ty - ), - } - } - - // Iterates over all fields of an array. Much more efficient than doing the - // same by repeatedly calling `mplace_array`. - pub(super) fn mplace_array_fields<'a>( - &self, - base: &'a MPlaceTy<'tcx, Tag>, - ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a> - { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout"); - }; - let layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) - } - - fn mplace_subslice( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let actual_to = if from_end { - if from.checked_add(to).map_or(true, |to| to > len) { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); - } - len.checked_sub(to).unwrap() - } else { - to - }; - - // Not using layout method because that works with usize, and does not work with slices - // (that have count 0 in their layout). - let from_offset = match base.layout.fields { - FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked - _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) - } - }; - - // Compute meta and new layout - let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind() { - // It is not nice to match on the type, but that seems to be the only way to - // implement this. - ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)), - ty::Slice(..) => { - let len = Scalar::from_machine_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base.layout.ty) - } - _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) - } - }; - let layout = self.layout_of(ty)?; - base.offset(from_offset, meta, layout, self) - } - - pub(crate) fn mplace_downcast( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Downcasts only change the layout. - // (In particular, no check about whether this is even the active variant -- that's by design, - // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - assert!(!base.meta.has_meta()); - Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..*base }) - } - - /// Project into an mplace - #[instrument(skip(self), level = "debug")] - pub(super) fn mplace_projection( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.mplace_field(base, field.index())?, - Downcast(_, variant) => self.mplace_downcast(base, variant)?, - Deref => self.deref_operand(&base.into())?, - - Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_scalar(&n)?; - let n = n.to_machine_usize(self)?; - self.mplace_index(base, n)? - } - - ConstantIndex { offset, min_length, from_end } => { - let n = base.len(self)?; - if n < min_length { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: min_length, index: n }); - } - - let index = if from_end { - assert!(0 < offset && offset <= min_length); - n.checked_sub(offset).unwrap() - } else { - assert!(offset < min_length); - offset - }; - - self.mplace_index(base, index)? - } - - Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?, - }) - } - /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. /// Also returns the number of elements. pub fn mplace_to_simd( &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // (Transmuting is okay since this is an in-memory place. We also double-check the size // stays the same.) - let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx); + let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx); let array = self.tcx.mk_array(e_ty, len); let layout = self.layout_of(array)?; - assert_eq!(layout.size, base.layout.size); - Ok((MPlaceTy { layout, ..*base }, len)) - } - - /// Gets the place of a field inside the place, and also the field's type. - /// Just a convenience function, but used quite a bit. - /// This is the only projection that might have a side-effect: We cannot project - /// into the field of a local `ScalarPair`, we have to first allocate it. - #[instrument(skip(self), level = "debug")] - pub fn place_field( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // FIXME: We could try to be smarter and avoid allocation for fields that span the - // entire place. - let mplace = self.force_allocation(base)?; - Ok(self.mplace_field(&mplace, field)?.into()) - } - - pub fn place_index( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mplace = self.force_allocation(base)?; - Ok(self.mplace_index(&mplace, index)?.into()) - } - - pub fn place_downcast( - &self, - base: &PlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // Downcast just changes the layout - Ok(match base.try_as_mplace() { - Ok(mplace) => self.mplace_downcast(&mplace, variant)?.into(), - Err(..) => { - let layout = base.layout.for_variant(self, variant); - PlaceTy { layout, ..*base } - } - }) - } - - /// Projects into a place. - pub fn place_projection( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - &proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.place_field(base, field.index())?, - Downcast(_, variant) => self.place_downcast(base, variant)?, - Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), - // For the other variants, we have to force an allocation. - // This matches `operand_projection`. - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - let mplace = self.force_allocation(base)?; - self.mplace_projection(&mplace, proj_elem)?.into() - } - }) + assert_eq!(layout.size, mplace.layout.size); + Ok((MPlaceTy { layout, ..*mplace }, len)) } /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. /// Also returns the number of elements. pub fn place_to_simd( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { - let mplace = self.force_allocation(base)?; + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { + let mplace = self.force_allocation(place)?; self.mplace_to_simd(&mplace) } @@ -669,7 +423,7 @@ where &self, frame: usize, local: mir::Local, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(&self.stack()[frame], local, None)?; let place = Place::Local { frame, local }; Ok(PlaceTy { place, layout, align: layout.align.abi }) @@ -680,30 +434,30 @@ where #[instrument(skip(self), level = "debug")] pub fn eval_place( &mut self, - place: mir::Place<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mut place_ty = self.local_to_place(self.frame_idx(), place.local)?; - - for elem in place.projection.iter() { - place_ty = self.place_projection(&place_ty, &elem)? + mir_place: mir::Place<'tcx>, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; + // Using `try_fold` turned out to be bad for performance, hence the loop. + for elem in mir_place.projection.iter() { + place = self.place_projection(&place, elem)? } - trace!("{:?}", self.dump_place(place_ty.place)); + trace!("{:?}", self.dump_place(place.place)); // Sanity-check the type we ended up with. debug_assert!( mir_assign_valid_types( *self.tcx, self.param_env, self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty )?)?, - place_ty.layout, + place.layout, ), - "eval_place of a MIR place with type {:?} produced an interpret place with type {:?}", - place.ty(&self.frame().body.local_decls, *self.tcx).ty, - place_ty.layout.ty, + "eval_place of a MIR place with type {:?} produced an interpreter place with type {:?}", + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty, + place.layout.ty, ); - Ok(place_ty) + Ok(place) } /// Write an immediate to a place @@ -711,8 +465,8 @@ where #[instrument(skip(self), level = "debug")] pub fn write_immediate( &mut self, - src: Immediate<M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: Immediate<M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate_no_validate(src, dest)?; @@ -728,8 +482,8 @@ where #[inline(always)] pub fn write_scalar( &mut self, - val: impl Into<ScalarMaybeUninit<M::PointerTag>>, - dest: &PlaceTy<'tcx, M::PointerTag>, + val: impl Into<ScalarMaybeUninit<M::Provenance>>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) } @@ -738,8 +492,8 @@ where #[inline(always)] pub fn write_pointer( &mut self, - ptr: impl Into<Pointer<Option<M::PointerTag>>>, - dest: &PlaceTy<'tcx, M::PointerTag>, + ptr: impl Into<Pointer<Option<M::Provenance>>>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest) } @@ -749,8 +503,8 @@ where /// right type. fn write_immediate_no_validate( &mut self, - src: Immediate<M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: Immediate<M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); @@ -783,10 +537,10 @@ where /// right layout. fn write_immediate_to_mplace_no_validate( &mut self, - value: Immediate<M::PointerTag>, + value: Immediate<M::Provenance>, layout: TyAndLayout<'tcx>, align: Align, - dest: MemPlace<M::PointerTag>, + dest: MemPlace<M::Provenance>, ) -> InterpResult<'tcx> { // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here @@ -835,7 +589,7 @@ where } } - pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { let mplace = match dest.try_as_mplace() { Ok(mplace) => mplace, Err((frame, local)) => { @@ -865,8 +619,8 @@ where #[instrument(skip(self), level = "debug")] pub fn copy_op( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { self.copy_op_no_validate(src, dest, allow_transmute)?; @@ -886,8 +640,8 @@ where #[instrument(skip(self), level = "debug")] fn copy_op_no_validate( &mut self, - src: &OpTy<'tcx, M::PointerTag>, - dest: &PlaceTy<'tcx, M::PointerTag>, + src: &OpTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can @@ -959,8 +713,8 @@ where #[instrument(skip(self), level = "debug")] pub fn force_allocation( &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { Place::Local { frame, local } => { match M::access_local_mut(self, frame, local)? { @@ -1006,7 +760,7 @@ where &mut self, layout: TyAndLayout<'tcx>, kind: MemoryKind<M::MemoryKind>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { assert!(!layout.is_unsized()); let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?; Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) @@ -1018,7 +772,7 @@ where str: &str, kind: MemoryKind<M::MemoryKind>, mutbl: Mutability, - ) -> MPlaceTy<'tcx, M::PointerTag> { + ) -> MPlaceTy<'tcx, M::Provenance> { let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl); let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self); let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) }; @@ -1036,7 +790,7 @@ where pub fn write_discriminant( &mut self, variant_index: VariantIdx, - dest: &PlaceTy<'tcx, M::PointerTag>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { // This must be an enum or generator. match dest.layout.ty.kind() { @@ -1058,10 +812,10 @@ where } match dest.layout.variants { - Variants::Single { index } => { + abi::Variants::Single { index } => { assert_eq!(index, variant_index); } - Variants::Multiple { + abi::Variants::Multiple { tag_encoding: TagEncoding::Direct, tag: tag_layout, tag_field, @@ -1082,7 +836,7 @@ where let tag_dest = self.place_field(dest, tag_field)?; self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; } - Variants::Multiple { + abi::Variants::Multiple { tag_encoding: TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, tag: tag_layout, @@ -1122,7 +876,7 @@ where pub fn raw_const_to_mplace( &self, raw: ConstAlloc<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // This must be an allocation in `tcx` let _ = self.tcx.global_alloc(raw.alloc_id); let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?; @@ -1131,28 +885,19 @@ where } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. - /// Also return some more information so drop doesn't have to run the same code twice. pub(super) fn unpack_dyn_trait( &self, - mplace: &MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { - let vtable = self.scalar_to_ptr(mplace.vtable())?; // also sanity checks the type - let (instance, ty) = self.read_drop_type_from_vtable(vtable)?; + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type + let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - // More sanity checks - if cfg!(debug_assertions) { - let (size, align) = self.read_size_and_align_from_vtable(vtable)?; - assert_eq!(size, layout.size); - // only ABI alignment is preserved - assert_eq!(align, layout.align.abi); - } - let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace }, layout, align: layout.align.abi, }; - Ok((instance, mplace)) + Ok(mplace) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs new file mode 100644 index 00000000000..742339f2b0a --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -0,0 +1,391 @@ +//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy. +//! +//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not". +//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway. +//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields), +//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial, +//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually +//! implement the logic on OpTy, and MPlaceTy calls that. + +use std::hash::Hash; + +use rustc_middle::mir; +use rustc_middle::ty; +use rustc_middle::ty::layout::LayoutOf; +use rustc_target::abi::{self, Abi, VariantIdx}; + +use super::{ + ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, + Provenance, Scalar, +}; + +// FIXME: Working around https://github.com/rust-lang/rust/issues/54385 +impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> +where + Prov: Provenance + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, Provenance = Prov>, +{ + //# Field access + + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is + /// always possible without allocating, so it can take `&self`. Also return the field's layout. + /// This supports both struct and array fields. + /// + /// This also works for arrays, but then the `usize` index type is restricting. + /// For indexing into arrays, use `mplace_index`. + pub fn mplace_field( + &self, + base: &MPlaceTy<'tcx, M::Provenance>, + field: usize, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + let offset = base.layout.fields.offset(field); + let field_layout = base.layout.field(self, field); + + // Offset may need adjustment for unsized fields. + let (meta, offset) = if field_layout.is_unsized() { + // Re-use parent metadata to determine dynamic field layout. + // With custom DSTS, this *will* execute user-defined code, but the same + // happens at run-time so that's okay. + match self.size_and_align_of(&base.meta, &field_layout)? { + Some((_, align)) => (base.meta, offset.align_to(align)), + None => { + // For unsized types with an extern type tail we perform no adjustments. + // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. + assert!(matches!(base.meta, MemPlaceMeta::None)); + (base.meta, offset) + } + } + } else { + // base.meta could be present; we might be accessing a sized field of an unsized + // struct. + (MemPlaceMeta::None, offset) + }; + + // We do not look at `base.layout.align` nor `field_layout.align`, unlike + // codegen -- mostly to see if we can get away with that + base.offset_with_meta(offset, meta, field_layout, self) + } + + /// Gets the place of a field inside the place, and also the field's type. + /// Just a convenience function, but used quite a bit. + /// This is the only projection that might have a side-effect: We cannot project + /// into the field of a local `ScalarPair`, we have to first allocate it. + pub fn place_field( + &mut self, + base: &PlaceTy<'tcx, M::Provenance>, + field: usize, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + // FIXME: We could try to be smarter and avoid allocation for fields that span the + // entire place. + let base = self.force_allocation(base)?; + Ok(self.mplace_field(&base, field)?.into()) + } + + pub fn operand_field( + &self, + base: &OpTy<'tcx, M::Provenance>, + field: usize, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let base = match base.try_as_mplace() { + Ok(ref mplace) => { + // We can reuse the mplace field computation logic for indirect operands. + let field = self.mplace_field(mplace, field)?; + return Ok(field.into()); + } + Err(value) => value, + }; + + let field_layout = base.layout.field(self, field); + let offset = base.layout.fields.offset(field); + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let field_val: Immediate<_> = match (*base, base.layout.abi) { + // the field contains no information, can be left uninit + _ if field_layout.is_zst() => Immediate::Uninit, + // the field covers the entire type + _ if field_layout.size == base.layout.size => { + assert!(match (base.layout.abi, field_layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }); + assert!(offset.bytes() == 0); + *base + } + // extract fields from types with `ScalarPair` ABI + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(field_layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(field_layout.size, a.size(self)); + a_val + } else { + debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); + debug_assert_eq!(field_layout.size, b.size(self)); + b_val + }) + } + _ => span_bug!( + self.cur_span(), + "invalid field access on immediate {}, layout {:#?}", + base, + base.layout + ), + }; + + Ok(ImmTy::from_immediate(field_val, field_layout).into()) + } + + //# Downcasting + + pub fn mplace_downcast( + &self, + base: &MPlaceTy<'tcx, M::Provenance>, + variant: VariantIdx, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + // Downcasts only change the layout. + // (In particular, no check about whether this is even the active variant -- that's by design, + // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) + assert!(!base.meta.has_meta()); + let mut base = *base; + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + pub fn place_downcast( + &self, + base: &PlaceTy<'tcx, M::Provenance>, + variant: VariantIdx, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + // Downcast just changes the layout + let mut base = base.clone(); + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + pub fn operand_downcast( + &self, + base: &OpTy<'tcx, M::Provenance>, + variant: VariantIdx, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + // Downcast just changes the layout + let mut base = base.clone(); + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + //# Slice indexing + + #[inline(always)] + pub fn operand_index( + &self, + base: &OpTy<'tcx, M::Provenance>, + index: u64, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + // Not using the layout method because we want to compute on u64 + match base.layout.fields { + abi::FieldsShape::Array { stride, count: _ } => { + // `count` is nonsense for slices, use the dynamic length instead. + let len = base.len(self)?; + if index >= len { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len, index }); + } + let offset = stride * index; // `Size` multiplication + // All fields have the same layout. + let field_layout = base.layout.field(self, 0); + base.offset(offset, field_layout, self) + } + _ => span_bug!( + self.cur_span(), + "`mplace_index` called on non-array type {:?}", + base.layout.ty + ), + } + } + + // Iterates over all fields of an array. Much more efficient than doing the + // same by repeatedly calling `operand_index`. + pub fn operand_array_fields<'a>( + &self, + base: &'a OpTy<'tcx, Prov>, + ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { + span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); + }; + let field_layout = base.layout.field(self, 0); + let dl = &self.tcx.data_layout; + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) + } + + /// Index into an array. + pub fn mplace_index( + &self, + base: &MPlaceTy<'tcx, M::Provenance>, + index: u64, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + Ok(self.operand_index(&base.into(), index)?.assert_mem_place()) + } + + pub fn place_index( + &mut self, + base: &PlaceTy<'tcx, M::Provenance>, + index: u64, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + // There's not a lot we can do here, since we cannot have a place to a part of a local. If + // we are accessing the only element of a 1-element array, it's still the entire local... + // that doesn't seem worth it. + let base = self.force_allocation(base)?; + Ok(self.mplace_index(&base, index)?.into()) + } + + //# ConstantIndex support + + fn operand_constant_index( + &self, + base: &OpTy<'tcx, M::Provenance>, + offset: u64, + min_length: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let n = base.len(self)?; + if n < min_length { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: min_length, index: n }); + } + + let index = if from_end { + assert!(0 < offset && offset <= min_length); + n.checked_sub(offset).unwrap() + } else { + assert!(offset < min_length); + offset + }; + + self.operand_index(base, index) + } + + fn place_constant_index( + &mut self, + base: &PlaceTy<'tcx, M::Provenance>, + offset: u64, + min_length: u64, + from_end: bool, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + let base = self.force_allocation(base)?; + Ok(self + .operand_constant_index(&base.into(), offset, min_length, from_end)? + .assert_mem_place() + .into()) + } + + //# Subslicing + + fn operand_subslice( + &self, + base: &OpTy<'tcx, M::Provenance>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let actual_to = if from_end { + if from.checked_add(to).map_or(true, |to| to > len) { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); + } + len.checked_sub(to).unwrap() + } else { + to + }; + + // Not using layout method because that works with usize, and does not work with slices + // (that have count 0 in their layout). + let from_offset = match base.layout.fields { + abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked + _ => { + span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + } + }; + + // Compute meta and new layout + let inner_len = actual_to.checked_sub(from).unwrap(); + let (meta, ty) = match base.layout.ty.kind() { + // It is not nice to match on the type, but that seems to be the only way to + // implement this. + ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)), + ty::Slice(..) => { + let len = Scalar::from_machine_usize(inner_len, self); + (MemPlaceMeta::Meta(len), base.layout.ty) + } + _ => { + span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + } + }; + let layout = self.layout_of(ty)?; + base.offset_with_meta(from_offset, meta, layout, self) + } + + pub fn place_subslice( + &mut self, + base: &PlaceTy<'tcx, M::Provenance>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + let base = self.force_allocation(base)?; + Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into()) + } + + //# Applying a general projection + + /// Projects into a place. + #[instrument(skip(self), level = "trace")] + pub fn place_projection( + &mut self, + base: &PlaceTy<'tcx, M::Provenance>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.place_field(base, field.index())?, + Downcast(_, variant) => self.place_downcast(base, variant)?, + Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), + Index(local) => { + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.read_scalar(&n)?.to_machine_usize(self)?; + self.place_index(base, n)? + } + ConstantIndex { offset, min_length, from_end } => { + self.place_constant_index(base, offset, min_length, from_end)? + } + Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?, + }) + } + + #[instrument(skip(self), level = "trace")] + pub fn operand_projection( + &self, + base: &OpTy<'tcx, M::Provenance>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.operand_field(base, field.index())?, + Downcast(_, variant) => self.operand_downcast(base, variant)?, + Deref => self.deref_operand(base)?.into(), + Index(local) => { + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.read_scalar(&n)?.to_machine_usize(self)?; + self.operand_index(base, n)? + } + ConstantIndex { offset, min_length, from_end } => { + self.operand_constant_index(base, offset, min_length, from_end)? + } + Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?, + }) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 515cc222dc6..d563e35f910 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::convert::TryFrom; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::Instance; @@ -267,10 +266,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn pass_argument<'x, 'y>( &mut self, caller_args: &mut impl Iterator< - Item = (&'x OpTy<'tcx, M::PointerTag>, &'y ArgAbi<'tcx, Ty<'tcx>>), + Item = (&'x OpTy<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>), >, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, - callee_arg: &PlaceTy<'tcx, M::PointerTag>, + callee_arg: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> where 'tcx: 'x, @@ -336,9 +335,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), - args: &[OpTy<'tcx, M::PointerTag>], + args: &[OpTy<'tcx, M::Provenance>], with_caller_location: bool, - destination: &PlaceTy<'tcx, M::PointerTag>, + destination: &PlaceTy<'tcx, M::Provenance>, target: Option<mir::BasicBlock>, mut unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -365,7 +364,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic. M::call_intrinsic(self, instance, args, destination, target, unwind) } - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::FnPtrShim(..) @@ -437,19 +436,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // last incoming argument. These two iterators do not have the same type, // so to keep the code paths uniform we accept an allocation // (for RustCall ABI only). - let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = + let caller_args: Cow<'_, [OpTy<'tcx, M::Provenance>]> = if caller_abi == Abi::RustCall && !args.is_empty() { // Untuple let (untuple_arg, args) = args.split_last().unwrap(); trace!("eval_fn_call: Will pass last argument by untupling"); Cow::from( args.iter() - .map(|&a| Ok(a)) + .map(|a| Ok(a.clone())) .chain( (0..untuple_arg.layout.fields.count()) .map(|i| self.operand_field(untuple_arg, i)), ) - .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>( + .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::Provenance>>>>( )?, ) } else { @@ -520,16 +519,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } // cannot use the shim here, because that will only result in infinite recursion - ty::InstanceDef::Virtual(_, idx) => { + ty::InstanceDef::Virtual(def_id, idx) => { let mut args = args.to_vec(); // We have to implement all "object safe receivers". So we have to go search for a // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively // unwrap those newtypes until we are there. - let mut receiver = args[0]; + let mut receiver = args[0].clone(); let receiver_place = loop { match receiver.layout.ty.kind() { ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, - ty::Dynamic(..) => break receiver.assert_mem_place(), + ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values _ => { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; @@ -553,17 +552,52 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } }; - // Find and consult vtable. The type now could be something like RcBox<dyn Trait>, - // i.e., it is still not necessarily `ty::Dynamic` (so we cannot use - // `place.vtable()`), but it should have a `dyn Trait` tail. - assert!(matches!( - self.tcx - .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env) - .kind(), - ty::Dynamic(..) - )); - let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?; - let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?; + // Obtain the underlying trait we are working on. + let receiver_tail = self + .tcx + .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); + let ty::Dynamic(data, ..) = receiver_tail.kind() else { + span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail) + }; + + // Get the required information from the vtable. + let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; + let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; + if dyn_trait != data.principal() { + throw_ub_format!( + "`dyn` call on a pointer whose vtable does not match its type" + ); + } + + // Now determine the actual method to call. We can do that in two different ways and + // compare them to ensure everything fits. + let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else { + throw_ub_format!("`dyn` call trying to call something that is not a method") + }; + if cfg!(debug_assertions) { + let tcx = *self.tcx; + + let trait_def_id = tcx.trait_of_item(def_id).unwrap(); + let virtual_trait_ref = + ty::TraitRef::from_method(tcx, trait_def_id, instance.substs); + assert_eq!( + receiver_tail, + virtual_trait_ref.self_ty(), + "mismatch in underlying dyn trait computation within Miri and MIR building", + ); + let existential_trait_ref = + ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); + let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); + + let concrete_method = Instance::resolve_for_vtable( + tcx, + self.param_env, + def_id, + instance.substs.rebase_onto(tcx, trait_def_id, concrete_trait_ref.substs), + ) + .unwrap(); + assert_eq!(fn_inst, concrete_method); + } // `*mut receiver_place.layout.ty` is almost the layout that we // want for args[0]: We have to project to field 0 because we want @@ -579,7 +613,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("Patched receiver operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call( - fn_val, + FnVal::Instance(fn_inst), (caller_abi, caller_fn_abi), &args, with_caller_location, @@ -593,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn drop_in_place( &mut self, - place: &PlaceTy<'tcx, M::PointerTag>, + place: &PlaceTy<'tcx, M::Provenance>, instance: ty::Instance<'tcx>, target: mir::BasicBlock, unwind: Option<mir::BasicBlock>, @@ -606,8 +640,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (instance, place) = match place.layout.ty.kind() { ty::Dynamic(..) => { - // Dropping a trait object. - self.unpack_dyn_trait(&place)? + // Dropping a trait object. Need to find actual drop fn. + let place = self.unpack_dyn_trait(&place)?; + let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); + (instance, place) } _ => (instance, place), }; @@ -617,16 +653,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { place.to_ref(self), self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, ); - - let ty = self.tcx.mk_unit(); // return type is () - let dest = MPlaceTy::dangling(self.layout_of(ty)?); + let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); self.eval_fn_call( FnVal::Instance(instance), (Abi::Rust, fn_abi), &[arg.into()], false, - &dest.into(), + &ret.into(), Some(target), match unwind { Some(cleanup) => StackPopUnwind::Cleanup(cleanup), diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 22c23df7b1a..b3a511d5a49 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,27 +1,24 @@ -use std::convert::TryFrom; - -use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic}; -use rustc_middle::ty::{ - self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE, - COMMON_VTABLE_ENTRIES_SIZE, -}; +use rustc_middle::mir::interpret::{InterpResult, Pointer}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::{Align, Size}; use super::util::ensure_monomorphic_enough; -use super::{FnVal, InterpCx, Machine}; +use super::{InterpCx, Machine}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// - /// The `trait_ref` encodes the erased self type. Hence, if we are - /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then - /// `trait_ref` would map `T: Trait`. - pub fn get_vtable( - &mut self, + /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>` + /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that + /// this is an auto trait without any methods, so we only need the basic vtable (drop, size, + /// align). + pub fn get_vtable_ptr( + &self, ty: Ty<'tcx>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { + ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { trace!("get_vtable(trait_ref={:?})", poly_trait_ref); let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); @@ -30,114 +27,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ensure_monomorphic_enough(*self.tcx, ty)?; ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; - let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref)); - - let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?; - + let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref); + let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } - /// Resolves the function at the specified slot in the provided - /// vtable. Currently an index of '3' (`TyCtxt::COMMON_VTABLE_ENTRIES.len()`) - /// corresponds to the first method declared in the trait of the provided vtable. - pub fn get_vtable_slot( - &self, - vtable: Pointer<Option<M::PointerTag>>, - idx: u64, - ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - let ptr_size = self.pointer_size(); - let vtable_slot = vtable.offset(ptr_size * idx, self)?; - let vtable_slot = self - .get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? - .expect("cannot be a ZST"); - let fn_ptr = self.scalar_to_ptr(vtable_slot.read_pointer(Size::ZERO)?.check_init()?)?; - self.get_ptr_fn(fn_ptr) - } - - /// Returns the drop fn instance as well as the actual dynamic type. - pub fn read_drop_type_from_vtable( + /// Returns a high-level representation of the entires of the given vtable. + pub fn get_vtable_entries( &self, - vtable: Pointer<Option<M::PointerTag>>, - ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> { - let pointer_size = self.pointer_size(); - // We don't care about the pointee type; we just want a pointer. - let vtable = self - .get_ptr_alloc( - vtable, - pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(), - self.tcx.data_layout.pointer_align.abi, - )? - .expect("cannot be a ZST"); - let drop_fn = vtable - .read_pointer(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap())? - .check_init()?; - // We *need* an instance here, no other kind of function value, to be able - // to determine the type. - let drop_instance = self.get_ptr_fn(self.scalar_to_ptr(drop_fn)?)?.as_instance()?; - trace!("Found drop fn: {:?}", drop_instance); - let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx); - let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig); - // The drop function takes `*mut T` where `T` is the type being dropped, so get that. - let args = fn_sig.inputs(); - if args.len() != 1 { - throw_ub!(InvalidVtableDropFn(fn_sig)); - } - let ty = - args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty; - Ok((drop_instance, ty)) + vtable: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { + let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; + Ok(if let Some(poly_trait_ref) = poly_trait_ref { + let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); + let trait_ref = self.tcx.erase_regions(trait_ref); + self.tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + }) } - pub fn read_size_and_align_from_vtable( + pub fn get_vtable_size_and_align( &self, - vtable: Pointer<Option<M::PointerTag>>, + vtable: Pointer<Option<M::Provenance>>, ) -> InterpResult<'tcx, (Size, Align)> { - let pointer_size = self.pointer_size(); - // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here), - // the size, and the align (which we read below). - let vtable = self - .get_ptr_alloc( - vtable, - pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(), - self.tcx.data_layout.pointer_align.abi, - )? - .expect("cannot be a ZST"); - let size = vtable - .read_integer(alloc_range( - pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap(), - pointer_size, - ))? - .check_init()?; - let size = size.to_machine_usize(self)?; - let size = Size::from_bytes(size); - let align = vtable - .read_integer(alloc_range( - pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap(), - pointer_size, - ))? - .check_init()?; - let align = align.to_machine_usize(self)?; - let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; - - if size > self.max_size_of_val() { - throw_ub!(InvalidVtableSize); - } - Ok((size, align)) - } - - pub fn read_new_vtable_after_trait_upcasting_from_vtable( - &self, - vtable: Pointer<Option<M::PointerTag>>, - idx: u64, - ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> { - let pointer_size = self.pointer_size(); - - let vtable_slot = vtable.offset(pointer_size * idx, self)?; - let new_vtable = self - .get_ptr_alloc(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)? - .expect("cannot be a ZST"); - - let new_vtable = self.scalar_to_ptr(new_vtable.read_pointer(Size::ZERO)?.check_init()?)?; - - Ok(new_vtable) + let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let layout = self.layout_of(ty)?; + assert!(!layout.is_unsized(), "there are no vtables for unsized types"); + Ok((layout.size, layout.align.abi)) } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 08102585a7b..d20f16755c3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -206,7 +206,7 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// starts must not be changed! `visit_fields` and `visit_array` rely on /// this stack discipline. path: Vec<PathElem>, - ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, + ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>, /// `None` indicates this is not validating for CTFE (but for runtime). ctfe_mode: Option<CtfeValidationMode>, ecx: &'rt InterpCx<'mir, 'tcx, M>, @@ -306,57 +306,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn check_wide_ptr_meta( &mut self, - meta: MemPlaceMeta<M::PointerTag>, + meta: MemPlaceMeta<M::Provenance>, pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { ty::Dynamic(..) => { - let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?; - // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. - try_validation!( - self.ecx.check_ptr_access_align( - vtable, - 3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align - self.ecx.tcx.data_layout.pointer_align.abi, - CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message - ), - self.path, - err_ub!(DanglingIntPointer(..)) | - err_ub!(PointerUseAfterFree(..)) => - { "dangling vtable pointer in wide pointer" }, - err_ub!(AlignmentCheckFailed { .. }) => - { "unaligned vtable pointer in wide pointer" }, - err_ub!(PointerOutOfBounds { .. }) => - { "too small vtable" }, - ); - try_validation!( - self.ecx.read_drop_type_from_vtable(vtable), + let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; + // Make sure it is a genuine vtable pointer. + let (_ty, _trait) = try_validation!( + self.ecx.get_ptr_vtable(vtable), self.path, err_ub!(DanglingIntPointer(..)) | - err_ub!(InvalidFunctionPointer(..)) => - { "invalid drop function pointer in vtable (not pointing to a function)" }, - err_ub!(InvalidVtableDropFn(..)) => - { "invalid drop function pointer in vtable (function has incompatible signature)" }, - // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123. - // (We assume there are no other MachineStop errors possible here.) - InterpError::MachineStop(_) => - { "vtable pointer does not have permission to read drop function pointer" }, + err_ub!(InvalidVTablePointer(..)) => + { "{vtable}" } expected { "a vtable pointer" }, ); - try_validation!( - self.ecx.read_size_and_align_from_vtable(vtable), - self.path, - err_ub!(InvalidVtableSize) => - { "invalid vtable: size is bigger than largest supported object" }, - err_ub!(InvalidVtableAlignment(msg)) => - { "invalid vtable: alignment {}", msg }, - err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" }, - // Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123. - // (We assume there are no other MachineStop errors possible here.) - InterpError::MachineStop(_) => - { "vtable pointer does not have permission to read size and alignment" }, - ); - // FIXME: More checks for the vtable. + // FIXME: check if the type/trait match what ty::Dynamic says? } ty::Slice(..) | ty::Str => { let _len = try_validation!( @@ -380,7 +345,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// Check a reference or `Box`. fn check_safe_pointer( &mut self, - value: &OpTy<'tcx, M::PointerTag>, + value: &OpTy<'tcx, M::Provenance>, kind: &str, ) -> InterpResult<'tcx> { let value = try_validation!( @@ -445,9 +410,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' if let Some(ref mut ref_tracking) = self.ref_tracking { // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. - if let Ok((alloc_id, _offset, _tag)) = self.ecx.ptr_try_get_alloc_id(place.ptr) { + if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) { // Special handling for pointers to statics (irrespective of their type). - let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id); + let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id); if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); @@ -491,8 +456,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn read_scalar( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> { Ok(try_validation!( self.ecx.read_scalar(op), self.path, @@ -502,8 +467,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn read_immediate_forced( &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Immediate<M::Provenance>> { Ok(*try_validation!( self.ecx.read_immediate_raw(op, /*force*/ true), self.path, @@ -515,7 +480,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// at that type. Return `true` if the type is indeed primitive. fn try_visit_primitive( &mut self, - value: &OpTy<'tcx, M::PointerTag>, + value: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; @@ -552,15 +517,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "{:x}", value } expected { "initialized bytes" } ); } - if M::enforce_number_no_provenance(self.ecx) { - // As a special exception we *do* match on a `Scalar` here, since we truly want - // to know its underlying representation (and *not* cast it to an integer). - let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..))); - if is_ptr { - throw_validation_failure!(self.path, - { "{:x}", value } expected { "plain (non-pointer) bytes" } - ) - } + // As a special exception we *do* match on a `Scalar` here, since we truly want + // to know its underlying representation (and *not* cast it to an integer). + let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..))); + if is_ptr { + throw_validation_failure!(self.path, + { "{:x}", value } expected { "plain (non-pointer) bytes" } + ) } Ok(true) } @@ -603,15 +566,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // If we check references recursively, also check that this points to a function. if let Some(_) = self.ref_tracking { - let ptr = self.ecx.scalar_to_ptr(value)?; + let ptr = value.to_pointer(self.ecx)?; let _fn = try_validation!( self.ecx.get_ptr_fn(ptr), self.path, - err_ub!(DanglingIntPointer(0, _)) => - { "a null function pointer" }, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) => - { "{:x}", value } expected { "a function pointer" }, + { "{ptr}" } expected { "a function pointer" }, ); // FIXME: Check if the signature matches } else { @@ -652,7 +613,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn visit_scalar( &mut self, - scalar: ScalarMaybeUninit<M::PointerTag>, + scalar: ScalarMaybeUninit<M::Provenance>, scalar_layout: ScalarAbi, ) -> InterpResult<'tcx> { // We check `is_full_range` in a slightly complicated way because *if* we are checking @@ -735,7 +696,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> for ValidityVisitor<'rt, 'mir, 'tcx, M> { - type V = OpTy<'tcx, M::PointerTag>; + type V = OpTy<'tcx, M::Provenance>; #[inline(always)] fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { @@ -744,7 +705,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> fn read_discriminant( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { self.with_elem(PathElem::EnumTag, move |this| { Ok(try_validation!( @@ -764,9 +725,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline] fn visit_field( &mut self, - old_op: &OpTy<'tcx, M::PointerTag>, + old_op: &OpTy<'tcx, M::Provenance>, field: usize, - new_op: &OpTy<'tcx, M::PointerTag>, + new_op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let elem = self.aggregate_field_path_elem(old_op.layout, field); self.with_elem(elem, move |this| this.visit_value(new_op)) @@ -775,9 +736,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline] fn visit_variant( &mut self, - old_op: &OpTy<'tcx, M::PointerTag>, + old_op: &OpTy<'tcx, M::Provenance>, variant_id: VariantIdx, - new_op: &OpTy<'tcx, M::PointerTag>, + new_op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let name = match old_op.layout.ty.kind() { ty::Adt(adt, _) => PathElem::Variant(adt.variant(variant_id).name), @@ -791,7 +752,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> #[inline(always)] fn visit_union( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, _fields: NonZeroUsize, ) -> InterpResult<'tcx> { // Special check preventing `UnsafeCell` inside unions in the inner part of constants. @@ -804,13 +765,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } #[inline] - fn visit_box(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { self.check_safe_pointer(op, "box")?; Ok(()) } #[inline] - fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + fn visit_value(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { trace!("visit_value: {:?}, {:?}", *op, op.layout); // Check primitive types -- the leaves of our recursive descent. @@ -821,7 +782,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Special check preventing `UnsafeCell` in the inner part of constants if let Some(def) = op.layout.ty.ty_adt_def() { if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) - && Some(def.did()) == self.ecx.tcx.lang_items().unsafe_cell_type() + && def.is_unsafe_cell() { throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); } @@ -847,15 +808,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } Abi::Scalar(scalar_layout) => { + // We use a 'forced' read because we always need a `Immediate` here + // and treating "partially uninit" as "fully uninit" is fine for us. let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit(); self.visit_scalar(scalar, scalar_layout)?; } Abi::ScalarPair(a_layout, b_layout) => { - // We would validate these things as we descend into the fields, + // There is no `rustc_layout_scalar_valid_range_start` for pairs, so + // we would validate these things as we descend into the fields, // but that can miss bugs in layout computation. Layout computation // is subtle due to enums having ScalarPair layout, where one field // is the discriminant. if cfg!(debug_assertions) { + // We use a 'forced' read because we always need a `Immediate` here + // and treating "partially uninit" as "fully uninit" is fine for us. let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair(); self.visit_scalar(a, a_layout)?; self.visit_scalar(b, b_layout)?; @@ -863,7 +829,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } Abi::Vector { .. } => { // No checks here, we assume layout computation gets this right. - // (This is harder to check since Miri does not represent these as `Immediate`.) + // (This is harder to check since Miri does not represent these as `Immediate`. We + // also cannot use field projections since this might be a newtype around a vector.) } Abi::Aggregate { .. } => { // Nothing to do. @@ -875,12 +842,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> fn visit_aggregate( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, ) -> InterpResult<'tcx> { match op.layout.ty.kind() { ty::Str => { - let mplace = op.assert_mem_place(); // strings are never immediate + let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate let len = mplace.len(self.ecx)?; try_validation!( self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)), @@ -900,14 +867,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> { // Optimized handling for arrays of integer/float type. - // Arrays cannot be immediate, slices are never immediate. - let mplace = op.assert_mem_place(); // This is the length of the array/slice. - let len = mplace.len(self.ecx)?; + let len = op.len(self.ecx)?; // This is the element type size. let layout = self.ecx.layout_of(*tys)?; // This is the size in bytes of the whole array. (This checks for overflow.) let size = layout.size * len; + // If the size is 0, there is nothing to check. + // (`size` can only be 0 of `len` is 0, and empty arrays are always valid.) + if size == Size::ZERO { + return Ok(()); + } + // Now that we definitely have a non-ZST array, we know it lives in memory. + let mplace = match op.try_as_mplace() { + Ok(mplace) => mplace, + Err(imm) => match *imm { + Immediate::Uninit => + throw_validation_failure!(self.path, { "uninitialized bytes" }), + Immediate::Scalar(..) | Immediate::ScalarPair(..) => + bug!("arrays/slices can never have Scalar/ScalarPair layout"), + } + }; // Optimization: we just check the entire range at once. // NOTE: Keep this in sync with the handling of integer and float @@ -919,15 +899,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // to reject those pointers, we just do not have the machinery to // talk about parts of a pointer. // We also accept uninit, for consistency with the slow path. - let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)? else { - // Size 0, nothing more to check. - return Ok(()); - }; + let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0"); match alloc.check_bytes( alloc_range(Size::ZERO, size), /*allow_uninit*/ !M::enforce_number_init(self.ecx), - /*allow_ptr*/ !M::enforce_number_no_provenance(self.ecx), + /*allow_ptr*/ false, ) { // In the happy case, we needn't check anything else. Ok(()) => {} @@ -976,9 +953,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn validate_operand_internal( &self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, path: Vec<PathElem>, - ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>>, + ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>, ctfe_mode: Option<CtfeValidationMode>, ) -> InterpResult<'tcx> { trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); @@ -1015,9 +992,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn const_validate_operand( &self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, path: Vec<PathElem>, - ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::PointerTag>, Vec<PathElem>>, + ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>, ctfe_mode: CtfeValidationMode, ) -> InterpResult<'tcx> { self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode)) @@ -1027,7 +1004,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// `op` is assumed to cover valid memory if it is an indirect operand. /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] - pub fn validate_operand(&self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + pub fn validate_operand(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { self.validate_operand_internal(op, vec![], None, None) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index ded4c6a557a..aee1f93b1a3 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -8,21 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; use std::num::NonZeroUsize; -use super::{InterpCx, MPlaceTy, Machine, OpTy}; +use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy}; -// A thing that we can project into, and that has a layout. -// This wouldn't have to depend on `Machine` but with the current type inference, -// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { +/// A thing that we can project into, and that has a layout. +/// This wouldn't have to depend on `Machine` but with the current type inference, +/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). +pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized { /// Gets this value's layout. fn layout(&self) -> TyAndLayout<'tcx>; - /// Makes this into an `OpTy`. - fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>) - -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + /// Makes this into an `OpTy`, in a cheap way that is good for reading. + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - /// Creates this from an `MPlaceTy`. - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self; + /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. + fn to_op_for_proj( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + self.to_op_for_read(ecx) + } + + /// Creates this from an `OpTy`. + /// + /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; /// Projects to the given enum variant. fn project_downcast( @@ -39,25 +51,67 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { ) -> InterpResult<'tcx, Self>; } -// Operands and memory-places are both values. -// Places in general are not due to `place_field` having to do `force_allocation`. -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { +/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout. +/// This wouldn't have to depend on `Machine` but with the current type inference, +/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). +pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Sized { + /// Gets this value's layout. + fn layout(&self) -> TyAndLayout<'tcx>; + + /// Makes this into an `OpTy`, in a cheap way that is good for reading. + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; + + /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. + fn to_op_for_proj( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; + + /// Creates this from an `OpTy`. + /// + /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; + + /// Projects to the given enum variant. + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self>; + + /// Projects to the n-th field. + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self>; +} + +// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we +// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.) +// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this +// double-impl, that would barely make the code shorter, if at all.) + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { self.layout } #[inline(always)] - fn to_op( + fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - Ok(*self) + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone()) } #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace.into() + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { + op.clone() } #[inline(always)] @@ -79,8 +133,56 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } } +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for OpTy<'tcx, M::Provenance> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone()) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone()) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { + op.clone() + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.operand_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.operand_field(self, field) + } +} + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::PointerTag> + for MPlaceTy<'tcx, M::Provenance> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -88,16 +190,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } #[inline(always)] - fn to_op( + fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.into()) } #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { + // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands. + op.assert_mem_place() } #[inline(always)] @@ -119,11 +222,111 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } } +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for MPlaceTy<'tcx, M::Provenance> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.into()) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.into()) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { + // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands. + op.assert_mem_place() + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_field(self, field) + } +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for PlaceTy<'tcx, M::Provenance> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + // We `force_allocation` here so that `from_op` below can work. + ecx.place_to_op(self) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + // We `force_allocation` here so that `from_op` below can work. + Ok(ecx.force_allocation(self)?.into()) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { + // assert is justified because our `to_op` only ever produces `Indirect` operands. + op.assert_mem_place().into() + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.place_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.place_field(self, field) + } +} + macro_rules! make_value_visitor { - ($visitor_trait_name:ident, $($mutability:ident)?) => { + ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => { // How to traverse a value and what to do when we are at the leaves. - pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: Value<'mir, 'tcx, M>; + pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { + type V: $value_trait<'mir, 'tcx, M>; /// The visitor must have an `InterpCx` in it. fn ecx(&$($mutability)? self) @@ -133,7 +336,7 @@ macro_rules! make_value_visitor { #[inline(always)] fn read_discriminant( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { Ok(self.ecx().read_discriminant(op)?.1) } @@ -212,19 +415,20 @@ macro_rules! make_value_visitor { } fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { - trace!("walk_value: type: {}", v.layout().ty); + let ty = v.layout().ty; + trace!("walk_value: type: {ty}"); // Special treatment for special types, where the (static) layout is not sufficient. - match *v.layout().ty.kind() { + match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { - // immediate trait objects are not a thing - let op = v.to_op(self.ecx())?; + // unsized values are never immediate, so we can assert_mem_place + let op = v.to_op_for_read(self.ecx())?; let dest = op.assert_mem_place(); - let inner = self.ecx().unpack_dyn_trait(&dest)?.1; - trace!("walk_value: dyn object layout: {:#?}", inner.layout); + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?; + trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type - return self.visit_field(&v, 0, &Value::from_mem_place(inner)); + return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); }, // Slices do not need special handling here: they have `Array` field // placement with length 0, so we enter the `Array` case below which @@ -269,16 +473,19 @@ macro_rules! make_value_visitor { // The second `Box` field is the allocator, which we recursively check for validity // like in regular structs. self.visit_field(v, 1, &alloc)?; + + // We visited all parts of this one. + return Ok(()); } _ => {}, }; // Visit the fields of this value. match v.layout().fields { - FieldsShape::Primitive => {}, + FieldsShape::Primitive => {} FieldsShape::Union(fields) => { self.visit_union(v, fields)?; - }, + } FieldsShape::Arbitrary { ref offsets, .. } => { // FIXME: We collect in a vec because otherwise there are lifetime // errors: Projecting to a field needs access to `ecx`. @@ -288,17 +495,17 @@ macro_rules! make_value_visitor { }) .collect(); self.visit_aggregate(v, fields.into_iter())?; - }, + } FieldsShape::Array { .. } => { - // Let's get an mplace first. - let op = v.to_op(self.ecx())?; - let mplace = op.assert_mem_place(); + // Let's get an mplace (or immediate) first. + // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway. + let op = v.to_op_for_proj(self.ecx())?; // Now we can go over all the fields. // This uses the *run-time length*, i.e., if we are a slice, // the dynamic info from the metadata is used. - let iter = self.ecx().mplace_array_fields(&mplace)? + let iter = self.ecx().operand_array_fields(&op)? .map(|f| f.and_then(|f| { - Ok(Value::from_mem_place(f)) + Ok($value_trait::from_op(&f)) })); self.visit_aggregate(v, iter)?; } @@ -308,7 +515,7 @@ macro_rules! make_value_visitor { // If this is a multi-variant layout, find the right variant and proceed // with *its* fields. Variants::Multiple { .. } => { - let op = v.to_op(self.ecx())?; + let op = v.to_op_for_read(self.ecx())?; let idx = self.read_discriminant(&op)?; let inner = v.project_downcast(self.ecx(), idx)?; trace!("walk_value: variant layout: {:#?}", inner.layout()); @@ -323,5 +530,5 @@ macro_rules! make_value_visitor { } } -make_value_visitor!(ValueVisitor,); -make_value_visitor!(MutValueVisitor, mut); +make_value_visitor!(ValueVisitor, Value,); +make_value_visitor!(MutValueVisitor, ValueMut, mut); diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index d65d4f7eb72..1063f03d0e2 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -9,7 +9,7 @@ Rust MIR: a lowered representation of Rust. #![feature(control_flow_enum)] #![feature(decl_macro)] #![feature(exact_size_is_empty)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -33,11 +33,13 @@ extern crate rustc_middle; pub mod const_eval; mod errors; pub mod interpret; +mod might_permit_raw_init; pub mod transform; pub mod util; use rustc_middle::ty; use rustc_middle::ty::query::Providers; +use rustc_target::abi::InitKind; pub fn provide(providers: &mut Providers) { const_eval::provide(providers); @@ -59,4 +61,8 @@ pub fn provide(providers: &mut Providers) { let (param_env, value) = param_env_and_value.into_parts(); const_eval::deref_mir_constant(tcx, param_env, value) }; + providers.permits_uninit_init = + |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Uninit); + providers.permits_zero_init = + |tcx, ty| might_permit_raw_init::might_permit_raw_init(tcx, ty, InitKind::Zero); } diff --git a/compiler/rustc_const_eval/src/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/might_permit_raw_init.rs new file mode 100644 index 00000000000..f971c2238c7 --- /dev/null +++ b/compiler/rustc_const_eval/src/might_permit_raw_init.rs @@ -0,0 +1,40 @@ +use crate::const_eval::CompileTimeInterpreter; +use crate::interpret::{InterpCx, MemoryKind, OpTy}; +use rustc_middle::ty::layout::LayoutCx; +use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt}; +use rustc_session::Limit; +use rustc_target::abi::InitKind; + +pub fn might_permit_raw_init<'tcx>( + tcx: TyCtxt<'tcx>, + ty: TyAndLayout<'tcx>, + kind: InitKind, +) -> bool { + let strict = tcx.sess.opts.unstable_opts.strict_init_checks; + + if strict { + let machine = CompileTimeInterpreter::new(Limit::new(0), false); + + let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); + + let allocated = cx + .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .expect("OOM: failed to allocate for uninit check"); + + if kind == InitKind::Zero { + cx.write_bytes_ptr( + allocated.ptr, + std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()), + ) + .expect("failed to write bytes for zero valid check"); + } + + let ot: OpTy<'_, _> = allocated.into(); + + // Assume that if it failed, it's a validation failure. + cx.validate_operand(&ot).is_ok() + } else { + let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() }; + ty.might_permit_raw_init(&layout_cx, kind) + } +} 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 39fabd17c52..628298df473 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -312,7 +312,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { Status::Forbidden => None, }; - if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { + if self.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { self.tcx.sess.miri_unleashed_feature(span, gate); return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 1d083b0bf82..33802261644 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -23,8 +23,8 @@ use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; use crate::errors::{ - MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr, - StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw, + MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr, + TransientMutBorrowErr, TransientMutBorrowErrRaw, }; use crate::util::{call_kind, CallDesugaringKind, CallKind}; @@ -654,10 +654,10 @@ pub struct RawPtrComparison; impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { fn build_error( &self, - ccx: &ConstCx<'_, 'tcx>, + _: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - ccx.tcx.sess.create_err(RawPtrComparisonErr { span }) + span_bug!(span, "raw ptr comparison should already be caught in the trait system"); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 9941afe5a12..e0994451172 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -96,13 +96,13 @@ impl Qualif for HasMutInterior { } fn in_adt_inherently<'tcx>( - cx: &ConstCx<'_, 'tcx>, + _cx: &ConstCx<'_, 'tcx>, adt: AdtDef<'tcx>, _: SubstsRef<'tcx>, ) -> bool { // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently. // It arises structurally for all other types. - Some(adt.did()) == cx.tcx.lang_items().unsafe_cell_type() + adt.is_unsafe_cell() } } @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cd2b8be0acd..15e820f2d19 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -223,7 +223,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed. - if self.tcx.sess.opts.debugging_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered + if self.tcx.sess.opts.unstable_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered { // `Operand::Copy` is only supposed to be used with `Copy` types. if let Operand::Copy(place) = operand { diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 2a801d0e702..5c641f54f68 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] arrayvec = { version = "0.7", default-features = false } ena = "0.14" -indexmap = { version = "1.8.2" } +indexmap = { version = "1.9.1" } tracing = "0.1" jobserver_crate = { version = "0.1.13", package = "jobserver" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 0a2d2b40709..265f45b72d1 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -61,12 +61,10 @@ pub mod sip128; pub mod small_c_str; pub mod small_str; pub mod snapshot_map; -pub mod stable_map; pub mod svh; pub use ena::snapshot_vec; pub mod memmap; pub mod sorted_map; -pub mod stable_set; #[macro_use] pub mod stable_hasher; mod atomic_ref; diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs index 26b26415eea..917416df6b8 100644 --- a/compiler/rustc_data_structures/src/memmap.rs +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -1,6 +1,6 @@ use std::fs::File; use std::io; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use crate::owning_ref::StableAddress; @@ -45,3 +45,64 @@ impl Deref for Mmap { // export any function that can cause the `Vec` to be re-allocated. As such the address of the // bytes inside this `Vec` is stable. unsafe impl StableAddress for Mmap {} + +#[cfg(not(target_arch = "wasm32"))] +pub struct MmapMut(memmap2::MmapMut); + +#[cfg(target_arch = "wasm32")] +pub struct MmapMut(Vec<u8>); + +#[cfg(not(target_arch = "wasm32"))] +impl MmapMut { + #[inline] + pub fn map_anon(len: usize) -> io::Result<Self> { + let mmap = memmap2::MmapMut::map_anon(len)?; + Ok(MmapMut(mmap)) + } + + #[inline] + pub fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + + #[inline] + pub fn make_read_only(self) -> std::io::Result<Mmap> { + let mmap = self.0.make_read_only()?; + Ok(Mmap(mmap)) + } +} + +#[cfg(target_arch = "wasm32")] +impl MmapMut { + #[inline] + pub fn map_anon(len: usize) -> io::Result<Self> { + let data = Vec::with_capacity(len); + Ok(MmapMut(data)) + } + + #[inline] + pub fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + #[inline] + pub fn make_read_only(self) -> std::io::Result<Mmap> { + Ok(Mmap(self.0)) + } +} + +impl Deref for MmapMut { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &*self.0 + } +} + +impl DerefMut for MmapMut { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut *self.0 + } +} diff --git a/compiler/rustc_data_structures/src/stable_map.rs b/compiler/rustc_data_structures/src/stable_map.rs deleted file mode 100644 index 670452d0d8c..00000000000 --- a/compiler/rustc_data_structures/src/stable_map.rs +++ /dev/null @@ -1,100 +0,0 @@ -pub use rustc_hash::FxHashMap; -use std::borrow::Borrow; -use std::collections::hash_map::Entry; -use std::fmt; -use std::hash::Hash; - -/// A deterministic wrapper around FxHashMap that does not provide iteration support. -/// -/// It supports insert, remove, get and get_mut functions from FxHashMap. -/// It also allows to convert hashmap to a sorted vector with the method `into_sorted_vector()`. -#[derive(Clone)] -pub struct StableMap<K, V> { - base: FxHashMap<K, V>, -} - -impl<K, V> Default for StableMap<K, V> -where - K: Eq + Hash, -{ - fn default() -> StableMap<K, V> { - StableMap::new() - } -} - -impl<K, V> fmt::Debug for StableMap<K, V> -where - K: Eq + Hash + fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.base) - } -} - -impl<K, V> PartialEq for StableMap<K, V> -where - K: Eq + Hash, - V: PartialEq, -{ - fn eq(&self, other: &StableMap<K, V>) -> bool { - self.base == other.base - } -} - -impl<K, V> Eq for StableMap<K, V> -where - K: Eq + Hash, - V: Eq, -{ -} - -impl<K, V> StableMap<K, V> -where - K: Eq + Hash, -{ - pub fn new() -> StableMap<K, V> { - StableMap { base: FxHashMap::default() } - } - - pub fn into_sorted_vector(self) -> Vec<(K, V)> - where - K: Ord + Copy, - { - let mut vector = self.base.into_iter().collect::<Vec<_>>(); - vector.sort_unstable_by_key(|pair| pair.0); - vector - } - - pub fn entry(&mut self, k: K) -> Entry<'_, K, V> { - self.base.entry(k) - } - - pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get(k) - } - - pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get_mut(k) - } - - pub fn insert(&mut self, k: K, v: V) -> Option<V> { - self.base.insert(k, v) - } - - pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> - where - K: Borrow<Q>, - Q: Hash + Eq, - { - self.base.remove(k) - } -} diff --git a/compiler/rustc_data_structures/src/stable_set.rs b/compiler/rustc_data_structures/src/stable_set.rs deleted file mode 100644 index c7ca74f5fbd..00000000000 --- a/compiler/rustc_data_structures/src/stable_set.rs +++ /dev/null @@ -1,77 +0,0 @@ -pub use rustc_hash::FxHashSet; -use std::borrow::Borrow; -use std::fmt; -use std::hash::Hash; - -/// A deterministic wrapper around FxHashSet that does not provide iteration support. -/// -/// It supports insert, remove, get functions from FxHashSet. -/// It also allows to convert hashset to a sorted vector with the method `into_sorted_vector()`. -#[derive(Clone)] -pub struct StableSet<T> { - base: FxHashSet<T>, -} - -impl<T> Default for StableSet<T> -where - T: Eq + Hash, -{ - fn default() -> StableSet<T> { - StableSet::new() - } -} - -impl<T> fmt::Debug for StableSet<T> -where - T: Eq + Hash + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.base) - } -} - -impl<T> PartialEq<StableSet<T>> for StableSet<T> -where - T: Eq + Hash, -{ - fn eq(&self, other: &StableSet<T>) -> bool { - self.base == other.base - } -} - -impl<T> Eq for StableSet<T> where T: Eq + Hash {} - -impl<T: Hash + Eq> StableSet<T> { - pub fn new() -> StableSet<T> { - StableSet { base: FxHashSet::default() } - } - - pub fn into_sorted_vector(self) -> Vec<T> - where - T: Ord, - { - let mut vector = self.base.into_iter().collect::<Vec<_>>(); - vector.sort_unstable(); - vector - } - - pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> - where - T: Borrow<Q>, - Q: Hash + Eq, - { - self.base.get(value) - } - - pub fn insert(&mut self, value: T) -> bool { - self.base.insert(value) - } - - pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool - where - T: Borrow<Q>, - Q: Hash + Eq, - { - self.base.remove(value) - } -} diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index cf0940df9e4..52952a7932d 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -146,7 +146,7 @@ cfg_if! { t.into_iter() } - pub fn par_for_each_in<T: IntoIterator>(t: T, for_each: impl Fn(T::Item) + Sync + Send) { + pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) { // We catch panics here ensuring that all the loop iterations execute. // This makes behavior consistent with the parallel compiler. let mut panic = None; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index b71cdad718a..53ae913f94f 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -29,7 +29,7 @@ use rustc_log::stdout_isatty; use rustc_metadata::locator; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; -use rustc_session::config::{nightly_options, CG_OPTIONS, DB_OPTIONS}; +use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts; @@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks { fn config(&mut self, config: &mut interface::Config) { // If a --prints=... option has been given, we don't print the "total" // time because it will mess up the --prints output. See #64339. - self.time_passes = config.opts.prints.is_empty() - && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time); + self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes(); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -248,8 +247,8 @@ fn run_compiler( let sopts = &compiler.session().opts; if sopts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( - sopts.debugging_opts.no_interleave_lints, - compiler.session().unstable_options(), + sopts.unstable_opts.no_interleave_lints, + compiler.session().enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { @@ -342,7 +341,7 @@ fn run_compiler( return early_exit(); } - if sess.opts.debugging_opts.parse_only || sess.opts.debugging_opts.show_span.is_some() { + if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() { return early_exit(); } @@ -371,13 +370,13 @@ fn run_compiler( queries.global_ctxt()?; - if sess.opts.debugging_opts.no_analysis { + if sess.opts.unstable_opts.no_analysis { return early_exit(); } queries.global_ctxt()?.peek_mut().enter(|tcx| { let result = tcx.analysis(()); - if sess.opts.debugging_opts.save_analysis { + if sess.opts.unstable_opts.save_analysis { let crate_name = queries.crate_name()?.peek().clone(); sess.time("save_analysis", || { save::process_crate( @@ -401,7 +400,7 @@ fn run_compiler( queries.ongoing_codegen()?; - if sess.opts.debugging_opts.print_type_sizes { + if sess.opts.unstable_opts.print_type_sizes { sess.code_stats.print_type_sizes(); } @@ -414,14 +413,14 @@ fn run_compiler( linker.link()? } - if sess.opts.debugging_opts.perf_stats { + if sess.opts.unstable_opts.perf_stats { sess.print_perf_stats(); } - if sess.opts.debugging_opts.print_fuel.is_some() { + if sess.opts.unstable_opts.print_fuel.is_some() { eprintln!( "Fuel used by {}: {}", - sess.opts.debugging_opts.print_fuel.as_ref().unwrap(), + sess.opts.unstable_opts.print_fuel.as_ref().unwrap(), sess.print_fuel.load(SeqCst) ); } @@ -576,7 +575,7 @@ fn show_content_with_pager(content: &str) { } pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { - if sess.opts.debugging_opts.link_only { + if sess.opts.unstable_opts.link_only { if let Input::File(file) = compiler.input() { // FIXME: #![crate_type] and #![crate_name] support not implemented yet sess.init_crate_types(collect_crate_types(sess, &[])); @@ -606,7 +605,7 @@ pub fn list_metadata( metadata_loader: &dyn MetadataLoader, input: &Input, ) -> Compilation { - if sess.opts.debugging_opts.ls { + if sess.opts.unstable_opts.ls { match *input { Input::File(ref ifile) => { let path = &(*ifile); @@ -928,7 +927,7 @@ Available lint options: fn describe_debug_flags() { println!("\nAvailable options:\n"); - print_flag_list("-Z", config::DB_OPTIONS); + print_flag_list("-Z", config::Z_OPTIONS); } fn describe_codegen_flags() { @@ -1000,7 +999,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> { getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS .iter() .map(|&(name, ..)| ('C', name)) - .chain(DB_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) + .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) .find(|&(_, name)| *opt == name.replace('_', "-")) .map(|(flag, _)| format!("{}. Did you mean `-{} {}`?", e, flag, opt)), _ => None, diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index d507293ccb0..854625579ee 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -492,6 +492,7 @@ E0785: include_str!("./error_codes/E0785.md"), E0786: include_str!("./error_codes/E0786.md"), E0787: include_str!("./error_codes/E0787.md"), E0788: include_str!("./error_codes/E0788.md"), +E0790: include_str!("./error_codes/E0790.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -558,7 +559,7 @@ E0788: include_str!("./error_codes/E0788.md"), // E0273, // on_unimplemented #1 // E0274, // on_unimplemented #2 // E0278, // requirement is not satisfied - E0279, // requirement is not satisfied +// E0279, E0280, // requirement is not satisfied // E0285, // overflow evaluation builtin bounds // E0296, // replaced with a generic attribute input check @@ -644,4 +645,5 @@ E0788: include_str!("./error_codes/E0788.md"), // E0721, // `await` keyword // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. + E0789, // rustc_allowed_through_unstable_modules without stability attribute } diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md index 8033aa8384c..cfabae1a634 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0118.md +++ b/compiler/rustc_error_codes/src/error_codes/E0118.md @@ -4,7 +4,7 @@ enum, union, or trait object. Erroneous code example: ```compile_fail,E0118 -impl fn(u8) { // error: no nominal type found for inherent implementation +impl<T> T { // error: no nominal type found for inherent implementation fn get_state(&self) -> String { // ... } @@ -20,8 +20,8 @@ trait LiveLongAndProsper { fn get_state(&self) -> String; } -// and now you can implement it on fn(u8) -impl LiveLongAndProsper for fn(u8) { +// and now you can implement it on T +impl<T> LiveLongAndProsper for T { fn get_state(&self) -> String { "He's dead, Jim!".to_owned() } @@ -33,9 +33,9 @@ For example, `NewType` is a newtype over `Foo` in `struct NewType(Foo)`. Example: ``` -struct TypeWrapper(fn(u8)); +struct TypeWrapper<T>(T); -impl TypeWrapper { +impl<T> TypeWrapper<T> { fn get_state(&self) -> String { "Fascinating!".to_owned() } diff --git a/compiler/rustc_error_codes/src/error_codes/E0283.md b/compiler/rustc_error_codes/src/error_codes/E0283.md index 6885f9a486d..79d2c8204f9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0283.md +++ b/compiler/rustc_error_codes/src/error_codes/E0283.md @@ -3,48 +3,27 @@ An implementation cannot be chosen unambiguously because of lack of information. Erroneous code example: ```compile_fail,E0283 -trait Generator { - fn create() -> u32; -} - -struct Impl; - -impl Generator for Impl { - fn create() -> u32 { 1 } -} - -struct AnotherImpl; +struct Foo; -impl Generator for AnotherImpl { - fn create() -> u32 { 2 } +impl Into<u32> for Foo { + fn into(self) -> u32 { 1 } } -fn main() { - let cont: u32 = Generator::create(); - // error, impossible to choose one of Generator trait implementation - // Should it be Impl or AnotherImpl, maybe something else? -} +let foo = Foo; +let bar: u32 = foo.into() * 1u32; ``` This error can be solved by adding type annotations that provide the missing -information to the compiler. In this case, the solution is to use a concrete -type: +information to the compiler. In this case, the solution is to specify the +trait's type parameter: ``` -trait Generator { - fn create() -> u32; -} - -struct AnotherImpl; +struct Foo; -impl Generator for AnotherImpl { - fn create() -> u32 { 2 } +impl Into<u32> for Foo { + fn into(self) -> u32 { 1 } } -fn main() { - let gen1 = AnotherImpl::create(); - - // if there are multiple methods with same name (different traits) - let gen2 = <AnotherImpl as Generator>::create(); -} +let foo = Foo; +let bar: u32 = Into::<u32>::into(foo) * 1u32; ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0790.md b/compiler/rustc_error_codes/src/error_codes/E0790.md new file mode 100644 index 00000000000..2aee9dfbdbd --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0790.md @@ -0,0 +1,47 @@ +You need to specify a specific implementation of the trait in order to call the +method. + +Erroneous code example: + +```compile_fail,E0790 +trait Generator { + fn create() -> u32; +} + +struct Impl; + +impl Generator for Impl { + fn create() -> u32 { 1 } +} + +struct AnotherImpl; + +impl Generator for AnotherImpl { + fn create() -> u32 { 2 } +} + +let cont: u32 = Generator::create(); +// error, impossible to choose one of Generator trait implementation +// Should it be Impl or AnotherImpl, maybe something else? +``` + +This error can be solved by adding type annotations that provide the missing +information to the compiler. In this case, the solution is to use a concrete +type: + +``` +trait Generator { + fn create() -> u32; +} + +struct AnotherImpl; + +impl Generator for AnotherImpl { + fn create() -> u32 { 2 } +} + +let gen1 = AnotherImpl::create(); + +// if there are multiple methods with same name (different traits) +let gen2 = <AnotherImpl as Generator>::create(); +``` diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl index e7e07093c03..55e96e58e46 100644 --- a/compiler/rustc_error_messages/locales/en-US/lint.ftl +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -247,11 +247,6 @@ lint-atomic-ordering-invalid = `{$method}`'s failure ordering may not be `Releas .label = invalid failure ordering .help = consider using `Acquire` or `Relaxed` failure ordering instead -lint-atomic-ordering-invalid-fail-success = `{$method}`'s success ordering must be at least as strong as its failure ordering - .fail-label = `{$fail_ordering}` failure ordering - .success-label = `{$success_ordering}` success ordering - .suggestion = consider using `{$success_suggestion}` success ordering instead - lint-unused-op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl new file mode 100644 index 00000000000..b17eb9c2d26 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -0,0 +1,264 @@ +-passes-previously-accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +-passes-see-issue = + see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information + +passes-outer-crate-level-attr = + crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + +passes-inner-crate-level-attr = + crate-level attribute should be in the root module + +passes-ignored-attr-with-macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-ignored-attr = `#[{$sym}]` is ignored on struct fields and match arms + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-inline-ignored-function-prototype = `#[inline]` is ignored on function prototypes + +passes-inline-ignored-constants = `#[inline]` is ignored on constants + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "65833")} + +passes-inline-not-fn-or-closure = attribute should be applied to function or closure + .label = not a function or closure + +passes-no-coverage-ignored-function-prototype = `#[no_coverage]` is ignored on function prototypes + +passes-no-coverage-propagate = + `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly + +passes-no-coverage-fn-defn = `#[no_coverage]` may only be applied to function definitions + +passes-no-coverage-not-coverable = `#[no_coverage]` must be applied to coverable code + .label = not coverable code + +passes-should-be-applied-to-fn = attribute should be applied to a function definition + .label = not a function definition + +passes-naked-tracked-caller = cannot use `#[track_caller]` with `#[naked]` + +passes-should-be-applied-to-struct-enum = attribute should be applied to a struct or enum + .label = not a struct or enum + +passes-should-be-applied-to-trait = attribute should be applied to a trait + .label = not a trait + +passes-target-feature-on-statement = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-should-be-applied-to-static = attribute should be applied to a static + .label = not a static + +passes-doc-expect-str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] + +passes-doc-alias-empty = {$attr_str} attribute cannot have empty value + +passes-doc-alias-bad-char = {$char_} character isn't allowed in {$attr_str} + +passes-doc-alias-start-end = {$attr_str} cannot start or end with ' ' + +passes-doc-alias-bad-location = {$attr_str} isn't allowed on {$location} + +passes-doc-alias-not-an-alias = {$attr_str} is the same as the item's name + +passes-doc-alias-duplicated = doc alias is duplicated + .label = first defined here + +passes-doc-alias-not-string-literal = `#[doc(alias("a"))]` expects string literals + +passes-doc-alias-malformed = + doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + +passes-doc-keyword-empty-mod = `#[doc(keyword = "...")]` should be used on empty modules + +passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules + +passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier + +passes-doc-fake-variadic-not-valid = + `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity + +passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks + +passes-doc-inline-conflict-first = this attribute... +passes-doc-inline-conflict-second = ...conflicts with this attribute +passes-doc-inline-conflict = conflicting doc inlining attributes + .help = remove one of the conflicting attributes + +passes-doc-inline-only-use = this attribute can only be applied to a `use` item + .label = only applicable on `use` items + .not-a-use-item-label = not a `use` item + .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information + +passes-doc-attr-not-crate-level = + `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute + +passes-attr-crate-level = this attribute can only be applied at the crate level + .suggestion = to apply to the crate, use an inner attribute + .help = to apply to the crate, use an inner attribute + .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information + +passes-doc-test-unknown = unknown `doc(test)` attribute `{$path}` + +passes-doc-test-takes-list = `#[doc(test(...)]` takes a list of attributes + +passes-doc-primitive = `doc(primitive)` should never have been stable + +passes-doc-test-unknown-any = unknown `doc` attribute `{$path}` + +passes-doc-test-unknown-spotlight = unknown `doc` attribute `{$path}` + .note = `doc(spotlight)` was renamed to `doc(notable_trait)` + .suggestion = use `notable_trait` instead + .no-op-note = `doc(spotlight)` is now a no-op + +passes-doc-test-unknown-include = unknown `doc` attribute `{$path}` + .suggestion = use `doc = include_str!` instead + +passes-doc-invalid = invalid `doc` attribute + +passes-pass-by-value = `pass_by_value` attribute should be applied to a struct, enum or type alias + .label = is not a struct, enum or type alias + +passes-allow-incoherent-impl = + `rustc_allow_incoherent_impl` attribute should be applied to impl items. + .label = the only currently supported targets are inherent methods + +passes-has-incoherent-inherent-impl = + `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits. + .label = only adts, extern types and traits are supported + +passes-must-use-async = + `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within + .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use` + +passes-must-use-no-effect = `#[must_use]` has no effect when applied to {$article} {$target} + +passes-must-not-suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait + .label = is not a struct, enum, or trait + +passes-cold = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-link = attribute should be applied to an `extern` block with non-Rust ABI + .warn = {-passes-previously-accepted} + .label = not an `extern` block + +passes-link-name = attribute should be applied to a foreign function or static + .warn = {-passes-previously-accepted} + .label = not a foreign function or static + .help = try `#[link(name = "{$value}")]` instead + +passes-no-link = attribute should be applied to an `extern crate` item + .label = not an `extern crate` item + +passes-export-name = attribute should be applied to a free function, impl method or static + .label = not a free function, impl method or static + +passes-rustc-layout-scalar-valid-range-not-struct = attribute should be applied to a struct + .label = not a struct + +passes-rustc-layout-scalar-valid-range-arg = expected exactly one integer literal argument + +passes-rustc-legacy-const-generics-only = #[rustc_legacy_const_generics] functions must only have const generics + .label = non-const generic parameter + +passes-rustc-legacy-const-generics-index = #[rustc_legacy_const_generics] must have one index for each generic parameter + .label = generic parameters + +passes-rustc-legacy-const-generics-index-exceed = index exceeds number of arguments + .label = there {$arg_count -> + [one] is + *[other] are + } only {$arg_count} {$arg_count -> + [one] argument + *[other] arguments + } + +passes-rustc-legacy-const-generics-index-negative = arguments should be non-negative integers + +passes-rustc-dirty-clean = attribute requires -Z query-dep-graph to be enabled + +passes-link-section = attribute should be applied to a function or static + .warn = {-passes-previously-accepted} + .label = not a function or static + +passes-no-mangle-foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind} + .warn = {-passes-previously-accepted} + .label = foreign {$foreign_item_kind} + .note = symbol names in extern blocks are not mangled + .suggestion = remove this attribute + +passes-no-mangle = attribute should be applied to a free function, impl method or static + .warn = {-passes-previously-accepted} + .label = not a free function, impl method or static + +passes-repr-ident = meta item in `repr` must be an identifier + +passes-repr-conflicting = conflicting representation hints + +passes-used-static = attribute must be applied to a `static` variable + +passes-used-compiler-linker = `used(compiler)` and `used(linker)` can't be used together + +passes-allow-internal-unstable = attribute should be applied to a macro + .label = not a macro + +passes-debug-visualizer-placement = attribute should be applied to a module + +passes-debug-visualizer-invalid = invalid argument + .note-1 = expected: `natvis_file = "..."` + .note-2 = OR + .note-3 = expected: `gdb_script_file = "..."` + +passes-rustc-allow-const-fn-unstable = attribute should be applied to `const fn` + .label = not a `const fn` + +passes-rustc-std-internal-symbol = attribute should be applied to functions or statics + .label = not a function or static + +passes-const-trait = attribute should be applied to a trait + +passes-stability-promotable = attribute cannot be applied to an expression + +passes-deprecated = attribute is ignored here + +passes-macro-use = `#[{$name}]` only has an effect on `extern crate` and modules + +passes-macro-export = `#[macro_export]` only has an effect on macro definitions + +passes-plugin-registrar = `#[plugin_registrar]` only has an effect on functions + +passes-unused-empty-lints-note = attribute `{$name}` with an empty list has no effect + +passes-unused-no-lints-note = attribute `{$name}` without any lints has no effect + +passes-unused-default-method-body-const-note = + `default_method_body_is_const` has been replaced with `#[const_trait]` on traits + +passes-unused = unused attribute + .suggestion = remove this attribute + +passes-non-exported-macro-invalid-attrs = attribute should be applied to function or closure + .label = not a function or closure + +passes-unused-duplicate = unused attribute + .suggestion = remove this attribute + .note = attribute also specified here + .warn = {-passes-previously-accepted} + +passes-unused-multiple = multiple `{$name}` attributes + .suggestion = remove this attribute + .note = attribute also specified here + +passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct + .label = not a struct + +passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field + .label = not a field diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl index 2b0778f48ca..f8a750da93f 100644 --- a/compiler/rustc_error_messages/locales/en-US/privacy.ftl +++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl @@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface .label = can't leak {$vis_descr} {$kind} .visibility-label = `{$descr}` declared as {$vis_descr} + +privacy-from-private-dep-in-public-interface = + {$kind} `{$descr}` from private dependency '{$krate}' in public interface + +private-in-public-lint = + {$vis_descr} {$kind} `{$descr}` in public interface (error {$kind -> + [trait] E0445 + *[other] E0446 + }) diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index d16171cb162..2ac5c1960cd 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(once_cell)] #![feature(rustc_attrs)] #![feature(type_alias_impl_trait)] @@ -37,6 +37,7 @@ fluent_messages! { expand => "../locales/en-US/expand.ftl", lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", + passes => "../locales/en-US/passes.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", } @@ -298,7 +299,7 @@ impl DiagnosticMessage { /// - If `self` is non-translatable then return `self`'s message. pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self { let attr = match sub { - SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s.clone()), + SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s), SubdiagnosticMessage::FluentIdentifier(id) => { return DiagnosticMessage::FluentIdentifier(id, None); } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index da321c45875..2a4f609a2d8 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -3,7 +3,7 @@ use crate::{ CodeSuggestion, DiagnosticMessage, EmissionGuarantee, Level, LintDiagnosticBuilder, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_error_messages::FluentValue; use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; @@ -40,6 +40,35 @@ pub trait IntoDiagnosticArg { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; } +macro_rules! into_diagnostic_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl IntoDiagnosticArg for $ty { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } + } + )+ + } +} + +into_diagnostic_arg_using_display!( + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + std::num::NonZeroU32, + hir::Target, + Edition, + Ident, +); + impl IntoDiagnosticArg for bool { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { if self { @@ -50,81 +79,9 @@ impl IntoDiagnosticArg for bool { } } -impl IntoDiagnosticArg for i8 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u8 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for String { +impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self)) - } -} - -impl IntoDiagnosticArg for std::num::NonZeroU32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for Edition { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) } } @@ -134,15 +91,15 @@ impl IntoDiagnosticArg for Symbol { } } -impl IntoDiagnosticArg for Ident { +impl<'a> IntoDiagnosticArg for &'a str { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { self.to_string().into_diagnostic_arg() } } -impl<'a> IntoDiagnosticArg for &'a str { +impl IntoDiagnosticArg for String { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() + DiagnosticArgValue::Str(Cow::Owned(self)) } } @@ -433,18 +390,17 @@ impl Diagnostic { expected: DiagnosticStyledString, found: DiagnosticStyledString, ) -> &mut Self { - let mut msg: Vec<_> = - vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)]; + let mut msg: Vec<_> = vec![("required when trying to coerce from type `", Style::NoStyle)]; msg.extend(expected.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight), })); - msg.push(("` to type '".to_string(), Style::NoStyle)); + msg.push(("` to type '", Style::NoStyle)); msg.extend(found.0.iter().map(|x| match *x { - StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), - StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight), })); - msg.push(("`".to_string(), Style::NoStyle)); + msg.push(("`", Style::NoStyle)); // For now, just attach these as notes self.highlighted_note(msg); @@ -496,7 +452,7 @@ impl Diagnostic { self } - pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self { + pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ (format!("`{}` from trait: `", name), Style::NoStyle), (signature, Style::Highlight), diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 99ac6a3546e..9e68ee282e6 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -595,6 +595,7 @@ macro_rules! error_code { pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. pub fn build(mut self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'a, G> { self.0.set_primary_message(msg); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 85ea8eb3937..61d953cd6f1 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -399,11 +399,11 @@ pub trait Emitter { ) { // Check for spans in macros, before `fix_multispans_in_extern_macros` // has a chance to replace them. - let has_macro_spans = iter::once(&*span) + let has_macro_spans: Vec<_> = iter::once(&*span) .chain(children.iter().map(|child| &child.span)) .flat_map(|span| span.primary_spans()) .flat_map(|sp| sp.macro_backtrace()) - .find_map(|expn_data| { + .filter_map(|expn_data| { match expn_data.kind { ExpnKind::Root => None, @@ -413,7 +413,8 @@ pub trait Emitter { ExpnKind::Macro(macro_kind, name) => Some((macro_kind, name)), } - }); + }) + .collect(); if !backtrace { self.fix_multispans_in_extern_macros(source_map, span, children); @@ -422,11 +423,22 @@ pub trait Emitter { self.render_multispans_macro_backtrace(span, children, backtrace); if !backtrace { - if let Some((macro_kind, name)) = has_macro_spans { - let descr = macro_kind.descr(); + if let Some((macro_kind, name)) = has_macro_spans.first() { + // Mark the actual macro this originates from + let and_then = if let Some((macro_kind, last_name)) = has_macro_spans.last() + && last_name != name + { + let descr = macro_kind.descr(); + format!( + " which comes from the expansion of the {descr} `{last_name}`", + ) + } else { + "".to_string() + }; + let descr = macro_kind.descr(); let msg = format!( - "this {level} originates in the {descr} `{name}` \ + "this {level} originates in the {descr} `{name}`{and_then} \ (in Nightly builds, run with -Z macro-backtrace for more info)", ); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index e59a74e380a..b173ac0e916 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -6,6 +6,7 @@ #![feature(drain_filter)] #![feature(backtrace)] #![feature(if_let_guard)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(never_type)] #![feature(adt_const_params)] diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bfe2d773788..6e093811fcf 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -8,11 +8,11 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; -use rustc_lint_defs::BuiltinLintDiagnostics; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics}; use rustc_parse::{self, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session, SessionDiagnostic}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; @@ -985,9 +985,11 @@ pub struct ExtCtxt<'a> { /// Error recovery mode entered when expansion is stuck /// (or during eager expansion, but that's a hack). pub force_mode: bool, - pub expansions: FxHashMap<Span, Vec<String>>, + pub expansions: FxIndexMap<Span, Vec<String>>, /// Used for running pre-expansion lints on freshly loaded modules. pub(super) lint_store: LintStoreExpandDyn<'a>, + /// Used for storing lints generated during expansion, like `NAMED_ARGUMENTS_USED_POSITIONALLY` + pub buffered_early_lint: Vec<BufferedEarlyLint>, /// When we 'expand' an inert attribute, we leave it /// in the AST, but insert it here so that we know /// not to expand it again. @@ -1018,8 +1020,9 @@ impl<'a> ExtCtxt<'a> { is_trailing_mac: false, }, force_mode: false, - expansions: FxHashMap::default(), + expansions: FxIndexMap::default(), expanded_inert_attrs: MarkedAttrs::new(), + buffered_early_lint: vec![], } } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 74e9bbeeeaf..fa3e2a4a5b8 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -520,6 +520,7 @@ impl<'a> ExtCtxt<'a> { self.expr( span, ast::ExprKind::Closure( + ast::ClosureBinder::NotPresent, ast::CaptureBy::Ref, ast::Async::No, ast::Movability::Movable, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3cada372570..ccc29adc015 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -129,7 +129,7 @@ fn get_features( .span_suggestion( mi.span(), "expected just one word", - format!("{}", ident.name), + ident.name, Applicability::MaybeIncorrect, ) .emit(); @@ -171,7 +171,7 @@ fn get_features( continue; } - if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() { + if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() { if allowed.iter().all(|f| name.as_str() != f) { struct_span_err!( span_handler, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 978f87b1d13..93eeca5b289 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -564,7 +564,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { .resolver .visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment); - if self.cx.sess.opts.debugging_opts.incremental_relative_spans { + if self.cx.sess.opts.unstable_opts.incremental_relative_spans { for (invoc, _) in invocations.iter_mut() { let expn_id = invoc.expansion_data.id; let parent_def = self.cx.resolver.invocation_parent(expn_id); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index c18147592dc..20d01b6dc26 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,9 +1,8 @@ -#![allow(rustc::potential_query_instability)] #![feature(array_windows)] #![feature(associated_type_bounds)] #![feature(associated_type_defaults)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(macro_metavar_expr)] #![feature(proc_macro_diagnostic)] diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 3e9ddd6aec0..3d44c408d8f 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -13,7 +13,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_feature::Features; use rustc_lint_defs::builtin::{ @@ -198,7 +198,7 @@ fn macro_rules_dummy_expander<'cx>( DummyResult::any(span) } -fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) { +fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span, message: String) { let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site); cx_expansions.entry(sp).or_default().push(message); } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index d4b8563a036..707cb73f097 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -234,6 +234,8 @@ fn parse_tree( sess, &Token { kind: token::Dollar, span }, ); + } else { + maybe_emit_macro_metavar_expr_feature(features, sess, span); } TokenTree::token(token::Dollar, span) } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index fdd8dc93fc1..3037855ae28 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -512,7 +512,18 @@ fn out_of_bounds_err<'a>( span: Span, ty: &str, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - cx.struct_span_err(span, &format!("{ty} depth must be less than {max}")) + let msg = if max == 0 { + format!( + "meta-variable expression `{ty}` with depth parameter \ + must be called inside of a macro repetition" + ) + } else { + format!( + "depth parameter on meta-variable expression `{ty}` \ + must be less than {max}" + ) + }; + cx.struct_span_err(span, &msg) } fn transcribe_metavar_expr<'a>( diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 876faad33b6..0315d11634c 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -218,10 +218,9 @@ pub fn default_submod_path<'a>( "" }; - let mod_name = ident.name.to_string(); - let default_path_str = format!("{}{}.rs", relative_prefix, mod_name); + let default_path_str = format!("{}{}.rs", relative_prefix, ident.name); let secondary_path_str = - format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); + format!("{}{}{}mod.rs", relative_prefix, ident.name, path::MAIN_SEPARATOR); let default_path = dir_path.join(&default_path_str); let secondary_path = dir_path.join(&secondary_path_str); let default_exists = sess.source_map().file_exists(&default_path); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 411d6be9c44..176c77ca6ed 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -11,13 +11,14 @@ use rustc_parse::lexer::nfc_normalize; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; -use rustc_span::symbol::{self, kw, sym, Symbol}; +use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; -use pm::bridge::{server, DelimSpan, ExpnGlobals, Group, Punct, TokenTree}; +use pm::bridge::{ + server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, +}; use pm::{Delimiter, Level, LineColumn}; use std::ops::Bound; -use std::{ascii, panic}; trait FromInternal<T> { fn from_internal(x: T) -> Self; @@ -49,9 +50,40 @@ impl ToInternal<token::Delimiter> for Delimiter { } } -impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> - for Vec<TokenTree<TokenStream, Span, Ident, Literal>> -{ +impl FromInternal<token::LitKind> for LitKind { + fn from_internal(kind: token::LitKind) -> Self { + match kind { + token::Byte => LitKind::Byte, + token::Char => LitKind::Char, + token::Integer => LitKind::Integer, + token::Float => LitKind::Float, + token::Str => LitKind::Str, + token::StrRaw(n) => LitKind::StrRaw(n), + token::ByteStr => LitKind::ByteStr, + token::ByteStrRaw(n) => LitKind::ByteStrRaw(n), + token::Err => LitKind::Err, + token::Bool => unreachable!(), + } + } +} + +impl ToInternal<token::LitKind> for LitKind { + fn to_internal(self) -> token::LitKind { + match self { + LitKind::Byte => token::Byte, + LitKind::Char => token::Char, + LitKind::Integer => token::Integer, + LitKind::Float => token::Float, + LitKind::Str => token::Str, + LitKind::StrRaw(n) => token::StrRaw(n), + LitKind::ByteStr => token::ByteStr, + LitKind::ByteStrRaw(n) => token::ByteStrRaw(n), + LitKind::Err => token::Err, + } + } +} + +impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStream, Span, Symbol>> { fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { use rustc_ast::token::*; @@ -135,16 +167,22 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> Question => op("?"), SingleQuote => op("'"), - Ident(name, false) if name == kw::DollarCrate => trees.push(TokenTree::Ident(Ident::dollar_crate(span))), - Ident(name, is_raw) => trees.push(TokenTree::Ident(Ident::new(rustc.sess(), name, is_raw, span))), + Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident { sym, is_raw, span })), Lifetime(name) => { let ident = symbol::Ident::new(name, span).without_first_quote(); trees.extend([ TokenTree::Punct(Punct { ch: b'\'', joint: true, span }), - TokenTree::Ident(Ident::new(rustc.sess(), ident.name, false, span)), + TokenTree::Ident(Ident { sym: ident.name, is_raw: false, span }), ]); } - Literal(lit) => trees.push(TokenTree::Literal(self::Literal { lit, span })), + Literal(token::Lit { kind, symbol, suffix }) => { + trees.push(TokenTree::Literal(self::Literal { + kind: FromInternal::from_internal(kind), + symbol, + suffix, + span, + })); + } DocComment(_, attr_style, data) => { let mut escaped = String::new(); for ch in data.as_str().chars() { @@ -170,7 +208,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> } Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => { - trees.push(TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span))) + trees.push(TokenTree::Ident(Ident { sym: ident.name, is_raw, span: ident.span })) } Interpolated(nt) => { @@ -200,11 +238,12 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> } } -impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> { +impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rustc<'_, '_>) { fn to_internal(self) -> TokenStream { use rustc_ast::token::*; - let (ch, joint, span) = match self { + let (tree, rustc) = self; + let (ch, joint, span) = match tree { TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), TokenTree::Group(Group { delimiter, stream, span: DelimSpan { open, close, .. } }) => { return tokenstream::TokenTree::Delimited( @@ -215,10 +254,13 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> { .into(); } TokenTree::Ident(self::Ident { sym, is_raw, span }) => { + rustc.sess().symbol_gallery.insert(sym, span); return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); } TokenTree::Literal(self::Literal { - lit: token::Lit { kind: token::Integer, symbol, suffix }, + kind: self::LitKind::Integer, + symbol, + suffix, span, }) if symbol.as_str().starts_with('-') => { let minus = BinOp(BinOpToken::Minus); @@ -229,7 +271,9 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> { return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { - lit: token::Lit { kind: token::Float, symbol, suffix }, + kind: self::LitKind::Float, + symbol, + suffix, span, }) if symbol.as_str().starts_with('-') => { let minus = BinOp(BinOpToken::Minus); @@ -239,8 +283,12 @@ impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> { let b = tokenstream::TokenTree::token(float, span); return [a, b].into_iter().collect(); } - TokenTree::Literal(self::Literal { lit, span }) => { - return tokenstream::TokenTree::token(Literal(lit), span).into(); + TokenTree::Literal(self::Literal { kind, symbol, suffix, span }) => { + return tokenstream::TokenTree::token( + TokenKind::lit(kind.to_internal(), symbol, suffix), + span, + ) + .into(); } }; @@ -289,40 +337,6 @@ impl ToInternal<rustc_errors::Level> for Level { pub struct FreeFunctions; -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Ident { - sym: Symbol, - is_raw: bool, - span: Span, -} - -impl Ident { - fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { - let sym = nfc_normalize(sym.as_str()); - let string = sym.as_str(); - if !rustc_lexer::is_ident(string) { - panic!("`{:?}` is not a valid identifier", string) - } - if is_raw && !sym.can_be_raw() { - panic!("`{}` cannot be a raw identifier", string); - } - sess.symbol_gallery.insert(sym, span); - Ident { sym, is_raw, span } - } - - fn dollar_crate(span: Span) -> Ident { - // `$crate` is accepted as an ident only if it comes from the compiler. - Ident { sym: kw::DollarCrate, is_raw: false, span } - } -} - -// FIXME(eddyb) `Literal` should not expose internal `Debug` impls. -#[derive(Clone, Debug)] -pub struct Literal { - lit: token::Lit, - span: Span, -} - pub(crate) struct Rustc<'a, 'b> { ecx: &'a mut ExtCtxt<'b>, def_site: Span, @@ -348,21 +362,16 @@ impl<'a, 'b> Rustc<'a, 'b> { fn sess(&self) -> &ParseSess { self.ecx.parse_sess() } - - fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal { - Literal { lit: token::Lit::new(kind, symbol, suffix), span: self.call_site } - } } impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; - type Ident = Ident; - type Literal = Literal; type SourceFile = Lrc<SourceFile>; type MultiSpan = Vec<Span>; type Diagnostic = Diagnostic; type Span = Span; + type Symbol = Symbol; } impl server::FreeFunctions for Rustc<'_, '_> { @@ -376,6 +385,57 @@ impl server::FreeFunctions for Rustc<'_, '_> { fn track_path(&mut self, path: &str) { self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path)); } + + fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> { + let name = FileName::proc_macro_source_code(s); + let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned()); + + let first_span = parser.token.span.data(); + let minus_present = parser.eat(&token::BinOp(token::Minus)); + + let lit_span = parser.token.span.data(); + let token::Literal(mut lit) = parser.token.kind else { + return Err(()); + }; + + // Check no comment or whitespace surrounding the (possibly negative) + // literal, or more tokens after it. + if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() { + return Err(()); + } + + if minus_present { + // If minus is present, check no comment or whitespace in between it + // and the literal token. + if first_span.hi.0 != lit_span.lo.0 { + return Err(()); + } + + // Check literal is a kind we allow to be negated in a proc macro token. + match lit.kind { + token::LitKind::Bool + | token::LitKind::Byte + | token::LitKind::Char + | token::LitKind::Str + | token::LitKind::StrRaw(_) + | token::LitKind::ByteStr + | token::LitKind::ByteStrRaw(_) + | token::LitKind::Err => return Err(()), + token::LitKind::Integer | token::LitKind::Float => {} + } + + // Synthesize a new symbol that includes the minus sign. + let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]); + lit = token::Lit::new(lit.kind, symbol, lit.suffix); + } + let token::Lit { kind, symbol, suffix } = lit; + Ok(Literal { + kind: FromInternal::from_internal(kind), + symbol, + suffix, + span: self.call_site, + }) + } } impl server::TokenStream for Rustc<'_, '_> { @@ -453,22 +513,22 @@ impl server::TokenStream for Rustc<'_, '_> { fn from_token_tree( &mut self, - tree: TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>, + tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol>, ) -> Self::TokenStream { - tree.to_internal() + (tree, &mut *self).to_internal() } fn concat_trees( &mut self, base: Option<Self::TokenStream>, - trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>>, + trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>, ) -> Self::TokenStream { let mut builder = tokenstream::TokenStreamBuilder::new(); if let Some(base) = base { builder.push(base); } for tree in trees { - builder.push(tree.to_internal()); + builder.push((tree, &mut *self).to_internal()); } builder.build() } @@ -491,178 +551,11 @@ impl server::TokenStream for Rustc<'_, '_> { fn into_trees( &mut self, stream: Self::TokenStream, - ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>> { + ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> { FromInternal::from_internal((stream, self)) } } -impl server::Ident for Rustc<'_, '_> { - fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(self.sess(), Symbol::intern(string), is_raw, span) - } - - fn span(&mut self, ident: Self::Ident) -> Self::Span { - ident.span - } - - fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { - Ident { span, ..ident } - } -} - -impl server::Literal for Rustc<'_, '_> { - fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> { - let name = FileName::proc_macro_source_code(s); - let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned()); - - let first_span = parser.token.span.data(); - let minus_present = parser.eat(&token::BinOp(token::Minus)); - - let lit_span = parser.token.span.data(); - let token::Literal(mut lit) = parser.token.kind else { - return Err(()); - }; - - // Check no comment or whitespace surrounding the (possibly negative) - // literal, or more tokens after it. - if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() { - return Err(()); - } - - if minus_present { - // If minus is present, check no comment or whitespace in between it - // and the literal token. - if first_span.hi.0 != lit_span.lo.0 { - return Err(()); - } - - // Check literal is a kind we allow to be negated in a proc macro token. - match lit.kind { - token::LitKind::Bool - | token::LitKind::Byte - | token::LitKind::Char - | token::LitKind::Str - | token::LitKind::StrRaw(_) - | token::LitKind::ByteStr - | token::LitKind::ByteStrRaw(_) - | token::LitKind::Err => return Err(()), - token::LitKind::Integer | token::LitKind::Float => {} - } - - // Synthesize a new symbol that includes the minus sign. - let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]); - lit = token::Lit::new(lit.kind, symbol, lit.suffix); - } - - Ok(Literal { lit, span: self.call_site }) - } - - fn to_string(&mut self, literal: &Self::Literal) -> String { - literal.lit.to_string() - } - - fn debug_kind(&mut self, literal: &Self::Literal) -> String { - format!("{:?}", literal.lit.kind) - } - - fn symbol(&mut self, literal: &Self::Literal) -> String { - literal.lit.symbol.to_string() - } - - fn suffix(&mut self, literal: &Self::Literal) -> Option<String> { - literal.lit.suffix.as_ref().map(Symbol::to_string) - } - - fn integer(&mut self, n: &str) -> Self::Literal { - self.lit(token::Integer, Symbol::intern(n), None) - } - - fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { - self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind))) - } - - fn float(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), None) - } - - fn f32(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), Some(sym::f32)) - } - - fn f64(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), Some(sym::f64)) - } - - fn string(&mut self, string: &str) -> Self::Literal { - let quoted = format!("{:?}", string); - assert!(quoted.starts_with('"') && quoted.ends_with('"')); - let symbol = "ed[1..quoted.len() - 1]; - self.lit(token::Str, Symbol::intern(symbol), None) - } - - fn character(&mut self, ch: char) -> Self::Literal { - let quoted = format!("{:?}", ch); - assert!(quoted.starts_with('\'') && quoted.ends_with('\'')); - let symbol = "ed[1..quoted.len() - 1]; - self.lit(token::Char, Symbol::intern(symbol), None) - } - - fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { - let string = bytes - .iter() - .cloned() - .flat_map(ascii::escape_default) - .map(Into::<char>::into) - .collect::<String>(); - self.lit(token::ByteStr, Symbol::intern(&string), None) - } - - fn span(&mut self, literal: &Self::Literal) -> Self::Span { - literal.span - } - - fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { - literal.span = span; - } - - fn subspan( - &mut self, - literal: &Self::Literal, - start: Bound<usize>, - end: Bound<usize>, - ) -> Option<Self::Span> { - let span = literal.span; - let length = span.hi().to_usize() - span.lo().to_usize(); - - let start = match start { - Bound::Included(lo) => lo, - Bound::Excluded(lo) => lo.checked_add(1)?, - Bound::Unbounded => 0, - }; - - let end = match end { - Bound::Included(hi) => hi.checked_add(1)?, - Bound::Excluded(hi) => hi, - Bound::Unbounded => length, - }; - - // Bounds check the values, preventing addition overflow and OOB spans. - if start > u32::MAX as usize - || end > u32::MAX as usize - || (u32::MAX - start as u32) < span.lo().to_u32() - || (u32::MAX - end as u32) < span.lo().to_u32() - || start >= end - || end > length - { - return None; - } - - let new_lo = span.lo() + BytePos::from_usize(start); - let new_hi = span.lo() + BytePos::from_usize(end); - Some(span.with_lo(new_lo).with_hi(new_hi)) - } -} - impl server::SourceFile for Rustc<'_, '_> { fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { Lrc::ptr_eq(file1, file2) @@ -767,6 +660,42 @@ impl server::Span for Rustc<'_, '_> { Some(first.to(second)) } + fn subspan( + &mut self, + span: Self::Span, + start: Bound<usize>, + end: Bound<usize>, + ) -> Option<Self::Span> { + let length = span.hi().to_usize() - span.lo().to_usize(); + + let start = match start { + Bound::Included(lo) => lo, + Bound::Excluded(lo) => lo.checked_add(1)?, + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(hi) => hi.checked_add(1)?, + Bound::Excluded(hi) => hi, + Bound::Unbounded => length, + }; + + // Bounds check the values, preventing addition overflow and OOB spans. + if start > u32::MAX as usize + || end > u32::MAX as usize + || (u32::MAX - start as u32) < span.lo().to_u32() + || (u32::MAX - end as u32) < span.lo().to_u32() + || start >= end + || end > length + { + return None; + } + + let new_lo = span.lo() + BytePos::from_usize(start); + let new_hi = span.lo() + BytePos::from_usize(end); + Some(span.with_lo(new_lo).with_hi(new_hi)) + } + fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { span.with_ctxt(at.ctxt()) } @@ -812,6 +741,13 @@ impl server::Span for Rustc<'_, '_> { } } +impl server::Symbol for Rustc<'_, '_> { + fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> { + let sym = nfc_normalize(string); + if rustc_lexer::is_ident(sym.as_str()) { Ok(sym) } else { Err(()) } + } +} + impl server::Server for Rustc<'_, '_> { fn globals(&mut self) -> ExpnGlobals<Self::Span> { ExpnGlobals { @@ -820,4 +756,12 @@ impl server::Server for Rustc<'_, '_> { mixed_site: self.mixed_site, } } + + fn intern_symbol(string: &str) -> Self::Symbol { + Symbol::intern(string) + } + + fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { + f(&symbol.as_str()) + } } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 099c40b215d..2d24101b2d5 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -186,6 +186,8 @@ declare_features! ( /// Allows some increased flexibility in the name resolution rules, /// especially around globs and shadowing (RFC 1560). (accepted, item_like_imports, "1.15.0", Some(35120), None), + /// Allows `if/while p && let q = r && ...` chains. + (accepted, let_chains, "1.64.0", Some(53667), None), /// Allows `break {expr}` with a value inside `loop`s. (accepted, loop_break_value, "1.19.0", Some(37339), None), /// Allows use of `?` as the Kleene "at most one" operator in macros. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index b54f0ef361a..1018facebae 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -148,6 +148,8 @@ declare_features! ( /// below (it has to be checked before expansion possibly makes /// macros disappear). (active, allow_internal_unstable, "1.0.0", None, None), + /// Allows using anonymous lifetimes in argument-position impl-trait. + (active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None), /// Allows identifying the `compiler_builtins` crate. (active, compiler_builtins, "1.13.0", None, None), /// Outputs useful `assert!` messages @@ -156,9 +158,6 @@ declare_features! ( (active, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. (active, lang_items, "1.0.0", None, None), - /// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`, - /// it is not on path for eventual stabilization). - (active, no_niche, "1.42.0", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. @@ -331,6 +330,8 @@ declare_features! ( (active, cfg_target_thread_local, "1.7.0", Some(29594), None), /// Allow conditional compilation depending on rust version (active, cfg_version, "1.45.0", Some(64796), None), + /// Allows `for<...>` on closures and generators. + (active, closure_lifetime_binder, "1.64.0", Some(97362), None), /// Allows `#[track_caller]` on closures and generators. (active, closure_track_caller, "1.57.0", Some(87417), None), /// Allows to use the `#[cmse_nonsecure_entry]` attribute. @@ -423,8 +424,6 @@ declare_features! ( (active, label_break_value, "1.28.0", Some(48594), None), // Allows setting the threshold for the `large_assignments` lint. (active, large_assignments, "1.52.0", Some(83518), None), - /// Allows `if/while p && let q = r && ...` chains. - (active, let_chains, "1.37.0", Some(53667), None), /// Allows `let...else` statements. (active, let_else, "1.56.0", Some(87335), None), /// Allows `#[link(..., cfg(..))]`. @@ -528,13 +527,6 @@ declare_features! ( (incomplete, unsized_locals, "1.30.0", Some(48055), None), /// Allows unsized tuple coercion. (active, unsized_tuple_coercion, "1.20.0", Some(42877), None), - /// Allows `union`s to implement `Drop`. Moreover, `union`s may now include fields - /// that don't implement `Copy` as long as they don't have any drop glue. - /// This is checked recursively. On encountering type variable where no progress can be made, - /// `T: Copy` is used as a substitute for "no drop glue". - /// - /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0. - (active, untagged_unions, "1.13.0", Some(55149), None), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. (active, used_with_arg, "1.60.0", Some(93798), None), /// Allows `extern "wasm" fn` diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6fcdfe44d8f..0e73d8fd7f6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -512,6 +512,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), + rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths"), // ========================================================================== // Internal attributes: Type system related: @@ -529,6 +532,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), gated!( alloc_error_handler, Normal, template!(Word), WarnFollowing, experimental!(alloc_error_handler) @@ -613,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 54626caaf53..2ddaf920109 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -180,6 +180,9 @@ declare_features! ( /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None, None), (removed, unsafe_no_drop_flag, "1.0.0", None, None, None), + /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue. + (removed, untagged_unions, "1.13.0", Some(55149), None, + Some("unions with `Copy` and `ManuallyDrop` fields are stable; there is no intent to stabilize more")), /// Allows `#[unwind(..)]`. /// /// Permits specifying whether a function should permit unwinding or abort on unwind. diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index a6d10f3adae..44335b7f42e 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -12,6 +12,7 @@ macro_rules! arena_types { [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, rustc_span::Span), [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, + [] closure: rustc_hir::Closure<'tcx>, [] block: rustc_hir::Block<'tcx>, [] bare_fn_ty: rustc_hir::BareFnTy<'tcx>, [] body: rustc_hir::Body<'tcx>, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index d0893cd09d8..be5b7eccbaf 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -713,7 +713,7 @@ impl<Id> Res<Id> { } /// Resolution for a lifetime appearing in a type. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum LifetimeRes { /// Successfully linked the lifetime to a generic parameter. Param { @@ -738,13 +738,8 @@ pub enum LifetimeRes { binder: NodeId, }, /// This variant is used for anonymous lifetimes that we did not resolve during - /// late resolution. Shifting the work to the HIR lifetime resolver. - Anonymous { - /// Id of the introducing place. See `Param`. - binder: NodeId, - /// Whether this lifetime was spelled or elided. - elided: bool, - }, + /// late resolution. Those lifetimes will be inferred by typechecking. + Infer, /// Explicit `'static` lifetime. Static, /// Resolution failure. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 04f585df34c..f71400898e6 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -90,9 +90,6 @@ pub enum LifetimeName { /// User-given names or fresh (synthetic) names. Param(LocalDefId, ParamName), - /// User wrote nothing (e.g., the lifetime in `&u32`). - Implicit, - /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the /// lifetime that they default to must appear elsewhere within the @@ -110,8 +107,9 @@ pub enum LifetimeName { /// that was already reported. Error, - /// User wrote specifies `'_`. - Underscore, + /// User wrote an anonymous lifetime, either `'_` or nothing. + /// The semantics of this lifetime should be inferred by typechecking code. + Infer, /// User wrote `'static`. Static, @@ -120,10 +118,8 @@ pub enum LifetimeName { impl LifetimeName { pub fn ident(&self) -> Ident { match *self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Error => Ident::empty(), - LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), + LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(_, param_name) => param_name.ident(), } @@ -132,8 +128,7 @@ impl LifetimeName { pub fn is_anonymous(&self) -> bool { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore + | LifetimeName::Infer | LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Error => true, LifetimeName::Static | LifetimeName::Param(..) => false, @@ -142,9 +137,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore => true, + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, // It might seem surprising that `Fresh` counts as // *not* elided -- but this is because, as far as the code @@ -922,6 +915,17 @@ pub struct Crate<'hir> { pub hir_hash: Fingerprint, } +#[derive(Debug, HashStable_Generic)] +pub struct Closure<'hir> { + pub binder: ClosureBinder, + pub capture_clause: CaptureBy, + pub bound_generic_params: &'hir [GenericParam<'hir>], + pub fn_decl: &'hir FnDecl<'hir>, + pub body: BodyId, + pub fn_decl_span: Span, + pub movability: Option<Movability>, +} + /// A block of statements `{ .. }`, which may have a label (in this case the /// `targeted_by_break` field will be `true`) and may be `unsafe` by means of /// the `rules` being anything but `DefaultBlock`. @@ -943,6 +947,16 @@ pub struct Block<'hir> { pub targeted_by_break: bool, } +impl<'hir> Block<'hir> { + pub fn innermost_block(&self) -> &Block<'hir> { + let mut block = self; + while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr { + block = inner_block; + } + block + } +} + #[derive(Debug, HashStable_Generic)] pub struct Pat<'hir> { #[stable_hasher(ignore)] @@ -1316,6 +1330,8 @@ pub struct Local<'hir> { pub ty: Option<&'hir Ty<'hir>>, /// Initializer expression to set the value, if any. pub init: Option<&'hir Expr<'hir>>, + /// Else block for a `let...else` binding. + pub els: Option<&'hir Block<'hir>>, pub hir_id: HirId, pub span: Span, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop @@ -1811,6 +1827,14 @@ impl Expr<'_> { _ => false, } } + + pub fn method_ident(&self) -> Option<Ident> { + match self.kind { + ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident), + ExprKind::Unary(_, expr) | ExprKind::AddrOf(.., expr) => expr.method_ident(), + _ => None, + } + } } /// Checks if the specified expression is a built-in range literal. @@ -1913,14 +1937,7 @@ pub enum ExprKind<'hir> { /// /// This may also be a generator literal or an `async block` as indicated by the /// `Option<Movability>`. - Closure { - capture_clause: CaptureBy, - bound_generic_params: &'hir [GenericParam<'hir>], - fn_decl: &'hir FnDecl<'hir>, - body: BodyId, - fn_decl_span: Span, - movability: Option<Movability>, - }, + Closure(&'hir Closure<'hir>), /// A block (e.g., `'label: { ... }`). Block(&'hir Block<'hir>, Option<Label>), @@ -2698,6 +2715,17 @@ impl FnRetTy<'_> { } } +/// Represents `for<...>` binder before a closure +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub enum ClosureBinder { + /// Binder is not specified. + Default, + /// Binder is specified. + /// + /// Span points to the whole `for<...>`. + For { span: Span }, +} + #[derive(Encodable, Debug, HashStable_Generic)] pub struct Mod<'hir> { pub spans: ModSpans, @@ -3462,7 +3490,7 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { rustc_data_structures::static_assert_size!(super::Block<'static>, 48); - rustc_data_structures::static_assert_size!(super::Expr<'static>, 64); + rustc_data_structures::static_assert_size!(super::Expr<'static>, 56); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 531d9f14040..640974115b9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -472,6 +472,9 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { walk_list!(visitor, visit_expr, &local.init); visitor.visit_id(local.hir_id); visitor.visit_pat(&local.pat); + if let Some(els) = local.els { + visitor.visit_block(els); + } walk_list!(visitor, visit_ty, &local.ty); } @@ -493,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Param(_, ParamName::Error) | LifetimeName::Static | LifetimeName::Error - | LifetimeName::Implicit | LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Underscore => {} + | LifetimeName::Infer => {} } } @@ -925,7 +927,7 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' FnKind::ItemFn(_, generics, ..) => { visitor.visit_generics(generics); } - FnKind::Method(..) | FnKind::Closure => {} + FnKind::Closure | FnKind::Method(..) => {} } } @@ -1144,14 +1146,15 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(subexpression); walk_list!(visitor, visit_arm, arms); } - ExprKind::Closure { + ExprKind::Closure(&Closure { + binder: _, bound_generic_params, - ref fn_decl, + fn_decl, body, capture_clause: _, fn_decl_span: _, movability: _, - } => { + }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id) } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b0bfac8e1f5..13b3e954e1f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -11,7 +11,7 @@ use crate::def_id::DefId; use crate::{MethodKind, Target}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable_Generic; use rustc_span::symbol::{kw, sym, Symbol}; @@ -134,8 +134,8 @@ macro_rules! language_item_table { } /// A mapping from the name of the lang item to its order and the form it must be of. - pub static ITEM_REFS: LazyLock<FxHashMap<Symbol, (usize, Target)>> = LazyLock::new(|| { - let mut item_refs = FxHashMap::default(); + pub static ITEM_REFS: LazyLock<FxIndexMap<Symbol, (usize, Target)>> = LazyLock::new(|| { + let mut item_refs = FxIndexMap::default(); $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )* item_refs }); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index b30076100bb..93112199b60 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,7 +1,7 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::DefId; use crate::hir::{self, HirId, PatKind}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::Ident; use rustc_span::Span; diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index dad22725511..b6a85c0472e 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -4,7 +4,7 @@ use crate::def_id::DefId; use crate::{lang_items, LangItem, LanguageItems}; use rustc_ast as ast; -use rustc_data_structures::stable_map::StableMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_span::symbol::{sym, Symbol}; use std::sync::LazyLock; @@ -12,8 +12,8 @@ use std::sync::LazyLock; macro_rules! weak_lang_items { ($($name:ident, $item:ident, $sym:ident;)*) => ( -pub static WEAK_ITEMS_REFS: LazyLock<StableMap<Symbol, LangItem>> = LazyLock::new(|| { - let mut map = StableMap::default(); +pub static WEAK_ITEMS_REFS: LazyLock<FxIndexMap<Symbol, LangItem>> = LazyLock::new(|| { + let mut map = FxIndexMap::default(); $(map.insert(sym::$name, LangItem::$item);)* map }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 50acb0270b0..e0179bd3ed1 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -6,6 +6,7 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; +use rustc_hir::LifetimeParamKind; use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term}; use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; use rustc_span::source_map::SourceMap; @@ -210,6 +211,10 @@ pub fn path_to_string(segment: &hir::Path<'_>) -> String { to_string(NO_ANN, |s| s.print_path(segment, false)) } +pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String { + to_string(NO_ANN, |s| s.print_qpath(segment, false)) +} + pub fn fn_to_string( decl: &hir::FnDecl<'_>, header: hir::FnHeader, @@ -883,7 +888,12 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::SubItem(ii.hir_id())) } - pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) { + pub fn print_local( + &mut self, + init: Option<&hir::Expr<'_>>, + els: Option<&hir::Block<'_>>, + decl: impl Fn(&mut Self), + ) { self.space_if_not_bol(); self.ibox(INDENT_UNIT); self.word_nbsp("let"); @@ -897,6 +907,13 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(init); } + + if let Some(els) = els { + self.nbsp(); + self.word_space("else"); + self.print_block(els); + } + self.end() } @@ -904,7 +921,7 @@ impl<'a> State<'a> { self.maybe_print_comment(st.span.lo()); match st.kind { hir::StmtKind::Local(loc) => { - self.print_local(loc.init, |this| this.print_local_decl(loc)); + self.print_local(loc.init, loc.els, |this| this.print_local_decl(loc)); } hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)), hir::StmtKind::Expr(expr) => { @@ -1404,7 +1421,7 @@ impl<'a> State<'a> { // Print `let _t = $init;`: let temp = Ident::from_str("_t"); - self.print_local(Some(init), |this| this.print_ident(temp)); + self.print_local(Some(init), None, |this| this.print_ident(temp)); self.word(";"); // Print `_t`: @@ -1440,15 +1457,16 @@ impl<'a> State<'a> { } self.bclose(expr.span); } - hir::ExprKind::Closure { + hir::ExprKind::Closure(&hir::Closure { + binder, capture_clause, bound_generic_params, fn_decl, body, fn_decl_span: _, movability: _, - } => { - self.print_formal_generic_params(bound_generic_params); + }) => { + self.print_closure_binder(binder, bound_generic_params); self.print_capture_clause(capture_clause); self.print_closure_params(fn_decl, body); @@ -2033,6 +2051,42 @@ impl<'a> State<'a> { } } + pub fn print_closure_binder( + &mut self, + binder: hir::ClosureBinder, + generic_params: &[GenericParam<'_>], + ) { + let generic_params = generic_params + .iter() + .filter(|p| { + matches!( + p, + GenericParam { + kind: GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit }, + .. + } + ) + }) + .collect::<Vec<_>>(); + + match binder { + hir::ClosureBinder::Default => {} + // we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions + hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"), + hir::ClosureBinder::For { .. } => { + self.word("for"); + self.word("<"); + + self.commasep(Inconsistent, &generic_params, |s, param| { + s.print_generic_param(param) + }); + + self.word(">"); + self.nbsp(); + } + } + } + pub fn print_bounds<'b>( &mut self, prefix: &'static str, diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 93528b4514b..69e482ce854 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -55,11 +55,11 @@ use std::io::{BufWriter, Write}; #[allow(missing_docs)] pub fn assert_dep_graph(tcx: TyCtxt<'_>) { tcx.dep_graph.with_ignore(|| { - if tcx.sess.opts.debugging_opts.dump_dep_graph { + if tcx.sess.opts.unstable_opts.dump_dep_graph { tcx.dep_graph.with_query(dump_graph); } - if !tcx.sess.opts.debugging_opts.query_dep_graph { + if !tcx.sess.opts.unstable_opts.query_dep_graph { return; } @@ -81,7 +81,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) { if !if_this_changed.is_empty() || !then_this_would_need.is_empty() { assert!( - tcx.sess.opts.debugging_opts.query_dep_graph, + tcx.sess.opts.unstable_opts.query_dep_graph, "cannot use the `#[{}]` or `#[{}]` annotations \ without supplying `-Z query-dep-graph`", sym::rustc_if_this_changed, diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 61b1dd8cb01..89d419bc8e9 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -23,7 +23,7 @@ //! was re-used. use rustc_ast as ast; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; @@ -76,7 +76,7 @@ impl<'tcx> AssertModuleSource<'tcx> { return; }; - if !self.tcx.sess.opts.debugging_opts.query_dep_graph { + if !self.tcx.sess.opts.unstable_opts.query_dep_graph { self.tcx.sess.span_fatal( attr.span, "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified", diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 94097357f8c..35a278e6c92 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -134,7 +134,7 @@ struct Assertion { } pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { - if !tcx.sess.opts.debugging_opts.query_dep_graph { + if !tcx.sess.opts.unstable_opts.query_dep_graph { return; } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 9b328842056..25c1b2e1c43 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -447,7 +447,7 @@ fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result<bo } } - if sess.opts.debugging_opts.incremental_info { + if sess.opts.unstable_opts.incremental_info { eprintln!( "[incremental] session directory: \ {} files hard-linked", diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index f59d8d596b9..1c5fd916902 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -141,7 +141,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. let path = dep_graph_path(&sess); - let report_incremental_info = sess.opts.debugging_opts.incremental_info; + let report_incremental_info = sess.opts.unstable_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false); let mut prev_work_products = FxHashMap::default(); @@ -163,7 +163,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { for swp in work_products { let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| { let exists = in_incr_comp_dir_sess(sess, path).exists(); - if !exists && sess.opts.debugging_opts.incremental_info { + if !exists && sess.opts.unstable_opts.incremental_info { eprintln!("incremental: could not find file for work product: {path}",); } exists @@ -225,7 +225,7 @@ pub fn load_query_result_cache<'a, C: OnDiskCache<'a>>(sess: &'a Session) -> Opt let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); match load_data( - sess.opts.debugging_opts.incremental_info, + sess.opts.unstable_opts.incremental_info, &query_cache_path(sess), sess.is_nightly_build(), ) { diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 4059b7cfc8e..71035031497 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -39,7 +39,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx)); sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx)); - if sess.opts.debugging_opts.incremental_info { + if sess.opts.unstable_opts.incremental_info { tcx.dep_graph.print_incremental_info() } @@ -182,7 +182,7 @@ pub fn build_dep_graph( prev_graph, prev_work_products, encoder, - sess.opts.debugging_opts.query_dep_graph, - sess.opts.debugging_opts.incremental_info, + sess.opts.unstable_opts.query_dep_graph, + sess.opts.unstable_opts.incremental_info, )) } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 1b184eca964..2f1853c441e 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -3,7 +3,7 @@ //! [work products]: WorkProduct use crate::persist::fs::*; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 1a55519d7b1..30ff364210d 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -234,7 +234,9 @@ impl<I: Idx, T> IndexVec<I, T> { self.raw.get_mut(index.index()) } - /// Returns mutable references to two distinct elements, a and b. Panics if a == b. + /// Returns mutable references to two distinct elements, `a` and `b`. + /// + /// Panics if `a == b`. #[inline] pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) { let (ai, bi) = (a.index(), b.index()); @@ -249,7 +251,9 @@ impl<I: Idx, T> IndexVec<I, T> { } } - /// Returns mutable references to three distinct elements or panics otherwise. + /// Returns mutable references to three distinct elements. + /// + /// Panics if the elements are not distinct. #[inline] pub fn pick3_mut(&mut self, a: I, b: I, c: I) -> (&mut T, &mut T, &mut T) { let (ai, bi, ci) = (a.index(), b.index(), c.index()); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ce2698ef44c..130214a653f 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -65,6 +65,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Self { tcx: self.tcx, defining_use_anchor: self.defining_use_anchor, + considering_regions: self.considering_regions, in_progress_typeck_results: self.in_progress_typeck_results, inner: self.inner.clone(), skip_leak_check: self.skip_leak_check.clone(), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 7120d5ad934..8dc20544f1b 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -153,7 +153,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { .opaque_type_storage .take_opaque_types() .into_iter() - .map(|(k, v)| (self.tcx.mk_opaque(k.def_id, k.substs), v.hidden_type.ty)) + .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) .collect() } @@ -714,10 +714,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { } fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) { - span_bug!( - self.cause.span(self.infcx.tcx), - "generic_const_exprs: unreachable `const_equate`" - ); + span_bug!(self.cause.span(), "generic_const_exprs: unreachable `const_equate`"); } fn normalization() -> NormalizationStrategy { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 05556f7d0f9..39faed0bf36 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -63,7 +63,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Item, ItemKind, Node}; +use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -237,12 +237,14 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, + opaque_ty: ty::OpaqueTypeKey<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs); let mut err = struct_span_err!( tcx.sess, span, E0700, - "hidden type for `impl Trait` captures lifetime that does not appear in bounds", + "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds", ); // Explain the region we are capturing. @@ -316,39 +318,12 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( err } -/// Structurally compares two types, modulo any inference variables. -/// -/// Returns `true` if two types are equal, or if one type is an inference variable compatible -/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or -/// FloatVar inference type are compatible with themselves or their concrete types (Int and -/// Float types, respectively). When comparing two ADTs, these rules apply recursively. -pub fn same_type_modulo_infer<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { - if did_a != did_b { - return false; - } - - substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type_modulo_infer(a, b)) - } - (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) - | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_))) - | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) - | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), - ) - | (&ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, - (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { - mut_a == mut_b && same_type_modulo_infer(*ty_a, *ty_b) - } - _ => a == b, - } -} - impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - pub fn report_region_errors(&self, errors: &[RegionResolutionError<'tcx>]) { + pub fn report_region_errors( + &self, + generic_param_scope: LocalDefId, + errors: &[RegionResolutionError<'tcx>], + ) { debug!("report_region_errors(): {} errors to start", errors.len()); // try to pre-process the errors, which will group some of them @@ -379,6 +354,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { self.report_generic_bound_failure( + generic_param_scope, origin.span(), Some(origin), param_ty, @@ -640,13 +616,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, "expected due to this"); } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - semi_span, + arm_block_id, + arm_span, + arm_ty, + prior_arm_block_id, + prior_arm_span, + prior_arm_ty, source, ref prior_arms, - last_ty, scrut_hir_id, opt_suggest_box_span, - arm_span, scrut_span, .. }) => match source { @@ -677,10 +656,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } _ => { - // `last_ty` can be `!`, `expected` will have better info when present. + // `prior_arm_ty` can be `!`, `expected` will have better info when present. let t = self.resolve_vars_if_possible(match exp_found { Some(ty::error::ExpectedFound { expected, .. }) => expected, - _ => last_ty, + _ => prior_arm_ty, }); let source_map = self.tcx.sess.source_map(); let mut any_multiline_arm = source_map.is_multiline(arm_span); @@ -705,37 +684,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let msg = "`match` arms have incompatible types"; err.span_label(outer_error_span, msg); - if let Some((sp, boxed)) = semi_span { - if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) = - (boxed, &prior_arms[..]) - { - err.multipart_suggestion( - "consider removing this semicolon and boxing the expressions", - vec![ - (prior_arm.shrink_to_lo(), "Box::new(".to_string()), - (prior_arm.shrink_to_hi(), ")".to_string()), - (arm_span.shrink_to_lo(), "Box::new(".to_string()), - (arm_span.shrink_to_hi(), ")".to_string()), - (sp, String::new()), - ], - Applicability::HasPlaceholders, - ); - } else if matches!(boxed, StatementAsExpression::NeedsBoxing) { - err.span_suggestion_short( - sp, - "consider removing this semicolon and boxing the expressions", - "", - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } + self.suggest_remove_semi_or_return_binding( + err, + prior_arm_block_id, + prior_arm_ty, + prior_arm_span, + arm_block_id, + arm_ty, + arm_span, + ); if let Some(ret_sp) = opt_suggest_box_span { // Get return type span and point to it. self.suggest_boxing_for_return_impl_trait( @@ -747,43 +704,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } }, ObligationCauseCode::IfExpression(box IfExpressionCause { - then, - else_sp, - outer, - semicolon, + then_id, + else_id, + then_ty, + else_ty, + outer_span, opt_suggest_box_span, }) => { - err.span_label(then, "expected because of this"); - if let Some(sp) = outer { + let then_span = self.find_block_span_from_hir_id(then_id); + let else_span = self.find_block_span_from_hir_id(then_id); + err.span_label(then_span, "expected because of this"); + if let Some(sp) = outer_span { err.span_label(sp, "`if` and `else` have incompatible types"); } - if let Some((sp, boxed)) = semicolon { - if matches!(boxed, StatementAsExpression::NeedsBoxing) { - err.multipart_suggestion( - "consider removing this semicolon and boxing the expression", - vec![ - (then.shrink_to_lo(), "Box::new(".to_string()), - (then.shrink_to_hi(), ")".to_string()), - (else_sp.shrink_to_lo(), "Box::new(".to_string()), - (else_sp.shrink_to_hi(), ")".to_string()), - (sp, String::new()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } + self.suggest_remove_semi_or_return_binding( + err, + Some(then_id), + then_ty, + then_span, + Some(else_id), + else_ty, + else_span, + ); if let Some(ret_sp) = opt_suggest_box_span { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - [then, else_sp].into_iter(), + [then_span, else_span].into_iter(), ); } } @@ -803,6 +750,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_remove_semi_or_return_binding( + &self, + err: &mut Diagnostic, + first_id: Option<hir::HirId>, + first_ty: Ty<'tcx>, + first_span: Span, + second_id: Option<hir::HirId>, + second_ty: Ty<'tcx>, + second_span: Span, + ) { + let remove_semicolon = + [(first_id, second_ty), (second_id, first_ty)].into_iter().find_map(|(id, ty)| { + let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None }; + self.could_remove_semicolon(blk, ty) + }); + match remove_semicolon { + Some((sp, StatementAsExpression::NeedsBoxing)) => { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expressions", + vec![ + (first_span.shrink_to_lo(), "Box::new(".to_string()), + (first_span.shrink_to_hi(), ")".to_string()), + (second_span.shrink_to_lo(), "Box::new(".to_string()), + (second_span.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::MachineApplicable, + ); + } + Some((sp, StatementAsExpression::CorrectType)) => { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + "", + Applicability::MachineApplicable, + ); + } + None => { + for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { + if let Some(id) = id + && let hir::Node::Block(blk) = self.tcx.hir().get(id) + && self.consider_returning_binding(blk, ty, err) + { + break; + } + } + } + } + } + fn suggest_boxing_for_return_impl_trait( &self, err: &mut Diagnostic, @@ -1435,7 +1432,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { swap_secondary_and_primary: bool, force_label: bool, ) { - let span = cause.span(self.tcx); + let span = cause.span(); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. @@ -1718,15 +1715,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); if let Some(exp_found) = exp_found { - let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } = - cause.code() - { - // Skip if the root_ty of the pattern is not the same as the expected_ty. - // If these types aren't equal then we've probably peeled off a layer of arrays. - same_type_modulo_infer(self.resolve_vars_if_possible(*root_ty), exp_found.expected) - } else { - true - }; + let should_suggest_fixes = + if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() { + // Skip if the root_ty of the pattern is not the same as the expected_ty. + // If these types aren't equal then we've probably peeled off a layer of arrays. + self.same_type_modulo_infer(*root_ty, exp_found.expected) + } else { + true + }; if should_suggest_fixes { self.suggest_tuple_pattern(cause, &exp_found, diag); @@ -1781,7 +1777,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .filter_map(|variant| { let sole_field = &variant.fields[0]; let sole_field_ty = sole_field.ty(self.tcx, substs); - if same_type_modulo_infer(sole_field_ty, exp_found.found) { + if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { let variant_path = with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); // FIXME #56861: DRYer prelude filtering @@ -1889,7 +1885,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_span, exp_found.expected, exp_found.found, ); - if let ObligationCauseCode::CompareImplMethodObligation { .. } = cause.code() { + if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() { return; } @@ -1897,12 +1893,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder), self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder), ) { - (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match cause.code() { - ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { + (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause + .code() + { + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); diag.multipart_suggestion( "consider `await`ing on both `Future`s", vec![ - (then.shrink_to_hi(), ".await".to_string()), + (then_span.shrink_to_hi(), ".await".to_string()), (exp_span.shrink_to_hi(), ".await".to_string()), ], Applicability::MaybeIncorrect, @@ -1929,7 +1928,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag.help("consider `await`ing on both `Future`s"); } }, - (_, Some(ty)) if same_type_modulo_infer(exp_found.expected, ty) => { + (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { diag.span_suggestion_verbose( exp_span.shrink_to_hi(), "consider `await`ing on the `Future`", @@ -1937,11 +1936,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - (Some(ty), _) if same_type_modulo_infer(ty, exp_found.found) => match cause.code() { - ObligationCauseCode::Pattern { span: Some(span), .. } - | ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => { + (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() + { + ObligationCauseCode::Pattern { span: Some(then_span), .. } => { + diag.span_suggestion_verbose( + then_span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); diag.span_suggestion_verbose( - span.shrink_to_hi(), + then_span.shrink_to_hi(), "consider `await`ing on the `Future`", ".await", Applicability::MaybeIncorrect, @@ -1987,7 +1995,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.name, field.ty(self.tcx, expected_substs))) - .find(|(_, ty)| same_type_modulo_infer(*ty, exp_found.found)) + .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) { if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { @@ -2052,7 +2060,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { | (_, ty::Infer(_)) | (ty::Param(_), _) | (ty::Infer(_), _) => {} - _ if same_type_modulo_infer(exp_ty, found_ty) => {} + _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} _ => show_suggestion = false, }; } @@ -2085,7 +2093,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); - let span = trace.cause.span(self.tcx); + let span = trace.cause.span(); let failure_code = trace.cause.as_failure_code(terr); let mut diag = match failure_code { FailureCode::Error0038(did) => { @@ -2174,7 +2182,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) { let [expected_tup_elem] = expected_fields[..] else { return }; - if !same_type_modulo_infer(expected_tup_elem, found) { + if !self.same_type_modulo_infer(expected_tup_elem, found) { return; } @@ -2269,56 +2277,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_generic_bound_failure( &self, + generic_param_scope: LocalDefId, span: Span, origin: Option<SubregionOrigin<'tcx>>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) { - let owner = - self.in_progress_typeck_results.map(|typeck_results| typeck_results.borrow().hir_owner); - self.construct_generic_bound_failure(span, origin, bound_kind, sub, owner).emit(); + self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) + .emit(); } pub fn construct_generic_bound_failure( &self, + generic_param_scope: LocalDefId, span: Span, origin: Option<SubregionOrigin<'tcx>>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, - owner: Option<LocalDefId>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let hir = self.tcx.hir(); // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. - let generics = owner.map(|owner| { - let hir_id = hir.local_def_id_to_hir_id(owner); - let parent_id = hir.get_parent_item(hir_id); - ( - // Parent item could be a `mod`, so we check the HIR before calling: - if let Some(Node::Item(Item { - kind: ItemKind::Trait(..) | ItemKind::Impl { .. }, - .. - })) = hir.find_by_def_id(parent_id) - { - Some(self.tcx.generics_of(parent_id)) - } else { - None - }, - self.tcx.generics_of(owner.to_def_id()), - hir.span(hir_id), - ) - }); - - let span = match generics { - // This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal - // for other diagnostics, so we need to recover it here. - Some((_, _, node)) if span.is_dummy() => node, - _ => span, - }; - + let generics = self.tcx.generics_of(generic_param_scope); // type_param_span is (span, has_bounds) - let type_param_span = match (generics, bound_kind) { - (Some((_, ref generics, _)), GenericKind::Param(ref param)) => { + let type_param_span = match bound_kind { + GenericKind::Param(ref param) => { // Account for the case where `param` corresponds to `Self`, // which doesn't have the expected type argument. if !(generics.has_self && param.index == 0) { @@ -2346,30 +2328,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } _ => None, }; - let new_lt = generics - .as_ref() - .and_then(|(parent_g, g, _)| { - let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); - let mut lts_names = g - .params - .iter() + + let new_lt = { + let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); + let lts_names = + iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) + .flat_map(|g| &g.params) .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) .map(|p| p.name.as_str()) .collect::<Vec<_>>(); - if let Some(g) = parent_g { - lts_names.extend( - g.params - .iter() - .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) - .map(|p| p.name.as_str()), - ); - } - possible.find(|candidate| !lts_names.contains(&&candidate[..])) - }) - .unwrap_or("'lt".to_string()); + possible + .find(|candidate| !lts_names.contains(&&candidate[..])) + .unwrap_or("'lt".to_string()) + }; + let add_lt_sugg = generics - .as_ref() - .and_then(|(_, g, _)| g.params.first()) + .params + .first() .and_then(|param| param.def_id.as_local()) .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt))); @@ -2378,7 +2353,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { GenericKind::Projection(ref p) => format!("the associated type `{}`", p), }; - if let Some(SubregionOrigin::CompareImplMethodObligation { + if let Some(SubregionOrigin::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id, @@ -2437,9 +2412,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[derive(Debug)] enum SubOrigin<'hir> { GAT(&'hir hir::Generics<'hir>), - Impl(&'hir hir::Generics<'hir>), - Trait(&'hir hir::Generics<'hir>), - Fn(&'hir hir::Generics<'hir>), + Impl, + Trait, + Fn, Unknown, } let sub_origin = 'origin: { @@ -2454,34 +2429,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { kind: hir::ImplItemKind::TyAlias(..), generics, .. - }) => SubOrigin::GAT(generics), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - generics, - .. - }) => SubOrigin::Fn(generics), - Node::TraitItem(hir::TraitItem { + }) + | Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(..), generics, .. }) => SubOrigin::GAT(generics), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(..), - generics, + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), .. - }) => SubOrigin::Fn(generics), - Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, generics, _, _), + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(..), .. - }) => SubOrigin::Trait(generics), + }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Fn(..), .. + }) => SubOrigin::Fn, Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { generics, .. }), + kind: hir::ItemKind::Trait(..), .. - }) => SubOrigin::Impl(generics), + }) => SubOrigin::Trait, Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, generics, _), - .. - }) => SubOrigin::Fn(generics), + kind: hir::ItemKind::Impl(..), .. + }) => SubOrigin::Impl, _ => continue, }; } @@ -2571,7 +2542,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); if let Some(infer::RelateParamBound(_, t, _)) = origin { let return_impl_trait = - owner.and_then(|owner| self.tcx.return_type_impl_trait(owner)).is_some(); + self.tcx.return_type_impl_trait(generic_param_scope).is_some(); let t = self.resolve_vars_if_possible(t); match t.kind() { // We've got: @@ -2675,6 +2646,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span.is_desugaring(DesugaringKind::QuestionMark) && self.tcx.is_diagnostic_item(sym::From, trait_def_id) } + + /// Structurally compares two types, modulo any inference variables. + /// + /// Returns `true` if two types are equal, or if one type is an inference variable compatible + /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or + /// FloatVar inference type are compatible with themselves or their concrete types (Int and + /// Float types, respectively). When comparing two ADTs, these rules apply recursively. + pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + let (a, b) = self.resolve_vars_if_possible((a, b)); + match (a.kind(), b.kind()) { + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { + if def_a != def_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) + | ( + &ty::Infer(ty::InferTy::IntVar(_)), + &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) + | (&ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, + (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { + mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) + } + (&ty::RawPtr(a), &ty::RawPtr(b)) => { + a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) + } + (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), + (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { + self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct + } + (&ty::Tuple(a), &ty::Tuple(b)) => { + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnPtr(a), &ty::FnPtr(b)) => { + let a = a.skip_binder().inputs_and_output; + let b = b.skip_binder().inputs_and_output; + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + // FIXME(compiler-errors): This needs to be generalized more + _ => a == b, + } + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -2745,8 +2786,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { use self::FailureCode::*; use crate::traits::ObligationCauseCode::*; match self.code() { - CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), - CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"), + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + Error0308("method not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + Error0308("type not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + Error0308("const not compatible with trait") + } MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { Error0308(match source { hir::MatchSource::TryDesugar => "`?` operator has incompatible types", @@ -2780,8 +2828,15 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { fn as_requirement_str(&self) -> &'static str { use crate::traits::ObligationCauseCode::*; match self.code() { - CompareImplMethodObligation { .. } => "method type is compatible with trait", - CompareImplTypeObligation { .. } => "associated type is compatible with trait", + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + "method type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + "associated type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + "const is compatible with trait" + } ExprAssignable => "expression is assignable", IfExpression { .. } => "`if` and `else` have incompatible types", IfExpressionWithNoElse => "`if` missing an `else` returns `()`", @@ -2826,3 +2881,237 @@ impl TyCategory { } } } + +impl<'tcx> InferCtxt<'_, 'tcx> { + /// Given a [`hir::Block`], get the span of its last expression or + /// statement, peeling off any inner blocks. + pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { + let block = block.innermost_block(); + if let Some(expr) = &block.expr { + expr.span + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + stmt.span + } else { + // empty block; point at its entirety + block.span + } + } + + /// Given a [`hir::HirId`] for a block, get the span of its last expression + /// or statement, peeling off any inner blocks. + pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { + match self.tcx.hir().get(hir_id) { + hir::Node::Block(blk) => self.find_block_span(blk), + // The parser was in a weird state if either of these happen, but + // it's better not to panic. + hir::Node::Expr(e) => e.span, + _ => rustc_span::DUMMY_SP, + } + } + + /// Be helpful when the user wrote `{... expr; }` and taking the `;` off + /// is enough to fix the error. + pub fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Span, StatementAsExpression)> { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return None; + } + let last_stmt = blk.stmts.last()?; + let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { + return None; + }; + let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?; + let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { + _ if last_expr_ty.references_error() => return None, + _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _)) + if last_def_id == exp_def_id => + { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { + debug!( + "both opaque, likely future {:?} {:?} {:?} {:?}", + last_def_id, last_bounds, exp_def_id, exp_bounds + ); + + let last_local_id = last_def_id.as_local()?; + let exp_local_id = exp_def_id.as_local()?; + + match ( + &self.tcx.hir().expect_item(last_local_id).kind, + &self.tcx.hir().expect_item(exp_local_id).kind, + ) { + ( + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), + ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { + match (left, right) { + ( + hir::GenericBound::Trait(tl, ml), + hir::GenericBound::Trait(tr, mr), + ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() + && ml == mr => + { + true + } + ( + hir::GenericBound::LangItemTrait(langl, _, _, argsl), + hir::GenericBound::LangItemTrait(langr, _, _, argsr), + ) if langl == langr => { + // FIXME: consider the bounds! + debug!("{:?} {:?}", argsl, argsr); + true + } + _ => false, + } + }) => + { + StatementAsExpression::NeedsBoxing + } + _ => StatementAsExpression::CorrectType, + } + } + _ => return None, + }; + let span = if last_stmt.span.from_expansion() { + let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); + self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? + } else { + last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + }; + Some((span, needs_box)) + } + + /// Suggest returning a local binding with a compatible type if the block + /// has no return expression. + pub fn consider_returning_binding( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return false; + } + let mut shadowed = FxHashSet::default(); + let mut candidate_idents = vec![]; + let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind + && let Some(pat_ty) = self + .in_progress_typeck_results + .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id)) + { + let pat_ty = self.resolve_vars_if_possible(pat_ty); + if self.same_type_modulo_infer(pat_ty, expected_ty) + && !(pat_ty, expected_ty).references_error() + && shadowed.insert(ident.name) + { + candidate_idents.push((*ident, pat_ty)); + } + } + true + }; + + let hir = self.tcx.hir(); + for stmt in blk.stmts.iter().rev() { + let hir::StmtKind::Local(local) = &stmt.kind else { continue; }; + local.pat.walk(&mut find_compatible_candidates); + } + match hir.find(hir.get_parent_node(blk.hir_id)) { + Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => { + match hir.find(hir.get_parent_node(*hir_id)) { + Some(hir::Node::Arm(hir::Arm { pat, .. })) => { + pat.walk(&mut find_compatible_candidates); + } + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), + .. + }), + ) => { + for param in hir.body(*body).params { + param.pat.walk(&mut find_compatible_candidates); + } + } + Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::If( + hir::Expr { kind: hir::ExprKind::Let(let_), .. }, + then_block, + _, + ), + .. + })) if then_block.hir_id == *hir_id => { + let_.pat.walk(&mut find_compatible_candidates); + } + _ => {} + } + } + _ => {} + } + + match &candidate_idents[..] { + [(ident, _ty)] => { + let sm = self.tcx.sess.source_map(); + if let Some(stmt) = blk.stmts.last() { + let stmt_span = sm.stmt_span(stmt.span, blk.span); + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(stmt_span) + { + format!("\n{spacing}{ident}") + } else { + format!(" {ident}") + }; + err.span_suggestion_verbose( + stmt_span.shrink_to_hi(), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } else { + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) + { + format!("\n{spacing} {ident}\n{spacing}") + } else { + format!(" {ident} ") + }; + let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); + err.span_suggestion_verbose( + sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } + true + } + values if (1..3).contains(&values.len()) => { + let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); + err.span_note(spans, "consider returning one of these bindings"); + true + } + _ => false, + } + } +} 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 4d29fc46946..561d1354edd 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 @@ -1,4 +1,4 @@ -use crate::infer::type_variable::TypeVariableOriginKind; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::InferCtxt; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; @@ -6,14 +6,14 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::ConstVariableOriginKind; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, InferConst}; -use rustc_middle::ty::{Ty, TyCtxt, TypeckResults}; +use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span}; use std::borrow::Cow; @@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind { - Some(name.to_string()) + Some(name) } else { None } @@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind { - return Some(name.to_string()); + return Some(name); } else { None } @@ -407,11 +407,40 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, cannot_infer_msg); - let printer = fmt_printer(self, Namespace::TypeNS); - let args = printer.comma_sep(generic_args.iter().copied()).unwrap().into_buffer(); + let args = fmt_printer(self, Namespace::TypeNS) + .comma_sep(generic_args.iter().copied().map(|arg| { + if arg.is_suggestable(self.tcx, true) { + return arg; + } + + match arg.unpack() { + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + GenericArgKind::Type(_) => self + .next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }) + .into(), + GenericArgKind::Const(arg) => self + .next_const_var( + arg.ty(), + ConstVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: ConstVariableOriginKind::MiscVariable, + }, + ) + .into(), + } + })) + .unwrap() + .into_buffer(); + err.span_suggestion_verbose( insert_span, - &format!("consider specifying the generic argument{}", pluralize!(args.len()),), + &format!( + "consider specifying the generic argument{}", + pluralize!(generic_args.len()), + ), format!("::<{}>", args), Applicability::HasPlaceholders, ); @@ -1051,7 +1080,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { if let Some(node_ty) = self.opt_node_type(expr.hir_id) { if let ( - &ExprKind::Closure { fn_decl, body, fn_decl_span, .. }, + &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }), ty::Closure(_, substs), ) = (&expr.kind, node_ty.kind()) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index b744594ddb7..9a2ab3e3224 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -228,7 +228,7 @@ pub fn suggest_adding_lifetime_params<'tcx>( if is_impl { sugg.push_str(" and update trait if needed"); } - err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect); + err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect); true } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index e5ae835e813..c1b201da691 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // the lifetime of the TyRptr let hir_id = lifetime.hir_id; match (self.tcx.named_region(hir_id), self.bound_region) { - // Find the index of the anonymous region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - debug!( - "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", - debruijn_index, anon_index, br_index - ); - if debruijn_index == self.current_index && anon_index == br_index { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index @@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { rl::Region::Static | rl::Region::Free(_, _) | rl::Region::EarlyBound(_, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _), + | rl::Region::LateBound(_, _, _), ) | None, _, @@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - if debruijn_index == self.current_index && anon_index == br_index { - self.found_it = true; - return; - } - } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { @@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { rl::Region::Static | rl::Region::EarlyBound(_, _) | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _) | rl::Region::Free(_, _), ) | None, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 43d5c9fdf33..893ca3cf79d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -6,7 +6,7 @@ use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index e2185d86133..998699158ff 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -204,7 +204,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { expected_substs: SubstsRef<'tcx>, actual_substs: SubstsRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let span = cause.span(self.tcx()); + let span = cause.span(); let msg = format!( "implementation of `{}` is not general enough", self.tcx().def_path_str(trait_def_id), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 02928c4aa57..246d27be71c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 91bf9695dfc..da465a76429 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -2,17 +2,17 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::{SubregionOrigin, Subtype}; -use crate::traits::ObligationCauseCode::CompareImplMethodObligation; +use crate::infer::Subtype; +use crate::traits::ObligationCauseCode::CompareImplItemObligation; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use std::ops::ControlFlow; @@ -22,38 +22,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let error = self.error.as_ref()?; debug!("try_report_impl_not_conforming_to_trait {:?}", error); if let RegionResolutionError::SubSupConflict( - _, var_origin, sub_origin, _sub, sup_origin, _sup, _, + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + _, ) = error.clone() - && let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) - && let ( - sub_expected_found @ Some((sub_expected, sub_found)), - sup_expected_found @ Some(_), - CompareImplMethodObligation { trait_item_def_id, .. }, - ) = (sub_trace.values.ty(), sup_trace.values.ty(), sub_trace.cause.code()) + && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) + && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty() + && let sup_expected_found @ Some(_) = sup_trace.values.ty() + && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code() && sup_expected_found == sub_expected_found { - let guar = self.emit_err( - var_origin.span(), - sub_expected, - sub_found, - *trait_item_def_id, - ); - return Some(guar); - } - if let RegionResolutionError::ConcreteFailure(origin, _, _) - | RegionResolutionError::GenericBoundFailure(origin, _, _) = error.clone() - && let SubregionOrigin::CompareImplTypeObligation { - span, - impl_item_def_id, - trait_item_def_id, - } = origin - { - let guar = self.emit_associated_type_err( - span, - self.infcx.tcx.item_name(impl_item_def_id.to_def_id()), - impl_item_def_id, - trait_item_def_id, - ); + let guar = + self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id); return Some(guar); } None @@ -147,25 +131,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } err.emit() } - - fn emit_associated_type_err( - &self, - span: Span, - item_name: Symbol, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - ) -> ErrorGuaranteed { - let impl_sp = self.tcx().def_span(impl_item_def_id); - let trait_sp = self.tcx().def_span(trait_item_def_id); - let mut err = self - .tcx() - .sess - .struct_span_err(span, &format!("`impl` associated type signature for `{}` doesn't match `trait` associated type signature", item_name)); - err.span_label(impl_sp, "found"); - err.span_label(trait_sp, "expected"); - - err.emit() - } } struct TypeParamSpanVisitor<'tcx> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 67bbace39e3..c1940c5c082 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -86,13 +86,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "...so that the declared lifetime parameter bounds are satisfied", ); } - infer::CompareImplMethodObligation { span, .. } => { - label_or_note( - span, - "...so that the definition in impl matches the definition from the trait", - ); - } - infer::CompareImplTypeObligation { span, .. } => { + infer::CompareImplItemObligation { span, .. } => { label_or_note( span, "...so that the definition in impl matches the definition from the trait", @@ -329,15 +323,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => { - self.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ) - } - infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self .report_extra_impl_obligation( span, impl_item_def_id, @@ -357,7 +343,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id); let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id); - let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> = + let impl_predicates: rustc_data_structures::fx::FxHashSet<_> = impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect(); let clauses: Vec<_> = trait_predicates .predicates diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index 12c44b4baaa..e543ae1fcda 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -122,7 +122,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // subtyping errors that it would have caught will now be // caught later on, during region checking. However, we // continue to use it for a transition period. - if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() { + if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() { return Ok(()); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 28f037cc61a..0ac6e8c541b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -21,6 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; +use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::relate::RelateResult; @@ -238,17 +239,36 @@ impl<'tcx> InferCtxtInner<'tcx> { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DefiningAnchor { + /// `DefId` of the item. + Bind(LocalDefId), + /// When opaque types are not resolved, we `Bubble` up, meaning + /// return the opaque/hidden type pair from query, for caller of query to handle it. + Bubble, + /// Used to catch type mismatch errors when handling opaque types. + Error, +} + pub struct InferCtxt<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. /// - /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up /// the obligation. This frequently happens for /// short lived InferCtxt within queries. The opaque type obligations are forwarded /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. - pub defining_use_anchor: Option<LocalDefId>, + /// + /// It is default value is `DefiningAnchor::Error`, this way it is easier to catch errors that + /// might come up during inference or typeck. + pub defining_use_anchor: DefiningAnchor, + + /// Whether this inference context should care about region obligations in + /// the root universe. Most notably, this is used during hir typeck as region + /// solving is left to borrowck instead. + pub considering_regions: bool, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -385,15 +405,7 @@ pub enum SubregionOrigin<'tcx> { /// Comparing the signature and requirements of an impl method against /// the containing trait. - CompareImplMethodObligation { - span: Span, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - }, - - /// Comparing the signature and requirements of an impl associated type - /// against the containing trait - CompareImplTypeObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, + CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, /// Checking that the bounds of a trait's associated type hold for a given impl CheckAssociatedTypeBounds { @@ -524,8 +536,9 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { /// without using `Rc` or something similar. pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, + defining_use_anchor: DefiningAnchor, + considering_regions: bool, fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, - defining_use_anchor: Option<LocalDefId>, } pub trait TyCtxtInferExt<'tcx> { @@ -534,7 +547,12 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } + InferCtxtBuilder { + tcx: self, + defining_use_anchor: DefiningAnchor::Error, + considering_regions: true, + fresh_typeck_results: None, + } } } @@ -544,7 +562,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// Will also change the scope for opaque type defining use checks to the given owner. pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self { self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner))); - self.with_opaque_type_inference(table_owner) + self.with_opaque_type_inference(DefiningAnchor::Bind(table_owner)) } /// Whenever the `InferCtxt` should be able to handle defining uses of opaque types, @@ -553,8 +571,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// It is only meant to be called in two places, for typeck /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. - pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = Some(defining_use_anchor); + pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor) -> Self { + self.defining_use_anchor = defining_use_anchor; + self + } + + pub fn ignoring_regions(mut self) -> Self { + self.considering_regions = false; self } @@ -582,11 +605,17 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R { - let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self; + let InferCtxtBuilder { + tcx, + defining_use_anchor, + considering_regions, + ref fresh_typeck_results, + } = *self; let in_progress_typeck_results = fresh_typeck_results.as_ref(); f(InferCtxt { tcx, defining_use_anchor, + considering_regions, in_progress_typeck_results, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), @@ -937,14 +966,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn member_constraint( &self, - opaque_type_def_id: DefId, + key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, hidden_ty: Ty<'tcx>, region: ty::Region<'tcx>, in_regions: &Lrc<Vec<ty::Region<'tcx>>>, ) { self.inner.borrow_mut().unwrap_region_constraints().member_constraint( - opaque_type_def_id, + key, definition_span, hidden_ty, region, @@ -1024,16 +1053,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, cause: &traits::ObligationCause<'tcx>, predicate: ty::PolyRegionOutlivesPredicate<'tcx>, - ) -> UnitResult<'tcx> { - self.commit_if_ok(|_snapshot| { - let ty::OutlivesPredicate(r_a, r_b) = - self.replace_bound_vars_with_placeholders(predicate); - let origin = SubregionOrigin::from_obligation_cause(cause, || { - RelateRegionParamBound(cause.span) - }); - self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` - Ok(()) - }) + ) { + let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(predicate); + let origin = + SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span)); + self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` } /// Number of type variables created so far. @@ -1300,7 +1324,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors(&self, outlives_env: &OutlivesEnvironment<'tcx>) { + pub fn resolve_regions_and_report_errors( + &self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { let errors = self.resolve_regions(outlives_env); if !self.is_tainted_by_errors() { @@ -1309,7 +1337,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(&errors); + self.report_region_errors(generic_param_scope, &errors); } } @@ -1647,18 +1675,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, - param_env: ty::ParamEnv<'tcx>, + mut param_env: ty::ParamEnv<'tcx>, unevaluated: ty::Unevaluated<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx> { - let substs = self.resolve_vars_if_possible(unevaluated.substs); + let mut substs = self.resolve_vars_if_possible(unevaluated.substs); debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { - debug!("substs have infer types or consts: {:?}", substs); - return Err(ErrorHandled::TooGeneric); + let ac = AbstractConst::new(self.tcx, unevaluated.shrink()); + match ac { + Ok(None) => { + substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); + param_env = self.tcx.param_env(unevaluated.def.did); + } + Ok(Some(ct)) => { + if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete { + substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs); + } else { + return Err(ErrorHandled::TooGeneric); + } + } + Err(guar) => return Err(ErrorHandled::Reported(guar)), + } } let param_env_erased = self.tcx.erase_regions(param_env); @@ -1905,8 +1946,7 @@ impl<'tcx> SubregionOrigin<'tcx> { ReborrowUpvar(a, _) => a, DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, - CompareImplMethodObligation { span, .. } => span, - CompareImplTypeObligation { span, .. } => span, + CompareImplItemObligation { span, .. } => span, CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), } } @@ -1920,19 +1960,11 @@ impl<'tcx> SubregionOrigin<'tcx> { SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) } - traits::ObligationCauseCode::CompareImplMethodObligation { - impl_item_def_id, - trait_item_def_id, - } => SubregionOrigin::CompareImplMethodObligation { - span: cause.span, - impl_item_def_id, - trait_item_def_id, - }, - - traits::ObligationCauseCode::CompareImplTypeObligation { + traits::ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, - } => SubregionOrigin::CompareImplTypeObligation { + kind: _, + } => SubregionOrigin::CompareImplItemObligation { span: cause.span, impl_item_def_id, trait_item_def_id, @@ -1977,3 +2009,43 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> { ) } } + +/// Replaces substs that reference param or infer variables with suitable +/// placeholders. This function is meant to remove these param and infer +/// substs when they're not actually needed to evaluate a constant. +fn replace_param_and_infer_substs_with_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, +) -> SubstsRef<'tcx> { + tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| { + match arg.unpack() { + GenericArgKind::Type(_) + if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() => + { + tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundVar::from_usize(idx), + })) + .into() + } + GenericArgKind::Const(ct) + if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() => + { + let ty = ct.ty(); + // If the type references param or infer, replace that too... + if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() { + bug!("const `{ct}`'s type should not reference params or types"); + } + tcx.mk_const(ty::ConstS { + ty, + kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundConst { ty, var: ty::BoundVar::from_usize(idx) }, + }), + }) + .into() + } + _ => arg, + } + })) +} diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index f11701bba6f..a1c7b70bd9c 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -1,4 +1,4 @@ -use crate::infer::{InferCtxt, InferOk}; +use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; use crate::traits; use hir::def_id::{DefId, LocalDefId}; use hir::{HirId, OpaqueTyOrigin}; @@ -51,7 +51,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return InferOk { value: ty, obligations: vec![] }; } let mut obligations = vec![]; - let replace_opaque_type = |def_id| self.opaque_type_origin(def_id, span).is_some(); + let replace_opaque_type = |def_id: DefId| { + def_id + .as_local() + .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some()) + }; let value = ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, @@ -96,44 +100,47 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let (a, b) = if a_is_expected { (a, b) } else { (b, a) }; let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Opaque(def_id, substs) if def_id.is_local() => { - let origin = if self.defining_use_anchor.is_some() { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - self.opaque_type_origin(def_id, cause.span)? - } else { - self.opaque_ty_origin_unchecked(def_id, cause.span) + let def_id = def_id.expect_local(); + let origin = match self.defining_use_anchor { + DefiningAnchor::Bind(_) => { + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + self.opaque_type_origin(def_id, cause.span)? + } + DefiningAnchor::Bubble => self.opaque_ty_origin_unchecked(def_id, cause.span), + DefiningAnchor::Error => return None, }; if let ty::Opaque(did2, _) = *b.kind() { // We could accept this, but there are various ways to handle this situation, and we don't @@ -141,7 +148,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // no one encounters it in practice. // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, // where it is of no concern, so we only check for TAITs. - if let Some(OpaqueTyOrigin::TyAlias) = self.opaque_type_origin(did2, cause.span) + if let Some(OpaqueTyOrigin::TyAlias) = + did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span)) { self.tcx .sess @@ -386,30 +394,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - op: |r| { - self.member_constraint( - opaque_type_key.def_id, - span, - concrete_ty, - r, - &choice_regions, - ) - }, + op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions), }); } #[instrument(skip(self), level = "trace")] - pub fn opaque_type_origin(&self, opaque_def_id: DefId, span: Span) -> Option<OpaqueTyOrigin> { - let def_id = opaque_def_id.as_local()?; + pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.defining_use_anchor?; + let parent_def_id = match self.defining_use_anchor { + DefiningAnchor::Bubble | DefiningAnchor::Error => return None, + DefiningAnchor::Bind(bind) => bind, + }; let item_kind = &self.tcx.hir().expect_item(def_id).kind; let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { span_bug!( span, "weird opaque type: {:#?}, {:#?}", - opaque_def_id, + def_id, item_kind ) }; @@ -428,12 +430,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "trace")] - fn opaque_ty_origin_unchecked(&self, opaque_def_id: DefId, span: Span) -> OpaqueTyOrigin { - let def_id = opaque_def_id.as_local().unwrap(); + fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { let origin = match self.tcx.hir().expect_item(def_id).kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin, ref itemkind => { - span_bug!(span, "weird opaque type: {:?}, {:#?}", opaque_def_id, itemkind) + span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind) } }; trace!(?origin); @@ -557,7 +558,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations; } - let item_bounds = tcx.bound_explicit_item_bounds(def_id); + let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id()); for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { debug!(?predicate); @@ -579,7 +580,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } // Replace all other mentions of the same opaque type with the hidden type, // as the bounds must hold on the hidden type after all. - ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => { + ty::Opaque(def_id2, substs2) + if def_id.to_def_id() == def_id2 && substs == substs2 => + { hidden_ty } _ => ty, diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 7234660dbcd..b2d7f4a663a 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -190,7 +190,11 @@ fn compute_components<'tcx>( } } -fn compute_components_recursive<'tcx>( +/// Collect [Component]s for *all* the substs of `parent`. +/// +/// This should not be used to get the components of `parent` itself. +/// Use [push_outlives_components] instead. +pub(super) fn compute_components_recursive<'tcx>( tcx: TyCtxt<'tcx>, parent: GenericArg<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index b897de7315a..b2decd64f0f 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -1,6 +1,7 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; +use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -53,7 +54,8 @@ pub struct OutlivesEnvironment<'tcx> { /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = Vec<(Region<'tcx>, GenericKind<'tcx>)>; +pub type RegionBoundPairs<'tcx> = + FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>; impl<'a, 'tcx> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { @@ -97,10 +99,12 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); } OutlivesBound::RegionSubRegion(r_a, r_b) => { if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 59cf39abe64..a57971bfb69 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -68,6 +68,7 @@ use crate::infer::{ }; use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable}; use smallvec::smallvec; @@ -163,6 +164,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn check_region_obligations_and_report_errors( &self, + generic_param_scope: LocalDefId, outlives_env: &OutlivesEnvironment<'tcx>, ) { self.process_registered_region_obligations( @@ -170,7 +172,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { outlives_env.param_env, ); - self.resolve_regions_and_report_errors(outlives_env) + self.resolve_regions_and_report_errors(generic_param_scope, outlives_env) } } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 86b025dce5e..c7d7ef40d9d 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,11 +1,14 @@ +use crate::infer::outlives::components::{compute_components_recursive, Component}; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; use crate::infer::{GenericKind, VerifyBound}; use rustc_data_structures::captures::Captures; use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::subst::{GenericArg, Subst}; +use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt}; + +use smallvec::smallvec; /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verifys", as @@ -47,43 +50,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } } - fn type_bound( - &self, - ty: Ty<'tcx>, - visited: &mut SsoHashSet<GenericArg<'tcx>>, - ) -> VerifyBound<'tcx> { - match *ty.kind() { - ty::Param(p) => self.param_bound(p), - ty::Projection(data) => self.projection_bound(data, visited), - ty::FnDef(_, substs) => { - // HACK(eddyb) ignore lifetimes found shallowly in `substs`. - // This is inconsistent with `ty::Adt` (including all substs), - // but consistent with previous (accidental) behavior. - // See https://github.com/rust-lang/rust/issues/70917 - // for further background and discussion. - let mut bounds = substs - .iter() - .filter_map(|child| match child.unpack() { - GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), - GenericArgKind::Lifetime(_) => None, - GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)), - }) - .filter(|bound| { - // Remove bounds that must hold, since they are not interesting. - !bound.must_hold() - }); - - match (bounds.next(), bounds.next()) { - (Some(first), None) => first, - (first, second) => VerifyBound::AllBounds( - first.into_iter().chain(second).chain(bounds).collect(), - ), - } - } - _ => self.recursive_bound(ty.into(), visited), - } - } - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { debug!("param_bound(param_ty={:?})", param_ty); @@ -188,27 +154,24 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { .map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_bound(ty.into(), visited); + let recursive_bound = { + let mut components = smallvec![]; + let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + compute_components_recursive(self.tcx, ty.into(), &mut components, visited); + self.bound_from_components(&components, visited) + }; VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) } - fn recursive_bound( + fn bound_from_components( &self, - parent: GenericArg<'tcx>, + components: &[Component<'tcx>], visited: &mut SsoHashSet<GenericArg<'tcx>>, ) -> VerifyBound<'tcx> { - let mut bounds = parent - .walk_shallow(visited) - .filter_map(|child| match child.unpack() { - GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), - GenericArgKind::Lifetime(lt) => { - // Ignore late-bound regions. - if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None } - } - GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)), - }) + let mut bounds = components + .iter() + .map(|component| self.bound_from_single_component(component, visited)) .filter(|bound| { // Remove bounds that must hold, since they are not interesting. !bound.must_hold() @@ -222,6 +185,32 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } } + fn bound_from_single_component( + &self, + component: &Component<'tcx>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + match *component { + Component::Region(lt) => VerifyBound::OutlivedBy(lt), + Component::Param(param_ty) => self.param_bound(param_ty), + Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited), + Component::EscapingProjection(ref components) => { + self.bound_from_components(components, visited) + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unresolved inference variable in outlives: {:?}", v), + ); + // add a bound that never holds + VerifyBound::AnyBound(vec![]) + } + } + } + /// Searches the environment for where-clauses like `G: 'a` where /// `G` is either some type parameter `T` or a projection like /// `T::Item`. Returns a vector of the `'a` bounds it can find. @@ -270,16 +259,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must outlive `'a`, but we don't know that // this holds from first principles. - let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", - (r, p) - ); - let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_regions(p_ty); - (erased_p_ty == erased_ty) - .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) - }); + let from_region_bound_pairs = + self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty) + .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) + }); param_bounds .chain(from_region_bound_pairs) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index c5747ecf702..0d4472a1cfd 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -12,7 +12,6 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; -use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion}; use rustc_middle::ty::ReStatic; @@ -533,7 +532,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn member_constraint( &mut self, - opaque_type_def_id: DefId, + key: ty::OpaqueTypeKey<'tcx>, definition_span: Span, hidden_ty: Ty<'tcx>, member_region: ty::Region<'tcx>, @@ -546,7 +545,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { } self.data.member_constraints.push(MemberConstraint { - opaque_type_def_id, + key, definition_span, hidden_ty, member_region, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 7769a68ba2a..490ba52503f 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -18,7 +18,7 @@ #![feature(control_flow_enum)] #![feature(extend_one)] #![feature(label_break_value)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 7e42458fda3..95b6c4ce1f2 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -18,20 +18,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { trait_item_def_id: DefId, requirement: &dyn fmt::Display, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let msg = "impl has stricter requirements than trait"; - let sp = self.tcx.sess.source_map().guess_head_span(error_span); + let mut err = struct_span_err!( + self.tcx.sess, + error_span, + E0276, + "impl has stricter requirements than trait" + ); - let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); - - if trait_item_def_id.is_local() { + if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - self.tcx.def_span(trait_item_def_id), - format!("definition of `{}` from trait", item_name), - ); + err.span_label(span, format!("definition of `{}` from trait", item_name)); } - err.span_label(sp, format!("impl has extra requirement {}", requirement)); + err.span_label(error_span, format!("impl has extra requirement {}", requirement)); err } @@ -48,7 +47,6 @@ pub fn report_object_safety_error<'tcx>( hir::Node::Item(item) => Some(item.ident.span), _ => None, }); - let span = tcx.sess.source_map().guess_head_span(span); let mut err = struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 674c75fdee5..38ff9343537 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -145,7 +145,12 @@ impl<'tcx> Elaborator<'tcx> { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let obligations = predicates.predicates.iter().map(|&(pred, _)| { + let obligations = predicates.predicates.iter().map(|&(mut pred, _)| { + // when parent predicate is non-const, elaborate it to non-const predicates. + if data.constness == ty::BoundConstness::NotConst { + pred = pred.without_const(tcx); + } + predicate_obligation( pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), obligation.param_env, diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 9aacfba3c82..1ecbc876c8d 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -46,7 +46,6 @@ rustc_query_impl = { path = "../rustc_query_impl" } rustc_resolve = { path = "../rustc_resolve" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } -tempfile = "3.2" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index e7563933c88..94f81b66077 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -300,7 +300,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R ); } - let temps_dir = sess.opts.debugging_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); + let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); let compiler = Compiler { sess, @@ -329,11 +329,13 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R }) } +// JUSTIFICATION: before session exists, only config +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); util::run_in_thread_pool_with_globals( config.opts.edition, - config.opts.debugging_opts.threads, + config.opts.unstable_opts.threads, || create_compiler_and_run(config, f), ) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index b7d1d6edfaa..8f083591786 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -5,18 +5,15 @@ use crate::util; use ast::CRATE_NODE_ID; use rustc_ast::{self as ast, visit}; use rustc_borrowck as mir_borrowck; -use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; -use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult}; use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; -use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; +use rustc_hir::def_id::StableCrateId; use rustc_hir::definitions::Definitions; -use rustc_lint::{EarlyCheckNode, LintStore}; +use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; -use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::query::{ExternProviders, Providers}; @@ -29,14 +26,13 @@ use rustc_query_impl::{OnDiskCache, Queries as TcxQueries}; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn}; -use rustc_session::output::{filename_for_input, filename_for_metadata}; +use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::FileName; use rustc_trait_selection::traits; use rustc_typeck as typeck; -use tempfile::Builder as TempFileBuilder; use tracing::{info, warn}; use std::any::Any; @@ -58,16 +54,16 @@ pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { } })?; - if sess.opts.debugging_opts.input_stats { + if sess.opts.unstable_opts.input_stats { eprintln!("Lines of code: {}", sess.source_map().count_lines()); eprintln!("Pre-expansion node count: {}", count_nodes(&krate)); } - if let Some(ref s) = sess.opts.debugging_opts.show_span { + if let Some(ref s) = sess.opts.unstable_opts.show_span { rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate); } - if sess.opts.debugging_opts.hir_stats { + if sess.opts.unstable_opts.hir_stats { hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS"); } @@ -181,7 +177,7 @@ pub fn register_plugins<'a>( rustc_builtin_macros::cmdline_attrs::inject( krate, &sess.parse_sess, - &sess.opts.debugging_opts.crate_attr, + &sess.opts.unstable_opts.crate_attr, ) }); @@ -213,8 +209,8 @@ pub fn register_plugins<'a>( } let mut lint_store = rustc_lint::new_lint_store( - sess.opts.debugging_opts.no_interleave_lints, - sess.unstable_options(), + sess.opts.unstable_opts.no_interleave_lints, + sess.enable_internal_lints(), ); register_lints(sess, &mut lint_store); @@ -327,21 +323,24 @@ pub fn configure_and_expand( let cfg = rustc_expand::expand::ExpansionConfig { features: Some(features), recursion_limit, - trace_mac: sess.opts.debugging_opts.trace_macros, + trace_mac: sess.opts.unstable_opts.trace_macros, should_test: sess.opts.test, - span_debug: sess.opts.debugging_opts.span_debug, - proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace, + span_debug: sess.opts.unstable_opts.span_debug, + proc_macro_backtrace: sess.opts.unstable_opts.proc_macro_backtrace, ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; let lint_store = LintStoreExpandImpl(lint_store); let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store)); - // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); // The rest is error reporting + sess.parse_sess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| { + buffered_lints.append(&mut ecx.buffered_early_lint); + }); + sess.time("check_unused_macros", || { ecx.check_unused_macros(); }); @@ -413,11 +412,11 @@ pub fn configure_and_expand( // Done with macro expansion! - if sess.opts.debugging_opts.input_stats { + if sess.opts.unstable_opts.input_stats { eprintln!("Post-expansion node count: {}", count_nodes(&krate)); } - if sess.opts.debugging_opts.hir_stats { + if sess.opts.unstable_opts.hir_stats { hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS"); } @@ -500,7 +499,7 @@ fn generated_output_paths( out_filenames.push(p); } } - OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { + OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => { // Don't add the dep-info output when omitting it from dep-info targets } _ => { @@ -598,7 +597,7 @@ fn write_out_deps( files.extend(extra_tracked_files); if sess.binary_dep_depinfo() { - if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend { + if let Some(ref backend) = sess.opts.unstable_opts.codegen_backend { if backend.contains('.') { // If the backend name contain a `.`, it is the path to an external dynamic // library. If not, it is not a path. @@ -928,7 +927,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { sess.time("MIR_effect_checking", || { for def_id in tcx.hir().body_owners() { tcx.ensure().thir_check_unsafety(def_id); - if !tcx.sess.opts.debugging_opts.thir_unsafeck { + if !tcx.sess.opts.unstable_opts.thir_unsafeck { rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id); } tcx.ensure().has_ffi_unwind_calls(def_id); @@ -990,69 +989,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { Ok(()) } -fn encode_and_write_metadata( - tcx: TyCtxt<'_>, - outputs: &OutputFilenames, -) -> (EncodedMetadata, bool) { - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed, - } - - let metadata_kind = tcx - .sess - .crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, - - CrateType::Rlib => MetadataKind::Uncompressed, - - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None); - - let metadata = match metadata_kind { - MetadataKind::None => EncodedMetadata::new(), - MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx), - }; - - let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); - - let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); - if need_metadata_file { - let crate_name = tcx.crate_name(LOCAL_CRATE); - let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with an `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() - .prefix("rmeta") - .tempdir_in(out_filename.parent().unwrap()) - .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); - let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); - let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir); - if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) { - tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - if tcx.sess.opts.json_artifact_notifications { - tcx.sess - .parse_sess - .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); - } - } - - let need_metadata_module = metadata_kind == MetadataKind::Compressed; - - (metadata, need_metadata_module) -} - /// Runs the codegen backend, after which the AST and analysis can /// be discarded. pub fn start_codegen<'tcx>( @@ -1062,7 +998,8 @@ pub fn start_codegen<'tcx>( ) -> Box<dyn Any> { info!("Pre-codegen\n{:?}", tcx.debug_stats()); - let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs); + let (metadata, need_metadata_module) = + rustc_metadata::fs::encode_and_write_metadata(tcx, outputs); let codegen = tcx.sess.time("codegen_crate", move || { codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 8ffb1ad0539..73402ae0842 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -357,7 +357,7 @@ impl Linker { return Ok(()); } - if sess.opts.debugging_opts.no_link { + if sess.opts.unstable_opts.no_link { let encoded = CodegenResults::serialize_rlink(&codegen_results); let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT); std::fs::write(&rlink_file, encoded).map_err(|err| { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 55827ff583a..57ce4933a3b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; @@ -629,14 +630,14 @@ fn test_top_level_options_tracked_no_crate() { } #[test] -fn test_debugging_options_tracking_hash() { +fn test_unstable_options_tracking_hash() { let reference = Options::default(); let mut opts = Options::default(); macro_rules! untracked { ($name: ident, $non_default_value: expr) => { - assert_ne!(opts.debugging_opts.$name, $non_default_value); - opts.debugging_opts.$name = $non_default_value; + assert_ne!(opts.unstable_opts.$name, $non_default_value); + opts.unstable_opts.$name = $non_default_value; assert_same_hash(&reference, &opts); }; } @@ -669,6 +670,7 @@ fn test_debugging_options_tracking_hash() { untracked!(ls, true); untracked!(macro_backtrace, true); untracked!(meta_stats, true); + untracked!(mir_pretty_relative_line_numbers, true); untracked!(nll_facts, true); untracked!(no_analysis, true); untracked!(no_interleave_lints, true); @@ -705,8 +707,8 @@ fn test_debugging_options_tracking_hash() { macro_rules! tracked { ($name: ident, $non_default_value: expr) => { opts = reference.clone(); - assert_ne!(opts.debugging_opts.$name, $non_default_value); - opts.debugging_opts.$name = $non_default_value; + assert_ne!(opts.unstable_opts.$name, $non_default_value); + opts.unstable_opts.$name = $non_default_value; assert_different_hash(&reference, &opts); }; } @@ -718,6 +720,7 @@ fn test_debugging_options_tracking_hash() { tracked!(asm_comments, true); tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); + tracked!(box_noalias, Some(false)); tracked!( branch_protection, Some(BranchProtection { @@ -732,8 +735,10 @@ fn test_debugging_options_tracking_hash() { tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); + tracked!(export_executable_symbols, true); tracked!(dual_proc_macros, true); tracked!(dwarf_version, Some(5)); + tracked!(emit_thin_lto, false); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); @@ -804,8 +809,8 @@ fn test_debugging_options_tracking_hash() { macro_rules! tracked_no_crate_hash { ($name: ident, $non_default_value: expr) => { opts = reference.clone(); - assert_ne!(opts.debugging_opts.$name, $non_default_value); - opts.debugging_opts.$name = $non_default_value; + assert_ne!(opts.unstable_opts.$name, $non_default_value); + opts.unstable_opts.$name = $non_default_value; assert_non_crate_hash_different(&reference, &opts); }; } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 8796ad5a33c..4c64e679b95 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -48,7 +48,10 @@ pub fn add_configuration( ) { let tf = sym::target_feature; - let target_features = codegen_backend.target_features(sess); + let unstable_target_features = codegen_backend.target_features(sess, true); + sess.unstable_target_features.extend(unstable_target_features.iter().cloned()); + + let target_features = codegen_backend.target_features(sess, false); sess.target_features.extend(target_features.iter().cloned()); cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat)))); @@ -76,7 +79,7 @@ pub fn create_session( } else { get_codegen_backend( &sopts.maybe_sysroot, - sopts.debugging_opts.codegen_backend.as_ref().map(|name| &name[..]), + sopts.unstable_opts.codegen_backend.as_ref().map(|name| &name[..]), ) }; @@ -86,9 +89,9 @@ pub fn create_session( let bundle = match rustc_errors::fluent_bundle( sopts.maybe_sysroot.clone(), sysroot_candidates(), - sopts.debugging_opts.translate_lang.clone(), - sopts.debugging_opts.translate_additional_ftl.as_deref(), - sopts.debugging_opts.translate_directionality_markers, + sopts.unstable_opts.translate_lang.clone(), + sopts.unstable_opts.translate_additional_ftl.as_deref(), + sopts.unstable_opts.translate_directionality_markers, ) { Ok(bundle) => bundle, Err(e) => { @@ -556,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C // Only check command line flags if present. If no types are specified by // command line, then reuse the empty `base` Vec to hold the types that // will be found in crate attributes. + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] let mut base = session.opts.crate_types.clone(); if base.is_empty() { base.extend(attr_types); @@ -647,24 +652,6 @@ pub fn build_output_filenames( } } -#[cfg(not(target_os = "linux"))] -pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { - std::fs::rename(src, dst) -} - -/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems -/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" -/// write back the source file before committing the rename in case a developer forgot some of -/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. -/// -/// To avoid triggering this heuristic we delete the destination first, if it exists. -/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. -#[cfg(target_os = "linux")] -pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { - let _ = std::fs::remove_file(dst); - std::fs::rename(src, dst) -} - /// Returns a version string such as "1.46.0 (04488afe3 2020-08-24)" pub fn version_str() -> Option<&'static str> { option_env!("CFG_VERSION") diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index a0472f98d72..9e4dc702f07 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2858,9 +2858,10 @@ impl ClashingExternDeclarations { let a_poly_sig = a.fn_sig(tcx); let b_poly_sig = b.fn_sig(tcx); - // As we don't compare regions, skip_binder is fine. - let a_sig = a_poly_sig.skip_binder(); - let b_sig = b_poly_sig.skip_binder(); + // We don't compare regions, but leaving bound regions around ICEs, so + // we erase them. + let a_sig = tcx.erase_late_bound_regions(a_poly_sig); + let b_sig = tcx.erase_late_bound_regions(b_poly_sig); (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 5725c240320..04ac50f1d48 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -694,9 +694,8 @@ pub trait LintContext: Sized { } if let Some(span) = in_test_module { - let def_span = self.sess().source_map().guess_head_span(span); db.span_help( - span.shrink_to_lo().to(def_span), + self.sess().source_map().guess_head_span(span), "consider adding a `#[cfg(test)]` to the containing module", ); } @@ -857,6 +856,19 @@ pub trait LintContext: Sized { Applicability::MachineApplicable, ); }, + BuiltinLintDiagnostics::NamedArgumentUsedPositionally(positional_arg, named_arg, name) => { + db.span_label(named_arg, "this named argument is only referred to by position in formatting string"); + if let Some(positional_arg) = positional_arg { + let msg = format!("this formatting argument uses named argument `{}` by position", name); + db.span_label(positional_arg, msg); + db.span_suggestion_verbose( + positional_arg, + "use the named argument by name to avoid ambiguity", + name, + Applicability::MaybeIncorrect, + ); + } + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 5de35dc0856..b0a7a87fda4 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -154,6 +154,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.check_id(closure_id); } } + run_early_pass!(self, check_fn_post, fk, span, id); } @@ -218,7 +219,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node match e.kind { - ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..) + ast::ExprKind::Closure(_, _, ast::Async::Yes { closure_id, .. }, ..) | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), _ => {} } @@ -416,7 +417,7 @@ pub fn check_ast_node<'a>( let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect(); let mut buffered = lint_buffer.unwrap_or_default(); - if sess.opts.debugging_opts.no_interleave_lints { + if sess.opts.unstable_opts.no_interleave_lints { for (i, pass) in passes.iter_mut().enumerate() { buffered = sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 738f475983e..c26d7824758 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'_>, ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> { - // FIXME(rustdoc): Lints which use this function use typecheck results which can cause - // `rustdoc` to error if there are resolution failures. - // - // As internal lints are currently always run if there are `unstable_options`, they are added - // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query. - // Crate lints run outside of a query so rustdoc currently doesn't disable them. - // - // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only - // run internal lints if the user is explicitly opting in or figure out a different way to - // avoid running lints for rustdoc. - if cx.tcx.sess.opts.actually_rustdoc { - return None; - } - match expr.kind { ExprKind::MethodCall(segment, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => @@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics { } } } + +declare_tool_lint! { + pub rustc::BAD_OPT_ACCESS, + Deny, + "prevent using options by field access when there is a wrapper function", + report_in_external_macro: true +} + +declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); + +impl LateLintPass<'_> for BadOptAccess { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ExprKind::Field(base, target) = expr.kind else { return }; + let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; + // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be + // avoided. + if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + return; + } + + for field in adt_def.all_fields() { + if field.name == target.name && + let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && + let Some(items) = attr.meta_item_list() && + let Some(item) = items.first() && + let Some(literal) = item.literal() && + let ast::LitKind::Str(val, _) = literal.kind + { + cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| { + lint.build(val.as_str()).emit(); } + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 27f67207209..afb18451cf3 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -402,7 +402,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>( module_def_id: LocalDefId, builtin_lints: T, ) { - if tcx.sess.opts.debugging_opts.no_interleave_lints { + if tcx.sess.opts.unstable_opts.no_interleave_lints { // These passes runs in late_lint_crate with -Z no_interleave_lints return; } @@ -448,7 +448,7 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) { let mut passes = unerased_lint_store(tcx).late_passes.iter().map(|p| (p)()).collect::<Vec<_>>(); - if !tcx.sess.opts.debugging_opts.no_interleave_lints { + if !tcx.sess.opts.unstable_opts.no_interleave_lints { if !passes.is_empty() { late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] }); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index aaee0caa070..7b0702dad75 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -33,7 +33,7 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iter_order_by)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] @@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_late_pass(|| Box::new(Diagnostics)); + store.register_lints(&BadOptAccess::get_lints()); + store.register_late_pass(|| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); + // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and + // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and + // these lints will trigger all of the time - change this once migration to diagnostic structs + // and translation is completed store.register_group( false, "rustc::internal", @@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), + LintId::of(BAD_OPT_ACCESS), ], ); } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index be4843c7ff1..5c07afeb7aa 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -703,9 +703,8 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi return true; } - // Types with a `#[repr(no_niche)]` attribute have their niche hidden. - // The attribute is used by the UnsafeCell for example (the only use so far). - if def.repr().hide_niche() { + // `UnsafeCell` has its niche hidden. + if def.is_unsafe_cell() { return false; } @@ -1435,10 +1434,6 @@ declare_lint! { /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure /// ordering for any of `AtomicType::compare_exchange`, /// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`. - /// - /// - Passing in a pair of orderings to `AtomicType::compare_exchange`, - /// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update` - /// where the failure ordering is stronger than the success ordering. INVALID_ATOMIC_ORDERING, Deny, "usage of invalid atomic ordering in atomic operations and memory fences" @@ -1545,9 +1540,9 @@ impl InvalidAtomicOrdering { let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak]) else {return }; - let (success_order_arg, fail_order_arg) = match method { - sym::fetch_update => (&args[1], &args[2]), - sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]), + let fail_order_arg = match method { + sym::fetch_update => &args[2], + sym::compare_exchange | sym::compare_exchange_weak => &args[4], _ => return, }; @@ -1569,37 +1564,6 @@ impl InvalidAtomicOrdering { InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span }, ); } - - let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return }; - - if matches!( - (success_ordering, fail_ordering), - (sym::Relaxed | sym::Release, sym::Acquire) - | (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst) - ) { - let success_suggestion = - if success_ordering == sym::Release && fail_ordering == sym::Acquire { - sym::AcqRel - } else { - fail_ordering - }; - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| { - diag.build(fluent::lint::atomic_ordering_invalid_fail_success) - .set_arg("method", method) - .set_arg("fail_ordering", fail_ordering) - .set_arg("success_ordering", success_ordering) - .set_arg("success_suggestion", success_suggestion) - .span_label(fail_order_arg.span, fluent::lint::fail_label) - .span_label(success_order_arg.span, fluent::lint::success_label) - .span_suggestion_short( - success_order_arg.span, - fluent::lint::suggestion, - format!("std::sync::atomic::Ordering::{success_suggestion}"), - Applicability::MaybeIncorrect, - ) - .emit(); - }); - } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9fc2249b290..39690851d1e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3132,6 +3132,60 @@ declare_lint! { "detects unexpected names and values in `#[cfg]` conditions", } +declare_lint! { + /// The `repr_transparent_external_private_fields` lint + /// detects types marked `#[repr(transparent)]` that (transitively) + /// contain an external ZST type marked `#[non_exhaustive]` or containing + /// private fields + /// + /// ### Example + /// + /// ```rust,ignore (needs external crate) + /// #![deny(repr_transparent_external_private_fields)] + /// use foo::NonExhaustiveZst; + /// + /// #[repr(transparent)] + /// struct Bar(u32, ([u32; 0], NonExhaustiveZst)); + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types + /// --> src/main.rs:5:28 + /// | + /// 5 | struct Bar(u32, ([u32; 0], NonExhaustiveZst)); + /// | ^^^^^^^^^^^^^^^^ + /// | + /// note: the lint level is defined here + /// --> src/main.rs:1:9 + /// | + /// 1 | #![deny(repr_transparent_external_private_fields)] + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586> + /// = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + /// ``` + /// + /// ### Explanation + /// + /// Previous, Rust accepted fields that contain external private zero-sized types, + /// even though it should not be a breaking change to add a non-zero-sized field to + /// that private type. + /// + /// This is a [future-incompatible] lint to transition this + /// to a hard error in the future. See [issue #78586] for more details. + /// + /// [issue #78586]: https://github.com/rust-lang/rust/issues/78586 + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + Warn, + "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>", + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -3237,6 +3291,8 @@ declare_lint_pass! { DEPRECATED_WHERE_CLAUSE_LOCATION, TEST_UNSTABLE_LINT, FFI_UNWIND_CALLS, + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + NAMED_ARGUMENTS_USED_POSITIONALLY, ] } @@ -3941,3 +3997,33 @@ declare_lint! { "call to foreign functions or function pointers with FFI-unwind ABI", @feature_gate = sym::c_unwind; } + +declare_lint! { + /// The `named_arguments_used_positionally` lint detects cases where named arguments are only + /// used positionally in format strings. This usage is valid but potentially very confusing. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(named_arguments_used_positionally)] + /// fn main() { + /// let _x = 5; + /// println!("{}", _x = 1); // Prints 1, will trigger lint + /// + /// println!("{}", _x); // Prints 5, no lint emitted + /// println!("{_x}", _x = _x); // Prints 5, no lint emitted + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust formatting strings can refer to named arguments by their position, but this usage is + /// potentially confusing. In particular, readers can incorrectly assume that the declaration + /// of named arguments is an assignment (which would produce the unit type). + /// For backwards compatibility, this is not a hard error. + pub NAMED_ARGUMENTS_USED_POSITIONALLY, + Warn, + "named arguments in format used positionally" +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 48f441e69d6..4fd57ed8533 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -467,6 +467,7 @@ pub enum BuiltinLintDiagnostics { /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, }, + NamedArgumentUsedPositionally(Option<Span>, Span, String), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 7729ec6bef4..62ef5804dce 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -4,6 +4,29 @@ use std::fmt::Display; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; +const OPTIONAL_COMPONENTS: &[&str] = &[ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "m68k", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + "riscv", + "bpf", +]; + +const REQUIRED_COMPONENTS: &[&str] = + &["ipo", "bitreader", "bitwriter", "linker", "asmparser", "lto", "coverage", "instrumentation"]; + fn detect_llvm_link() -> (&'static str, &'static str) { // Force the link mode we want, preferring static by default, but // possibly overridden by `configure --enable-llvm-link-shared`. @@ -76,6 +99,10 @@ fn output(cmd: &mut Command) -> String { } fn main() { + for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) { + println!("cargo:rustc-check-cfg=values(llvm_component,\"{}\")", component); + } + if tracked_env_var_os("RUST_CHECK").is_some() { // If we're just running `check`, there's no need for LLVM to be built. return; @@ -131,42 +158,11 @@ fn main() { let host = env::var("HOST").expect("HOST was not set"); let is_crossed = target != host; - let optional_components = &[ - "x86", - "arm", - "aarch64", - "amdgpu", - "avr", - "m68k", - "mips", - "powerpc", - "systemz", - "jsbackend", - "webassembly", - "msp430", - "sparc", - "nvptx", - "hexagon", - "riscv", - "bpf", - ]; - - let required_components = &[ - "ipo", - "bitreader", - "bitwriter", - "linker", - "asmparser", - "lto", - "coverage", - "instrumentation", - ]; - let components = output(Command::new(&llvm_config).arg("--components")); let mut components = components.split_whitespace().collect::<Vec<_>>(); - components.retain(|c| optional_components.contains(c) || required_components.contains(c)); + components.retain(|c| OPTIONAL_COMPONENTS.contains(c) || REQUIRED_COMPONENTS.contains(c)); - for component in required_components { + for component in REQUIRED_COMPONENTS { if !components.contains(component) { panic!("require llvm component {} but wasn't found", component); } diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index a2b0e9b4d29..93f302b44e8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -84,6 +84,11 @@ enum LLVMRustAttribute { StackProtect = 32, NoUndef = 33, SanitizeMemTag = 34, + NoCfCheck = 35, + ShadowCallStack = 36, + AllocSize = 37, + AllocatedPointer = 38, + AllocAlign = 39, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 0f4973ebf71..0a6bd49992d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -34,6 +34,7 @@ #include "llvm/Transforms/Utils/AddDiscriminators.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/LTO/LTO.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm-c/Transforms/PassManagerBuilder.h" #include "llvm/Transforms/Instrumentation.h" @@ -89,23 +90,6 @@ extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { timeTraceProfilerCleanup(); } -enum class LLVMRustPassKind { - Other, - Function, - Module, -}; - -static LLVMRustPassKind toRust(PassKind Kind) { - switch (Kind) { - case PT_Function: - return LLVMRustPassKind::Function; - case PT_Module: - return LLVMRustPassKind::Module; - default: - return LLVMRustPassKind::Other; - } -} - extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { #if LLVM_VERSION_LT(15, 0) StringRef SR(PassName); @@ -171,12 +155,6 @@ extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) { #endif } -extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { - assert(RustPass); - Pass *Pass = unwrap(RustPass); - return toRust(Pass->getPassKind()); -} - extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { #if LLVM_VERSION_LT(15, 0) assert(RustPass); @@ -1603,28 +1581,6 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, return true; } -extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload - const char*, // importing module name - const char*); // imported module name - -// Calls `module_name_callback` for each module import done by ThinLTO. -// The callback is provided with regular null-terminated C strings. -extern "C" void -LLVMRustGetThinLTOModules(const LLVMRustThinLTOData *data, - LLVMRustModuleNameCallback module_name_callback, - void* callback_payload) { - for (const auto& importing_module : data->ImportLists) { - const std::string importing_module_id = importing_module.getKey().str(); - const auto& imports = importing_module.getValue(); - for (const auto& imported_module : imports) { - const std::string imported_module_id = imported_module.getKey().str(); - module_name_callback(callback_payload, - importing_module_id.c_str(), - imported_module_id.c_str()); - } - } -} - // This struct and various functions are sort of a hack right now, but the // problem is that we've got in-memory LLVM modules after we generate and // optimize all codegen-units for one compilation in rustc. To be compatible @@ -1638,13 +1594,17 @@ struct LLVMRustThinLTOBuffer { }; extern "C" LLVMRustThinLTOBuffer* -LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { +LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { auto Ret = std::make_unique<LLVMRustThinLTOBuffer>(); { raw_string_ostream OS(Ret->data); { legacy::PassManager PM; - PM.add(createWriteThinLTOBitcodePass(OS)); + if (is_thin) { + PM.add(createWriteThinLTOBitcodePass(OS)); + } else { + PM.add(createBitcodeWriterPass(OS)); + } PM.run(*unwrap(M)); } } @@ -1715,7 +1675,7 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data, // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See // the comment in `back/lto.rs` for why this exists. extern "C" void -LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod, +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, DICompileUnit **A, DICompileUnit **B) { Module *M = unwrap(Mod); @@ -1733,7 +1693,7 @@ LLVMRustLTOGetDICompileUnit(LLVMModuleRef Mod, // Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See // the comment in `back/lto.rs` for why this exists. extern "C" void -LLVMRustLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { Module *M = unwrap(Mod); // If the original source module didn't have a `DICompileUnit` then try to diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8c5b4e2dc96..c333738ded4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -6,6 +6,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/Mangler.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" @@ -88,10 +89,6 @@ extern "C" char *LLVMRustGetLastError(void) { return Ret; } -extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) { - return unwrap(M)->getInstructionCount(); -} - extern "C" void LLVMRustSetLastError(const char *Err) { free((void *)LastError); LastError = strdup(Err); @@ -176,6 +173,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NoAlias; case NoCapture: return Attribute::NoCapture; + case NoCfCheck: + return Attribute::NoCfCheck; case NoInline: return Attribute::NoInline; case NonNull: @@ -230,6 +229,16 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NoUndef; case SanitizeMemTag: return Attribute::SanitizeMemTag; + case ShadowCallStack: + return Attribute::ShadowCallStack; + case AllocSize: + return Attribute::AllocSize; +#if LLVM_VERSION_GE(15, 0) + case AllocatedPointer: + return Attribute::AllocatedPointer; + case AllocAlign: + return Attribute::AllocAlign; +#endif } report_fatal_error("bad AttributeKind"); } @@ -292,6 +301,14 @@ extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTy return wrap(Attribute::getWithStructRetType(*unwrap(C), unwrap(Ty))); } +extern "C" LLVMAttributeRef LLVMRustCreateElementTypeAttr(LLVMContextRef C, LLVMTypeRef Ty) { +#if LLVM_VERSION_GE(15, 0) + return wrap(Attribute::get(*unwrap(C), Attribute::ElementType, unwrap(Ty))); +#else + report_fatal_error("Should not be needed on LLVM < 15"); +#endif +} + extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Async) { #if LLVM_VERSION_LT(15, 0) return wrap(Attribute::get(*unwrap(C), Attribute::UWTable)); @@ -301,6 +318,67 @@ extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Asy #endif } +extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) { + return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg, None)); +} + +#if LLVM_VERSION_GE(15, 0) + +// These values **must** match ffi::AllocKindFlags. +// It _happens_ to match the LLVM values of llvm::AllocFnKind, +// but that's happenstance and we do explicit conversions before +// passing them to LLVM. +enum class LLVMRustAllocKindFlags : uint64_t { + Unknown = 0, + Alloc = 1, + Realloc = 1 << 1, + Free = 1 << 2, + Uninitialized = 1 << 3, + Zeroed = 1 << 4, + Aligned = 1 << 5, +}; + +static LLVMRustAllocKindFlags operator&(LLVMRustAllocKindFlags A, LLVMRustAllocKindFlags B) { + return static_cast<LLVMRustAllocKindFlags>(static_cast<uint64_t>(A) & + static_cast<uint64_t>(B)); +} + +static bool isSet(LLVMRustAllocKindFlags F) { return F != LLVMRustAllocKindFlags::Unknown; } + +static llvm::AllocFnKind allocKindFromRust(LLVMRustAllocKindFlags F) { + llvm::AllocFnKind AFK = llvm::AllocFnKind::Unknown; + if (isSet(F & LLVMRustAllocKindFlags::Alloc)) { + AFK |= llvm::AllocFnKind::Alloc; + } + if (isSet(F & LLVMRustAllocKindFlags::Realloc)) { + AFK |= llvm::AllocFnKind::Realloc; + } + if (isSet(F & LLVMRustAllocKindFlags::Free)) { + AFK |= llvm::AllocFnKind::Free; + } + if (isSet(F & LLVMRustAllocKindFlags::Uninitialized)) { + AFK |= llvm::AllocFnKind::Uninitialized; + } + if (isSet(F & LLVMRustAllocKindFlags::Zeroed)) { + AFK |= llvm::AllocFnKind::Zeroed; + } + if (isSet(F & LLVMRustAllocKindFlags::Aligned)) { + AFK |= llvm::AllocFnKind::Aligned; + } + return AFK; +} +#endif + +extern "C" LLVMAttributeRef LLVMRustCreateAllocKindAttr(LLVMContextRef C, uint64_t AllocKindArg) { +#if LLVM_VERSION_GE(15, 0) + return wrap(Attribute::get(*unwrap(C), Attribute::AllocKind, + static_cast<uint64_t>(allocKindFromRust(static_cast<LLVMRustAllocKindFlags>(AllocKindArg))))); +#else + report_fatal_error( + "allockind attributes are new in LLVM 15 and should not be used on older LLVMs"); +#endif +} + // Enable a fast-math flag // // https://llvm.org/docs/LangRef.html#fast-math-flags @@ -411,8 +489,14 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, size_t ConstraintsLen) { +#if LLVM_VERSION_LT(15, 0) return InlineAsm::Verify(unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen)); +#else + // llvm::Error converts to true if it is an error. + return !llvm::errorToBool(InlineAsm::verify( + unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen))); +#endif } extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, @@ -1450,11 +1534,6 @@ extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, } } -extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { - GlobalObject *GV = unwrap<GlobalObject>(V); - GV->setComdat(nullptr); -} - enum class LLVMRustLinkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, @@ -1873,3 +1952,16 @@ extern "C" LLVMValueRef LLVMGetAggregateElement(LLVMValueRef C, unsigned Idx) { return wrap(unwrap<Constant>(C)->getAggregateElement(Idx)); } #endif + +extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { +#if LLVM_VERSION_GE(15, 0) + auto *CB = unwrap<CallBase>(CallSite); + switch (CB->getIntrinsicID()) { + case Intrinsic::arm_ldrex: + return 0; + case Intrinsic::arm_strex: + return 1; + } +#endif + return -1; +} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 027f377b0ac..6b5b8b59320 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some(DiagnosticDeriveKind::Lint), _) => { - span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") + span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported") .help("use the `#[error(...)]` attribute to create a error") .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 74ce1ab08c2..6c9561925fe 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -8,12 +8,13 @@ use crate::diagnostics::utils::{ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, + parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta, + Path, Type, }; use synstructure::{BindingInfo, Structure}; @@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder { } pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { - // Keep track of which fields are subdiagnostics or have no attributes. - let mut subdiagnostics_or_empty = std::collections::HashSet::new(); + // Keep track of which fields need to be handled with a by-move binding. + let mut needs_moved = std::collections::HashSet::new(); // Generates calls to `span_label` and similar functions based on the attributes // on fields. Code for suggestions uses formatting machinery and the value of @@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder { let attrs = structure .clone() .filter(|field_binding| { - let attrs = &field_binding.ast().attrs; - - (!attrs.is_empty() - && attrs.iter().all(|attr| { - "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() - })) - || { - subdiagnostics_or_empty.insert(field_binding.binding.clone()); - false - } + let ast = &field_binding.ast(); + !self.needs_move(ast) || { + needs_moved.insert(field_binding.binding.clone()); + false + } }) .each(|field_binding| self.generate_field_attrs_code(field_binding)); @@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder { // attributes or a `#[subdiagnostic]` attribute then it must be passed as an // argument to the diagnostic so that it can be referred to by Fluent messages. let args = structure - .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding)) + .filter(|field_binding| needs_moved.contains(&field_binding.binding)) .each(|field_binding| self.generate_field_attrs_code(field_binding)); (attrs, args) } + /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic + /// call (like `span_label`). + fn should_generate_set_arg(&self, field: &Field) -> bool { + field.attrs.is_empty() + } + + /// Returns `true` if `field` needs to have code generated in the by-move branch of the + /// generated derive rather than the by-ref branch. + fn needs_move(&self, field: &Field) -> bool { + let generates_set_arg = self.should_generate_set_arg(field); + let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); + // FIXME(davidtwco): better support for one field needing to be in the by-move and + // by-ref branches. + let is_subdiagnostic = field + .attrs + .iter() + .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) + .any(|attr| attr == "subdiagnostic"); + + // `set_arg` calls take their argument by-move.. + generates_set_arg + // If this is a `MultiSpan` field then it needs to be moved to be used by any + // attribute.. + || is_multispan + // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is + // unlikely to be `Copy`.. + || is_subdiagnostic + } + /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates /// diagnostic builder calls for setting error code and creating note/help messages. @@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder { let name = name.as_str(); let meta = attr.parse_meta()?; - let is_help_or_note = matches!(name, "help" | "note"); + let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_"); let nested = match meta { // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or @@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder { Meta::List(MetaList { ref nested, .. }) => nested, // Subdiagnostics without spans can be applied to the type too, and these are just // paths: `#[help]` and `#[note]` - Meta::Path(_) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); + Meta::Path(_) if is_help_note_or_warn => { + let fn_name = if name == "warn_" { + Ident::new("warn", attr.span()) + } else { + Ident::new(name, attr.span()) + }; return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); } _ => throw_invalid_attr!(attr, &meta), @@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder { "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), - "help" | "note" => (), + "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error`, `warning`, `help` and `note` are valid attributes") + diag.help( + "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes", + ) }), } @@ -163,14 +194,16 @@ impl DiagnosticDeriveBuilder { let mut nested_iter = nested.into_iter(); if let Some(nested_attr) = nested_iter.next() { // Report an error if there are any other list items after the path. - if is_help_or_note && nested_iter.next().is_some() { + if is_help_note_or_warn && nested_iter.next().is_some() { throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`help` and `note` struct attributes can only have one argument") + diag.help( + "`help`, `note` and `warn_` struct attributes can only have one argument", + ) }); } match nested_attr { - NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => { let fn_name = proc_macro2::Ident::new(name, attr.span()); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); } @@ -178,8 +211,8 @@ impl DiagnosticDeriveBuilder { self.slug.set_once((path.clone(), span)); } NestedMeta::Meta(meta @ Meta::NameValue(_)) - if !is_help_or_note - && meta.path().segments.last().unwrap().ident.to_string() == "code" => + if !is_help_note_or_warn + && meta.path().segments.last().unwrap().ident == "code" => { // don't error for valid follow-up attributes } @@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder { let field = binding_info.ast(); let field_binding = &binding_info.binding; - let inner_ty = FieldInnerTy::from_type(&field.ty); - - // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than - // borrow it to avoid requiring clones - this must therefore be the last use of - // each field (for example, any formatting machinery that might refer to a field - // should be generated already). - if field.attrs.is_empty() { + if self.should_generate_set_arg(&field) { let diag = &self.diag; let ident = field.ident.as_ref().unwrap(); - quote! { + return quote! { #diag.set_arg( stringify!(#ident), #field_binding ); - } - } else { - field - .attrs - .iter() - .map(move |attr| { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { - // `primary_span` can accept a `Vec<Span>` so don't destructure that. - ("primary_span", FieldInnerTy::Vec(_)) => { - (quote! { #field_binding.clone() }, false) - } - // `subdiagnostics` are not derefed because they are bound by value. - ("subdiagnostic", _) => (quote! { #field_binding }, true), - _ => (quote! { *#field_binding }, true), - }; - - let generated_code = self - .generate_inner_field_code( - attr, - FieldInfo { - binding: binding_info, - ty: inner_ty.inner_type().unwrap_or(&field.ty), - span: &field.span(), - }, - binding, - ) - .unwrap_or_else(|v| v.to_compile_error()); - - if needs_destructure { - inner_ty.with(field_binding, generated_code) - } else { - generated_code - } - }) - .collect() + }; } + + let needs_move = self.needs_move(&field); + let inner_ty = FieldInnerTy::from_type(&field.ty); + + field + .attrs + .iter() + .map(move |attr| { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let needs_clone = + name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); + let (binding, needs_destructure) = if needs_clone { + // `primary_span` can accept a `Vec<Span>` so don't destructure that. + (quote! { #field_binding.clone() }, false) + } else if needs_move { + (quote! { #field_binding }, true) + } else { + (quote! { *#field_binding }, true) + }; + + let generated_code = self + .generate_inner_field_code( + attr, + FieldInfo { + binding: binding_info, + ty: inner_ty.inner_type().unwrap_or(&field.ty), + span: &field.span(), + }, + binding, + ) + .unwrap_or_else(|v| v.to_compile_error()); + + if needs_destructure { + inner_ty.with(field_binding, generated_code) + } else { + generated_code + } + }) + .collect() } fn generate_inner_field_code( @@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) } - "note" | "help" => { - let path = match name { - "note" => parse_quote! { _subdiag::note }, - "help" => parse_quote! { _subdiag::help }, + "note" | "help" | "warn_" => { + let warn_ident = Ident::new("warn", Span::call_site()); + let (ident, path) = match name { + "note" => (ident, parse_quote! { _subdiag::note }), + "help" => (ident, parse_quote! { _subdiag::help }), + "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }), _ => unreachable!(), }; if type_matches_path(&info.ty, &["rustc_span", "Span"]) { @@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder { "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { return self.generate_inner_field_code_suggestion(attr, info); } - "label" | "help" | "note" => (), + "label" | "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \ valid field attributes", ) }), @@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder { Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) } "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - "note" | "help" => report_type_error(attr, "`Span` or `()`")?, + // `warn_` must be special-cased because the attribute `warn` already has meaning and + // so isn't used, despite the diagnostic API being named `warn`. + "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self + .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)), + "warn_" if type_is_unit(&info.ty) => { + Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg)) + } + "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?, _ => unreachable!(), } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2758fcd1310..562d5e9f4d2 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -194,8 +194,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok let snake_name = Ident::new( // FIXME: should probably trim prefix, not replace all occurrences &name - .replace(&format!("{}-", res.ident).replace("_", "-"), "") - .replace("-", "_"), + .replace(&format!("{}-", res.ident).replace('_', "-"), "") + .replace('-', "_"), span, ); constants.extend(quote! { @@ -207,7 +207,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok }); for Attribute { id: Identifier { name: attr_name }, .. } in attributes { - let snake_name = Ident::new(&attr_name.replace("-", "_"), span); + let snake_name = Ident::new(&attr_name.replace('-', "_"), span); if !previous_attrs.insert(snake_name.clone()) { continue; } @@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok #generated pub mod _subdiag { - pub const note: crate::SubdiagnosticMessage = - crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); pub const help: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); + pub const note: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); + pub const warn: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn")); pub const label: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); pub const suggestion: crate::SubdiagnosticMessage = diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 2a5b6beba94..edf4dbed985 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -37,6 +37,8 @@ enum SubdiagnosticKind { Note, /// `#[help(...)]` Help, + /// `#[warn_(...)]` + Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` Suggestion(SubdiagnosticSuggestionKind), } @@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind { "label" => Ok(SubdiagnosticKind::Label), "note" => Ok(SubdiagnosticKind::Note), "help" => Ok(SubdiagnosticKind::Help), + "warn_" => Ok(SubdiagnosticKind::Warn), "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), "suggestion_short" => { Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) @@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { write!(f, "suggestion") } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 8977db4606c..002abb152f7 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, ) -> Result<(), DiagnosticDeriveError> { - report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") + if !type_matches_path(&info.ty, &["rustc_span", "Span"]) + && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"]) + { + report_type_error(attr, "`Span` or `MultiSpan`")?; + } + + Ok(()) } /// Inner type of a field and type of wrapper. diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 168530c54b9..ab509b26f1c 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -130,8 +130,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -148,8 +149,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -166,6 +168,7 @@ decl_derive!( label, help, note, + warn_, suggestion, suggestion_short, suggestion_hidden, diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 3ab4a8b7294..2c5db9d8b27 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -12,6 +12,7 @@ odht = { version = "0.3.1", features = ["nightly"] } snap = "1" tracing = "0.1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +tempfile = "3.2" rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index cb50c0fb738..708d0b1fd8a 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -456,7 +456,7 @@ impl<'a> CrateLoader<'a> { proc_macro_locator.is_proc_macro = true; // Load the proc macro crate for the target - let (locator, target_result) = if self.sess.opts.debugging_opts.dual_proc_macros { + let (locator, target_result) = if self.sess.opts.unstable_opts.dual_proc_macros { proc_macro_locator.reset(); let result = match self.load(&mut proc_macro_locator)? { Some(LoadResult::Previous(cnum)) => { @@ -485,7 +485,7 @@ impl<'a> CrateLoader<'a> { return Ok(None); }; - Ok(Some(if self.sess.opts.debugging_opts.dual_proc_macros { + Ok(Some(if self.sess.opts.unstable_opts.dual_proc_macros { let host_result = match host_result { LoadResult::Previous(..) => { panic!("host and target proc macros must be loaded in lock-step") @@ -762,9 +762,9 @@ impl<'a> CrateLoader<'a> { } fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { - if self.sess.opts.debugging_opts.no_profiler_runtime + if self.sess.opts.unstable_opts.no_profiler_runtime || !(self.sess.instrument_coverage() - || self.sess.opts.debugging_opts.profile + || self.sess.opts.unstable_opts.profile || self.sess.opts.cg.profile_generate.enabled()) { return; @@ -772,7 +772,7 @@ impl<'a> CrateLoader<'a> { info!("loading profiler"); - let name = Symbol::intern(&self.sess.opts.debugging_opts.profiler_runtime); + let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime); if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) { self.sess.err( "`profiler_builtins` crate (required by compiler options) \ diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 770d164894a..b765c34f8e3 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -419,7 +419,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { } let found_drop_strategy = tcx.panic_in_drop_strategy(cnum); - if tcx.sess.opts.debugging_opts.panic_in_drop != found_drop_strategy { + if tcx.sess.opts.unstable_opts.panic_in_drop != found_drop_strategy { sess.err(&format!( "the crate `{}` is compiled with the \ panic-in-drop strategy `{}` which is \ @@ -427,7 +427,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) { strategy of `{}`", tcx.crate_name(cnum), found_drop_strategy.desc(), - tcx.sess.opts.debugging_opts.panic_in_drop.desc() + tcx.sess.opts.unstable_opts.panic_in_drop.desc() )); } } diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs new file mode 100644 index 00000000000..e6072901aaa --- /dev/null +++ b/compiler/rustc_metadata/src/fs.rs @@ -0,0 +1,137 @@ +use crate::{encode_metadata, EncodedMetadata}; + +use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, OutputFilenames, OutputType}; +use rustc_session::output::filename_for_metadata; +use rustc_session::Session; +use tempfile::Builder as TempFileBuilder; + +use std::fs; +use std::path::{Path, PathBuf}; + +// FIXME(eddyb) maybe include the crate name in this? +pub const METADATA_FILENAME: &str = "lib.rmeta"; + +/// We use a temp directory here to avoid races between concurrent rustc processes, +/// such as builds in the same directory using the same filename for metadata while +/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a +/// directory being searched for `extern crate` (observing an incomplete file). +/// The returned path is the temporary file containing the complete metadata. +pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf { + let out_filename = tmpdir.as_ref().join(METADATA_FILENAME); + let result = fs::write(&out_filename, metadata); + + if let Err(e) = result { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + + out_filename +} + +pub fn encode_and_write_metadata( + tcx: TyCtxt<'_>, + outputs: &OutputFilenames, +) -> (EncodedMetadata, bool) { + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed, + } + + let metadata_kind = tcx + .sess + .crate_types() + .iter() + .map(|ty| match *ty { + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, + + CrateType::Rlib => MetadataKind::Uncompressed, + + CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None); + + let crate_name = tcx.crate_name(LOCAL_CRATE); + let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with an `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = TempFileBuilder::new() + .prefix("rmeta") + .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); + let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); + let metadata_filename = metadata_tmpdir.as_ref().join(METADATA_FILENAME); + + // Always create a file at `metadata_filename`, even if we have nothing to write to it. + // This simplifies the creation of the output `out_filename` when requested. + match metadata_kind { + MetadataKind::None => { + std::fs::File::create(&metadata_filename).unwrap_or_else(|e| { + tcx.sess.fatal(&format!( + "failed to create the file {}: {}", + metadata_filename.display(), + e + )) + }); + } + MetadataKind::Uncompressed | MetadataKind::Compressed => { + encode_metadata(tcx, &metadata_filename); + } + }; + + let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata"); + + // If the user requests metadata as output, rename `metadata_filename` + // to the expected output `out_filename`. The match above should ensure + // this file always exists. + let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); + let (metadata_filename, metadata_tmpdir) = if need_metadata_file { + if let Err(e) = non_durable_rename(&metadata_filename, &out_filename) { + tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + if tcx.sess.opts.json_artifact_notifications { + tcx.sess + .parse_sess + .span_diagnostic + .emit_artifact_notification(&out_filename, "metadata"); + } + (out_filename, None) + } else { + (metadata_filename, Some(metadata_tmpdir)) + }; + + // Load metadata back to memory: codegen may need to include it in object files. + let metadata = + EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|e| { + tcx.sess.fatal(&format!("failed to create encoded metadata from file: {}", e)) + }); + + let need_metadata_module = metadata_kind == MetadataKind::Compressed; + + (metadata, need_metadata_module) +} + +#[cfg(not(target_os = "linux"))] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + std::fs::rename(src, dst) +} + +/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems +/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" +/// write back the source file before committing the rename in case a developer forgot some of +/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. +/// +/// To avoid triggering this heuristic we delete the destination first, if it exists. +/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. +#[cfg(target_os = "linux")] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + let _ = std::fs::remove_file(dst); + std::fs::rename(src, dst) +} diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 5ad16398695..e192378bcc3 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -4,7 +4,7 @@ #![feature(generators)] #![feature(generic_associated_types)] #![feature(iter_from_generator)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(once_cell)] #![feature(proc_macro_internals)] @@ -34,6 +34,8 @@ mod native_libs; mod rmeta; pub mod creader; +pub mod fs; pub mod locator; +pub use fs::{emit_metadata, METADATA_FILENAME}; pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER}; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index dbe53224e2a..2c1c84b0be2 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -67,7 +67,7 @@ //! //! ## What criterion to select on? //! -//! This a pretty tricky area of loading crates. Given a file, how do we know +//! This is a pretty tricky area of loading crates. Given a file, how do we know //! whether it's the right crate? Currently, the rules look along these lines: //! //! 1. Does the filename match an rlib/dylib pattern? That is to say, does the @@ -1179,7 +1179,7 @@ impl CrateError { err.help("consider building the standard library from source with `cargo build -Zbuild-std`"); } } else if crate_name - == Symbol::intern(&sess.opts.debugging_opts.profiler_runtime) + == Symbol::intern(&sess.opts.unstable_opts.profiler_runtime) { err.note("the compiler may have been built without the profiler runtime"); } else if crate_name.as_str().starts_with("rustc_") { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index f0ccf02c9fa..8fa703a7760 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -21,7 +21,6 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc_middle::thir; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::GeneratorDiagnosticData; @@ -42,7 +41,7 @@ use std::io; use std::iter::TrustedLen; use std::mem; use std::num::NonZeroUsize; -use std::path::PathBuf; +use std::path::Path; use tracing::debug; pub(super) use cstore_impl::provide; @@ -638,7 +637,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { } } -impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] { +impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) } @@ -952,6 +951,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) } + /// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute + /// has an `implied_by` meta item, then the mapping from the implied feature to the actual + /// feature is a stability implication). + fn get_stability_implications(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Symbol)] { + tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self)) + } + /// Iterates over the language items in the given crate. fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { tcx.arena.alloc_from_iter( @@ -1468,30 +1474,30 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] { + fn filter<'a>(sess: &Session, path: Option<&'a Path>) -> Option<&'a Path> { + path.filter(|_| { + // Only spend time on further checks if we have what to translate *to*. + sess.opts.real_rust_source_base_dir.is_some() + // Some tests need the translation to be always skipped. + && sess.opts.unstable_opts.translate_remapped_path_to_local_path + }) + .filter(|virtual_dir| { + // Don't translate away `/rustc/$hash` if we're still remapping to it, + // since that means we're still building `std`/`rustc` that need it, + // and we don't want the real path to leak into codegen/debuginfo. + !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) + }) + } + // Translate the virtual `/rustc/$hash` prefix back to a real directory // that should hold actual sources, where possible. // // NOTE: if you update this, you might need to also update bootstrap's code for generating // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. let virtual_rust_source_base_dir = [ - option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from), - sess.opts.debugging_opts.simulate_remapped_rust_src_base.clone(), - ] - .into_iter() - .filter(|_| { - // Only spend time on further checks if we have what to translate *to*. - sess.opts.real_rust_source_base_dir.is_some() - // Some tests need the translation to be always skipped. - && sess.opts.debugging_opts.translate_remapped_path_to_local_path - }) - .flatten() - .filter(|virtual_dir| { - // Don't translate away `/rustc/$hash` if we're still remapping to it, - // since that means we're still building `std`/`rustc` that need it, - // and we don't want the real path to leak into codegen/debuginfo. - !sess.opts.remap_path_prefix.iter().any(|(_from, to)| to == virtual_dir) - }) - .collect::<Vec<_>>(); + filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), + filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), + ]; let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { debug!( @@ -1500,7 +1506,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir, ); - for virtual_dir in &virtual_rust_source_base_dir { + for virtual_dir in virtual_rust_source_base_dir.iter().flatten() { if let Some(real_dir) = &sess.opts.real_rust_source_base_dir { if let rustc_span::FileName::Real(old_name) = name { if let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } = @@ -1584,7 +1590,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // `try_to_translate_virtual_to_real` don't have to worry about how the // compiler is bootstrapped. if let Some(virtual_dir) = - &sess.opts.debugging_opts.simulate_remapped_rust_src_base + &sess.opts.unstable_opts.simulate_remapped_rust_src_base { if let Some(real_dir) = &sess.opts.real_rust_source_base_dir { if let rustc_span::FileName::Real(ref mut old_name) = name { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 565eec18ea9..6bf237b8ed5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -291,6 +291,9 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } + stability_implications => { + cdata.get_stability_implications(tcx).iter().copied().collect() + } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } @@ -372,9 +375,13 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { use std::collections::vec_deque::VecDeque; let mut visible_parent_map: DefIdMap<DefId> = Default::default(); - // This is a secondary visible_parent_map, storing the DefId of parents that re-export - // the child as `_`. Since we prefer parents that don't do this, merge this map at the - // end, only if we're missing any keys from the former. + // This is a secondary visible_parent_map, storing the DefId of + // parents that re-export the child as `_` or module parents + // which are `#[doc(hidden)]`. Since we prefer paths that don't + // do this, merge this map at the end, only if we're missing + // keys from the former. + // This is a rudimentary check that does not catch all cases, + // just the easiest. let mut fallback_map: DefIdMap<DefId> = Default::default(); // Issue 46112: We want the map to prefer the shortest @@ -409,6 +416,11 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { return; } + if ty::util::is_doc_hidden(tcx, parent) { + fallback_map.insert(def_id, parent); + return; + } + match visible_parent_map.entry(def_id) { Entry::Occupied(mut entry) => { // If `child` is defined in crate `cnum`, ensure @@ -436,8 +448,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } } - // Fill in any missing entries with the (less preferable) path ending in `::_`. - // We still use this path in a diagnostic that suggests importing `::*`. + // Fill in any missing entries with the less preferable path. + // If this path re-exports the child as `_`, we still use this + // path in a diagnostic that suggests importing `::*`. for (child, parent) in fallback_map { visible_parent_map.entry(child).or_insert(parent); } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index caf5965c3a4..50d983754e8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -4,8 +4,10 @@ use crate::rmeta::*; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator}; +use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{ @@ -27,8 +29,7 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; -use rustc_serialize::opaque::MemEncoder; -use rustc_serialize::{Encodable, Encoder}; +use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}; @@ -39,12 +40,14 @@ use rustc_span::{ use rustc_target::abi::VariantIdx; use std::borrow::Borrow; use std::hash::Hash; +use std::io::{Read, Seek, Write}; use std::iter; use std::num::NonZeroUsize; +use std::path::{Path, PathBuf}; use tracing::{debug, trace}; pub(super) struct EncodeContext<'a, 'tcx> { - opaque: MemEncoder, + opaque: opaque::FileEncoder, tcx: TyCtxt<'tcx>, feat: &'tcx rustc_feature::Features, @@ -535,6 +538,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let lib_features = self.encode_lib_features(); let lib_feature_bytes = self.position() - i; + // Encode the stability implications. + i = self.position(); + let stability_implications = self.encode_stability_implications(); + let stability_implications_bytes = self.position() - i; + // Encode the language items. i = self.position(); let lang_items = self.encode_lang_items(); @@ -665,7 +673,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hash: tcx.crate_hash(LOCAL_CRATE), stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), - panic_in_drop_strategy: tcx.sess.opts.debugging_opts.panic_in_drop, + panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), @@ -683,6 +691,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { crate_deps, dylib_dependency_formats, lib_features, + stability_implications, lang_items, diagnostic_items, lang_items_missing, @@ -707,6 +716,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let computed_total_bytes = preamble_bytes + dep_bytes + lib_feature_bytes + + stability_implications_bytes + lang_item_bytes + diagnostic_item_bytes + native_lib_bytes @@ -729,12 +739,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { assert_eq!(total_bytes, computed_total_bytes); if tcx.sess.meta_stats() { + self.opaque.flush(); + + // Rewind and re-read all the metadata to count the zero bytes we wrote. + let pos_before_rewind = self.opaque.file().stream_position().unwrap(); let mut zero_bytes = 0; - for e in self.opaque.data.iter() { - if *e == 0 { + self.opaque.file().rewind().unwrap(); + let file = std::io::BufReader::new(self.opaque.file()); + for e in file.bytes() { + if e.unwrap() == 0 { zero_bytes += 1; } } + assert_eq!(self.opaque.file().stream_position().unwrap(), pos_before_rewind); let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64; let p = |label, bytes| { @@ -751,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { p("preamble", preamble_bytes); p("dep", dep_bytes); p("lib feature", lib_feature_bytes); + p("stability_implications", stability_implications_bytes); p("lang item", lang_item_bytes); p("diagnostic item", diagnostic_item_bytes); p("native lib", native_lib_bytes); @@ -862,7 +880,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { // Constructors DefKind::Ctor(_, _) => { let mir_opt_base = tcx.sess.opts.output_types.should_codegen() - || tcx.sess.opts.debugging_opts.always_encode_mir; + || tcx.sess.opts.unstable_opts.always_encode_mir; (true, mir_opt_base) } // Constants @@ -880,7 +898,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { // The function has a `const` modifier or is in a `#[const_trait]`. let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id()) || tcx.is_const_default_method(def_id.to_def_id()); - let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir; + let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir; (is_const_fn, needs_inline || always_encode_mir) } // Closures can't be const fn. @@ -889,7 +907,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { let needs_inline = (generics.requires_monomorphization(tcx) || tcx.codegen_fn_attrs(def_id).requests_inline()) && tcx.sess.opts.output_types.should_codegen(); - let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir; + let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir; (false, needs_inline || always_encode_mir) } // Generators require optimized MIR to compute layout. @@ -1360,7 +1378,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. - if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { + if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { record!(self.tables.lookup_stability[def_id] <- stab) } @@ -1372,7 +1390,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. - if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { + if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { record!(self.tables.lookup_const_stability[def_id] <- stab) } @@ -1767,6 +1785,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(lib_features.to_vec()) } + fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> { + empty_proc_macro!(self); + let tcx = self.tcx; + let implications = tcx.stability_implications(LOCAL_CRATE); + self.lazy_array(implications.iter().map(|(k, v)| (*k, *v))) + } + fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { empty_proc_macro!(self); let tcx = self.tcx; @@ -2133,24 +2158,58 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { // will allow us to slice the metadata to the precise length that we just // generated regardless of trailing bytes that end up in it. -#[derive(Encodable, Decodable)] pub struct EncodedMetadata { - raw_data: Vec<u8>, + // The declaration order matters because `mmap` should be dropped before `_temp_dir`. + mmap: Option<Mmap>, + // We need to carry MaybeTempDir to avoid deleting the temporary + // directory while accessing the Mmap. + _temp_dir: Option<MaybeTempDir>, } impl EncodedMetadata { #[inline] - pub fn new() -> EncodedMetadata { - EncodedMetadata { raw_data: Vec::new() } + pub fn from_path(path: PathBuf, temp_dir: Option<MaybeTempDir>) -> std::io::Result<Self> { + let file = std::fs::File::open(&path)?; + let file_metadata = file.metadata()?; + if file_metadata.len() == 0 { + return Ok(Self { mmap: None, _temp_dir: None }); + } + let mmap = unsafe { Some(Mmap::map(file)?) }; + Ok(Self { mmap, _temp_dir: temp_dir }) } #[inline] pub fn raw_data(&self) -> &[u8] { - &self.raw_data + self.mmap.as_ref().map(|mmap| mmap.as_ref()).unwrap_or_default() + } +} + +impl<S: Encoder> Encodable<S> for EncodedMetadata { + fn encode(&self, s: &mut S) { + let slice = self.raw_data(); + slice.encode(s) + } +} + +impl<D: Decoder> Decodable<D> for EncodedMetadata { + fn decode(d: &mut D) -> Self { + let len = d.read_usize(); + let mmap = if len > 0 { + let mut mmap = MmapMut::map_anon(len).unwrap(); + for _ in 0..len { + (&mut mmap[..]).write(&[d.read_u8()]).unwrap(); + } + mmap.flush().unwrap(); + Some(mmap.make_read_only().unwrap()) + } else { + None + }; + + Self { mmap, _temp_dir: None } } } -pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { +pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); // Since encoding metadata is not in a query, and nothing is cached, @@ -2158,7 +2217,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { tcx.dep_graph.assert_ignored(); join( - || encode_metadata_impl(tcx), + || encode_metadata_impl(tcx, path), || { if tcx.sess.threads() == 1 { return; @@ -2168,12 +2227,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { // It can be removed if it turns out to cause trouble or be detrimental to performance. join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); }, - ) - .0 + ); } -fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { - let mut encoder = MemEncoder::new(); +fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) { + let mut encoder = opaque::FileEncoder::new(path) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to create file encoder: {}", err))); encoder.emit_raw_bytes(METADATA_HEADER); // Will be filled with the root position after encoding everything. @@ -2208,20 +2267,29 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { // culminating in the `CrateRoot` which points to all of it. let root = ecx.encode_crate_root(); - let mut result = ecx.opaque.finish(); + ecx.opaque.flush(); + + let mut file = ecx.opaque.file(); + // We will return to this position after writing the root position. + let pos_before_seek = file.stream_position().unwrap(); // Encode the root position. let header = METADATA_HEADER.len(); + file.seek(std::io::SeekFrom::Start(header as u64)) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to seek the file: {}", err))); let pos = root.position.get(); - result[header + 0] = (pos >> 24) as u8; - result[header + 1] = (pos >> 16) as u8; - result[header + 2] = (pos >> 8) as u8; - result[header + 3] = (pos >> 0) as u8; + file.write_all(&[(pos >> 24) as u8, (pos >> 16) as u8, (pos >> 8) as u8, (pos >> 0) as u8]) + .unwrap_or_else(|err| tcx.sess.fatal(&format!("failed to write to the file: {}", err))); - // Record metadata size for self-profiling - tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64); + // Return to the position where we are before writing the root position. + file.seek(std::io::SeekFrom::Start(pos_before_seek)).unwrap(); - EncodedMetadata { raw_data: result } + // Record metadata size for self-profiling + tcx.prof.artifact_size( + "crate_metadata", + "crate_metadata", + file.metadata().unwrap().len() as u64, + ); } pub fn provide(providers: &mut Providers) { @@ -2242,5 +2310,5 @@ pub fn provide(providers: &mut Providers) { }, ..*providers - }; + } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a50eb2a71cf..0f291f92647 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -17,12 +17,11 @@ use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::mir; -use rustc_middle::thir; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, ReprOptions, Ty}; use rustc_middle::ty::{GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt}; -use rustc_serialize::opaque::MemEncoder; +use rustc_serialize::opaque::FileEncoder; use rustc_session::config::SymbolManglingVersion; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; @@ -227,6 +226,7 @@ pub(crate) struct CrateRoot { crate_deps: LazyArray<CrateDep>, dylib_dependency_formats: LazyArray<Option<LinkagePreference>>, lib_features: LazyArray<(Symbol, Option<Symbol>)>, + stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, usize)>, lang_items_missing: LazyArray<lang_items::LangItem>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, @@ -323,7 +323,7 @@ macro_rules! define_tables { } impl TableBuilders { - fn encode(&self, buf: &mut MemEncoder) -> LazyTables { + fn encode(&self, buf: &mut FileEncoder) -> LazyTables { LazyTables { $($name: self.$name.encode(buf)),+ } @@ -361,7 +361,7 @@ define_tables! { mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>, promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>, // FIXME(compiler-errors): Why isn't this a LazyArray? - thir_abstract_const: Table<DefIndex, LazyValue<&'static [thir::abstract_const::Node<'static>]>>, + thir_abstract_const: Table<DefIndex, LazyValue<&'static [ty::abstract_const::Node<'static>]>>, impl_parent: Table<DefIndex, RawDefId>, impl_polarity: Table<DefIndex, ty::ImplPolarity>, constness: Table<DefIndex, hir::Constness>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 5ab4269ae99..42759f0a652 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -4,8 +4,8 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::def::{CtorKind, CtorOf}; use rustc_index::vec::Idx; use rustc_middle::ty::ParameterizedOverTcx; -use rustc_serialize::opaque::MemEncoder; -use rustc_serialize::Encoder; +use rustc_serialize::opaque::FileEncoder; +use rustc_serialize::Encoder as _; use rustc_span::hygiene::MacroKind; use std::convert::TryInto; use std::marker::PhantomData; @@ -281,7 +281,7 @@ where Some(value).write_to_bytes(&mut self.blocks[i]); } - pub(crate) fn encode<const N: usize>(&self, buf: &mut MemEncoder) -> LazyTable<I, T> + pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T> where Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>, { diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 661a9b1944c..b94de537dc8 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -55,6 +55,7 @@ macro_rules! arena_types { [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, + [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>, [] type_op_subtype: rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, ()> diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 3e99ba5742a..0001e1aa53e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -22,7 +22,7 @@ fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), - Node::Expr(Expr { kind: ExprKind::Closure { fn_decl, .. }, .. }) + Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, ..), .. }) => { Some(fn_decl) } @@ -39,6 +39,7 @@ pub fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { } } +#[inline] pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> { match node { Node::Item(Item { @@ -54,7 +55,7 @@ pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> { kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), .. }) - | Node::Expr(Expr { kind: ExprKind::Closure { body, .. }, .. }) => Some(*body), + | Node::Expr(Expr { kind: ExprKind::Closure(Closure { body, .. }), .. }) => Some(*body), Node::AnonConst(constant) => Some(constant.body), @@ -279,8 +280,8 @@ impl<'hir> Map<'hir> { } Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { - ExprKind::Closure { movability: None, .. } => DefKind::Closure, - ExprKind::Closure { movability: Some(_), .. } => DefKind::Generator, + ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure, + ExprKind::Closure(Closure { movability: Some(_), .. }) => DefKind::Generator, _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), }, Node::GenericParam(param) => match param.kind { @@ -486,35 +487,11 @@ impl<'hir> Map<'hir> { /// crate. If you would prefer to iterate over the bodies /// themselves, you can do `self.hir().krate().body_ids.iter()`. pub fn body_owners(self) -> impl Iterator<Item = LocalDefId> + 'hir { - self.krate() - .owners - .iter_enumerated() - .flat_map(move |(owner, owner_info)| { - let bodies = &owner_info.as_owner()?.nodes.bodies; - Some(bodies.iter().map(move |&(local_id, _)| { - let hir_id = HirId { owner, local_id }; - let body_id = BodyId { hir_id }; - self.body_owner_def_id(body_id) - })) - }) - .flatten() + self.tcx.hir_crate_items(()).body_owners.iter().copied() } pub fn par_body_owners<F: Fn(LocalDefId) + Sync + Send>(self, f: F) { - use rustc_data_structures::sync::{par_iter, ParallelIterator}; - #[cfg(parallel_compiler)] - use rustc_rayon::iter::IndexedParallelIterator; - - par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| { - let owner = LocalDefId::new(owner); - if let MaybeOwner::Owner(owner_info) = owner_info { - par_iter(owner_info.nodes.bodies.range(..)).for_each(|(local_id, _)| { - let hir_id = HirId { owner, local_id: *local_id }; - let body_id = BodyId { hir_id }; - f(self.body_owner_def_id(body_id)) - }) - } - }); + par_for_each_in(&self.tcx.hir_crate_items(()).body_owners[..], |&def_id| f(def_id)); } pub fn ty_param_owner(self, def_id: LocalDefId) -> LocalDefId { @@ -1021,7 +998,9 @@ impl<'hir> Map<'hir> { _ => named_span(item.span, item.ident, None), }, Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)), - Node::Expr(Expr { kind: ExprKind::Closure { fn_decl_span, .. }, .. }) => *fn_decl_span, + Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), .. }) => { + *fn_decl_span + } _ => self.span_with_body(hir_id), }; Some(span) @@ -1149,7 +1128,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { hir_body_hash.hash_stable(&mut hcx, &mut stable_hasher); upstream_crates.hash_stable(&mut hcx, &mut stable_hasher); source_file_names.hash_stable(&mut hcx, &mut stable_hasher); - if tcx.sess.opts.debugging_opts.incremental_relative_spans { + if tcx.sess.opts.unstable_opts.incremental_relative_spans { let definitions = tcx.definitions_untracked(); let mut owner_spans: Vec<_> = krate .owners @@ -1281,85 +1260,48 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { } pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalDefId) -> ModuleItems { - let mut collector = ModuleCollector { - tcx, - submodules: Vec::default(), - items: Vec::default(), - trait_items: Vec::default(), - impl_items: Vec::default(), - foreign_items: Vec::default(), - }; + let mut collector = ItemCollector::new(tcx, false); let (hir_mod, span, hir_id) = tcx.hir().get_module(module_id); collector.visit_mod(hir_mod, span, hir_id); - let ModuleCollector { submodules, items, trait_items, impl_items, foreign_items, .. } = - collector; + let ItemCollector { + submodules, + items, + trait_items, + impl_items, + foreign_items, + body_owners, + .. + } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), + body_owners: body_owners.into_boxed_slice(), }; - - struct ModuleCollector<'tcx> { - tcx: TyCtxt<'tcx>, - submodules: Vec<LocalDefId>, - items: Vec<ItemId>, - trait_items: Vec<TraitItemId>, - impl_items: Vec<ImplItemId>, - foreign_items: Vec<ForeignItemId>, - } - - impl<'hir> Visitor<'hir> for ModuleCollector<'hir> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_item(&mut self, item: &'hir Item<'hir>) { - self.items.push(item.item_id()); - if let ItemKind::Mod(..) = item.kind { - // If this declares another module, do not recurse inside it. - self.submodules.push(item.def_id); - } else { - intravisit::walk_item(self, item) - } - } - - fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { - self.trait_items.push(item.trait_item_id()); - intravisit::walk_trait_item(self, item) - } - - fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { - self.impl_items.push(item.impl_item_id()); - intravisit::walk_impl_item(self, item) - } - - fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { - self.foreign_items.push(item.foreign_item_id()); - intravisit::walk_foreign_item(self, item) - } - } } pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { - let mut collector = CrateCollector { - tcx, - submodules: Vec::default(), - items: Vec::default(), - trait_items: Vec::default(), - impl_items: Vec::default(), - foreign_items: Vec::default(), - }; + let mut collector = ItemCollector::new(tcx, true); + // A "crate collector" and "module collector" start at a + // module item (the former starts at the crate root) but only + // the former needs to collect it. ItemCollector does not do this for us. + collector.submodules.push(CRATE_DEF_ID); tcx.hir().walk_toplevel_module(&mut collector); - let CrateCollector { submodules, items, trait_items, impl_items, foreign_items, .. } = - collector; + let ItemCollector { + submodules, + items, + trait_items, + impl_items, + foreign_items, + body_owners, + .. + } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), @@ -1367,47 +1309,96 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), + body_owners: body_owners.into_boxed_slice(), }; +} - struct CrateCollector<'tcx> { - tcx: TyCtxt<'tcx>, - submodules: Vec<LocalDefId>, - items: Vec<ItemId>, - trait_items: Vec<TraitItemId>, - impl_items: Vec<ImplItemId>, - foreign_items: Vec<ForeignItemId>, +struct ItemCollector<'tcx> { + // When true, it collects all items in the create, + // otherwise it collects items in some module. + crate_collector: bool, + tcx: TyCtxt<'tcx>, + submodules: Vec<LocalDefId>, + items: Vec<ItemId>, + trait_items: Vec<TraitItemId>, + impl_items: Vec<ImplItemId>, + foreign_items: Vec<ForeignItemId>, + body_owners: Vec<LocalDefId>, +} + +impl<'tcx> ItemCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>, crate_collector: bool) -> ItemCollector<'tcx> { + ItemCollector { + crate_collector, + tcx, + submodules: Vec::default(), + items: Vec::default(), + trait_items: Vec::default(), + impl_items: Vec::default(), + foreign_items: Vec::default(), + body_owners: Vec::default(), + } } +} + +impl<'hir> Visitor<'hir> for ItemCollector<'hir> { + type NestedFilter = nested_filter::All; - impl<'hir> Visitor<'hir> for CrateCollector<'hir> { - type NestedFilter = nested_filter::All; + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() + fn visit_item(&mut self, item: &'hir Item<'hir>) { + if associated_body(Node::Item(item)).is_some() { + self.body_owners.push(item.def_id); } - fn visit_item(&mut self, item: &'hir Item<'hir>) { - self.items.push(item.item_id()); + self.items.push(item.item_id()); + + // Items that are modules are handled here instead of in visit_mod. + if let ItemKind::Mod(module) = &item.kind { + self.submodules.push(item.def_id); + // A module collector does not recurse inside nested modules. + if self.crate_collector { + intravisit::walk_mod(self, module, item.hir_id()); + } + } else { intravisit::walk_item(self, item) } + } - fn visit_mod(&mut self, m: &'hir Mod<'hir>, _s: Span, n: HirId) { - self.submodules.push(n.owner); - intravisit::walk_mod(self, m, n); - } + fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { + self.foreign_items.push(item.foreign_item_id()); + intravisit::walk_foreign_item(self, item) + } + + fn visit_anon_const(&mut self, c: &'hir AnonConst) { + self.body_owners.push(self.tcx.hir().local_def_id(c.hir_id)); + intravisit::walk_anon_const(self, c) + } - fn visit_foreign_item(&mut self, item: &'hir ForeignItem<'hir>) { - self.foreign_items.push(item.foreign_item_id()); - intravisit::walk_foreign_item(self, item) + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { + if matches!(ex.kind, ExprKind::Closure { .. }) { + self.body_owners.push(self.tcx.hir().local_def_id(ex.hir_id)); } + intravisit::walk_expr(self, ex) + } - fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { - self.trait_items.push(item.trait_item_id()); - intravisit::walk_trait_item(self, item) + fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { + if associated_body(Node::TraitItem(item)).is_some() { + self.body_owners.push(item.def_id); } - fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { - self.impl_items.push(item.impl_item_id()); - intravisit::walk_impl_item(self, item) + self.trait_items.push(item.trait_item_id()); + intravisit::walk_trait_item(self, item) + } + + fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { + if associated_body(Node::ImplItem(item)).is_some() { + self.body_owners.push(item.def_id); } + + self.impl_items.push(item.impl_item_id()); + intravisit::walk_impl_item(self, item) } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 070a063c881..a605e234be9 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -44,6 +44,7 @@ pub struct ModuleItems { trait_items: Box<[TraitItemId]>, impl_items: Box<[ImplItemId]>, foreign_items: Box<[ForeignItemId]>, + body_owners: Box<[LocalDefId]>, } impl ModuleItems { diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 55e00c4c0d8..38868c21049 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -2,9 +2,8 @@ pub mod canonical; pub mod unify_key; use crate::ty::Region; -use crate::ty::Ty; +use crate::ty::{OpaqueTypeKey, Ty}; use rustc_data_structures::sync::Lrc; -use rustc_hir::def_id::DefId; use rustc_span::Span; /// Requires that `region` must be equal to one of the regions in `choice_regions`. @@ -15,8 +14,9 @@ use rustc_span::Span; /// ``` #[derive(Debug, Clone, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct MemberConstraint<'tcx> { - /// The `DefId` of the opaque type causing this constraint: used for error reporting. - pub opaque_type_def_id: DefId, + /// The `DefId` and substs of the opaque type causing this constraint. + /// Used for error reporting. + pub key: OpaqueTypeKey<'tcx>, /// The span where the hidden type was instantiated. pub definition_span: Span, diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index ef06c457bf4..45c6468bc24 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -40,7 +40,7 @@ #![feature(extern_types)] #![feature(new_uninit)] #![feature(once_cell)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(trusted_len)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 4b156de410d..2f45222de47 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -300,7 +300,7 @@ pub fn struct_lint_level<'s, 'd>( let has_future_breakage = future_incompatible.map_or( // Default allow lints trigger too often for testing. - sess.opts.debugging_opts.future_incompat_test && lint.default_level != Level::Allow, + sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow, |incompat| { matches!(incompat.reason, FutureIncompatibilityReason::FutureReleaseErrorReportNow) }, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 321fcd43797..45d33a1659f 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -50,7 +50,7 @@ bitflags! { /// the hot path. const COLD = 1 << 0; /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this - /// function is never null. + /// function is never null and the function has no side effects other than allocating. const ALLOCATOR = 1 << 1; /// An indicator that function will never unwind. Will become obsolete /// once C-unwind is fully stabilized. @@ -91,6 +91,12 @@ bitflags! { const NO_COVERAGE = 1 << 15; /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function. const USED_LINKER = 1 << 16; + /// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory. + const DEALLOCATOR = 1 << 17; + /// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory. + const REALLOCATOR = 1 << 18; + /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory. + const ALLOCATOR_ZEROED = 1 << 19; } } diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 20dcb670cd6..acced0492ef 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -25,7 +25,7 @@ pub fn provide(providers: &mut ty::query::Providers) { tcx.hir().krate_attrs(), tcx.sess, sym::move_size_limit, - tcx.sess.opts.debugging_opts.move_size_limit.unwrap_or(0), + tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0), ), type_length_limit: get_limit( tcx.hir().krate_attrs(), diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index fc35cafcc77..8dc68b1f5a8 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -3,14 +3,14 @@ pub mod dependency_format; pub mod exported_symbols; pub mod lang_items; pub mod lib_features { - use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use rustc_span::symbol::Symbol; + use rustc_data_structures::fx::FxHashMap; + use rustc_span::{symbol::Symbol, Span}; #[derive(HashStable, Debug)] pub struct LibFeatures { - // A map from feature to stabilisation version. - pub stable: FxHashMap<Symbol, Symbol>, - pub unstable: FxHashSet<Symbol>, + /// A map from feature to stabilisation version. + pub stable: FxHashMap<Symbol, (Symbol, Span)>, + pub unstable: FxHashMap<Symbol, Span>, } impl LibFeatures { @@ -18,8 +18,8 @@ pub mod lib_features { let mut all_features: Vec<_> = self .stable .iter() - .map(|(f, s)| (*f, Some(*s))) - .chain(self.unstable.iter().map(|f| (*f, None))) + .map(|(f, (s, _))| (*f, Some(*s))) + .chain(self.unstable.iter().map(|(f, _)| (*f, None))) .collect(); all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap()); all_features diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index c71ba7b1753..9b2f4456705 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -12,7 +12,6 @@ pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId), LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId), - LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 802b7852bac..414912dd0f7 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -62,6 +62,19 @@ pub struct Index { pub stab_map: FxHashMap<LocalDefId, Stability>, pub const_stab_map: FxHashMap<LocalDefId, ConstStability>, pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>, + /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` + /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute + /// exists, then this map will have a `impliee -> implier` entry. + /// + /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should + /// specify their implications (both `implies` and `implied_by`). If only one of the two + /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this + /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is + /// reported, only the `#[stable]` attribute information is available, so the map is necessary + /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` + /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of + /// unstable feature" error for a feature that was implied. + pub implications: FxHashMap<Symbol, Symbol>, } impl Index { @@ -423,7 +436,9 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::Unstable { reason, issue, is_soft }, feature, .. + level: attr::Unstable { reason, issue, is_soft, implied_by }, + feature, + .. }) => { if span.allows_unstable(feature) { debug!("stability: skipping span={:?} since it is internal", span); @@ -433,6 +448,13 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } + // If this item was previously part of a now-stabilized feature which is still + // active (i.e. the user hasn't removed the attribute for the stabilized feature + // yet) then allow use of this item. + if let Some(implied_by) = implied_by && self.features().active(implied_by) { + return EvalResult::Allow; + } + // When we're compiling the compiler itself we may pull in // crates from crates.io, but those crates may depend on other // crates also pulled in from crates.io. We want to ideally be @@ -443,7 +465,7 @@ impl<'tcx> TyCtxt<'tcx> { // compiling a compiler crate), then let this missing feature // annotation slide. if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { - if self.sess.opts.debugging_opts.force_unstable_if_unmarked { + if self.sess.opts.unstable_opts.force_unstable_if_unmarked { return EvalResult::Allow; } } @@ -453,7 +475,13 @@ impl<'tcx> TyCtxt<'tcx> { } let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); - EvalResult::Deny { feature, reason, issue, suggestion, is_soft } + EvalResult::Deny { + feature, + reason: reason.to_opt_reason(), + issue, + suggestion, + is_soft, + } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are @@ -471,13 +499,15 @@ impl<'tcx> TyCtxt<'tcx> { /// /// This function will also check if the item is deprecated. /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_stability( self, def_id: DefId, id: Option<HirId>, span: Span, method_span: Option<Span>, - ) { + ) -> bool { self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No) } @@ -490,6 +520,8 @@ impl<'tcx> TyCtxt<'tcx> { /// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted. /// /// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_stability_allow_unstable( self, def_id: DefId, @@ -497,7 +529,7 @@ impl<'tcx> TyCtxt<'tcx> { span: Span, method_span: Option<Span>, allow_unstable: AllowUnstable, - ) { + ) -> bool { self.check_optional_stability( def_id, id, @@ -516,6 +548,8 @@ impl<'tcx> TyCtxt<'tcx> { /// missing stability attributes (not necessarily just emit a `bug!`). This is necessary /// for default generic parameters, which only have stability attributes if they were /// added after the type on which they're defined. + /// + /// Returns `true` if item is allowed aka, stable or unstable under an enabled feature. pub fn check_optional_stability( self, def_id: DefId, @@ -524,13 +558,16 @@ impl<'tcx> TyCtxt<'tcx> { method_span: Option<Span>, allow_unstable: AllowUnstable, unmarked: impl FnOnce(Span, DefId), - ) { + ) -> bool { let soft_handler = |lint, span, msg: &_| { self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { lint.build(msg).emit(); }) }; - match self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable) { + let eval_result = + self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable); + let is_allowed = matches!(eval_result, EvalResult::Allow); + match eval_result { EvalResult::Allow => {} EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( self.sess, @@ -544,6 +581,8 @@ impl<'tcx> TyCtxt<'tcx> { ), EvalResult::Unmarked => unmarked(span, def_id), } + + is_allowed } pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> { diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs index a4d78911b27..f3621cd99d3 100644 --- a/compiler/rustc_middle/src/mir/generic_graph.rs +++ b/compiler/rustc_middle/src/mir/generic_graph.rs @@ -8,7 +8,7 @@ pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Grap let def_id = body.source.def_id(); let def_name = graphviz_safe_def_name(def_id); let graph_name = format!("Mir_{}", def_name); - let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode; // Nodes let nodes: Vec<Node> = body diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs index c907680bda1..11ac45943ac 100644 --- a/compiler/rustc_middle/src/mir/generic_graphviz.rs +++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs @@ -56,11 +56,11 @@ impl< writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?; // Global graph properties - let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font); let mut graph_attrs = vec![&font[..]]; let mut content_attrs = vec![&font[..]]; - let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode; if dark_mode { graph_attrs.push(r#"bgcolor="black""#); graph_attrs.push(r#"fontcolor="white""#); diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 92c7a358c0a..5de56dad07a 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -57,11 +57,11 @@ where W: Write, { // Global graph properties - let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font); let mut graph_attrs = vec![&font[..]]; let mut content_attrs = vec![&font[..]]; - let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode; if dark_mode { graph_attrs.push(r#"bgcolor="black""#); graph_attrs.push(r#"fontcolor="white""#); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index ae333846f06..db7e0fb8a3b 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -30,7 +30,7 @@ use crate::ty; // hashed. (see the `Hash` impl below for more details), so the impl is not derived. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(HashStable)] -pub struct Allocation<Tag = AllocId, Extra = ()> { +pub struct Allocation<Prov = AllocId, Extra = ()> { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. bytes: Box<[u8]>, @@ -38,7 +38,7 @@ pub struct Allocation<Tag = AllocId, Extra = ()> { /// Only the first byte of a pointer is inserted into the map; i.e., /// every entry in this map applies to `pointer_size` consecutive bytes starting /// at the given offset. - relocations: Relocations<Tag>, + relocations: Relocations<Prov>, /// Denotes which part of this allocation is initialized. init_mask: InitMask, /// The alignment of the allocation to detect unaligned reads. @@ -102,8 +102,8 @@ impl hash::Hash for Allocation { /// (`ConstAllocation`) are used quite a bit. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] #[rustc_pass_by_value] -pub struct ConstAllocation<'tcx, Tag = AllocId, Extra = ()>( - pub Interned<'tcx, Allocation<Tag, Extra>>, +pub struct ConstAllocation<'tcx, Prov = AllocId, Extra = ()>( + pub Interned<'tcx, Allocation<Prov, Extra>>, ); impl<'tcx> fmt::Debug for ConstAllocation<'tcx> { @@ -114,8 +114,8 @@ impl<'tcx> fmt::Debug for ConstAllocation<'tcx> { } } -impl<'tcx, Tag, Extra> ConstAllocation<'tcx, Tag, Extra> { - pub fn inner(self) -> &'tcx Allocation<Tag, Extra> { +impl<'tcx, Prov, Extra> ConstAllocation<'tcx, Prov, Extra> { + pub fn inner(self) -> &'tcx Allocation<Prov, Extra> { self.0.0 } } @@ -200,7 +200,7 @@ impl AllocRange { } // The constructors are all without extra; the extra gets added by a machine hook later. -impl<Tag> Allocation<Tag> { +impl<Prov> Allocation<Prov> { /// Creates an allocation initialized by the given bytes pub fn from_bytes<'a>( slice: impl Into<Cow<'a, [u8]>>, @@ -225,6 +225,8 @@ impl<Tag> Allocation<Tag> { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. + /// + /// If `panic_on_fail` is true, this will never return `Err`. pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> { let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| { // This results in an error that can happen non-deterministically, since the memory @@ -254,14 +256,15 @@ impl<Tag> Allocation<Tag> { } impl Allocation { - /// Convert Tag and add Extra fields - pub fn convert_tag_add_extra<Tag, Extra, Err>( + /// Adjust allocation from the ones in tcx to a custom Machine instance + /// with a different Provenance and Extra type. + pub fn adjust_from_tcx<Prov, Extra, Err>( self, cx: &impl HasDataLayout, extra: Extra, - mut tagger: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Tag>, Err>, - ) -> Result<Allocation<Tag, Extra>, Err> { - // Compute new pointer tags, which also adjusts the bytes. + mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>, + ) -> Result<Allocation<Prov, Extra>, Err> { + // Compute new pointer provenance, which also adjusts the bytes. let mut bytes = self.bytes; let mut new_relocations = Vec::with_capacity(self.relocations.0.len()); let ptr_size = cx.data_layout().pointer_size.bytes_usize(); @@ -270,10 +273,10 @@ impl Allocation { let idx = offset.bytes_usize(); let ptr_bytes = &mut bytes[idx..idx + ptr_size]; let bits = read_target_uint(endian, ptr_bytes).unwrap(); - let (ptr_tag, ptr_offset) = - tagger(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts(); + let (ptr_prov, ptr_offset) = + adjust_ptr(Pointer::new(alloc_id, Size::from_bytes(bits)))?.into_parts(); write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap(); - new_relocations.push((offset, ptr_tag)); + new_relocations.push((offset, ptr_prov)); } // Create allocation. Ok(Allocation { @@ -288,7 +291,7 @@ impl Allocation { } /// Raw accessors. Provide access to otherwise private bytes. -impl<Tag, Extra> Allocation<Tag, Extra> { +impl<Prov, Extra> Allocation<Prov, Extra> { pub fn len(&self) -> usize { self.bytes.len() } @@ -311,13 +314,13 @@ impl<Tag, Extra> Allocation<Tag, Extra> { } /// Returns the relocation list. - pub fn relocations(&self) -> &Relocations<Tag> { + pub fn relocations(&self) -> &Relocations<Prov> { &self.relocations } } /// Byte accessors. -impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { +impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about relocations. It just deduplicates some code between `read_scalar` /// and `get_bytes_internal`. @@ -411,7 +414,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { } /// Reading and writing. -impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { +impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a /// relocation. If `allow_uninit`/`allow_ptr` is `false`, also enforces that the memory in the /// given range contains no uninitialized bytes/relocations. @@ -449,7 +452,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { cx: &impl HasDataLayout, range: AllocRange, read_provenance: bool, - ) -> AllocResult<ScalarMaybeUninit<Tag>> { + ) -> AllocResult<ScalarMaybeUninit<Prov>> { if read_provenance { assert_eq!(range.size, cx.data_layout().pointer_size); } @@ -473,7 +476,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { // If we are *not* reading a pointer, and we can just ignore relocations, // then do exactly that. - if !read_provenance && Tag::OFFSET_IS_ADDR { + if !read_provenance && Prov::OFFSET_IS_ADDR { // We just strip provenance. let bytes = self.get_bytes_even_more_internal(range); let bits = read_target_uint(cx.data_layout().endian, bytes).unwrap(); @@ -504,7 +507,7 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { &mut self, cx: &impl HasDataLayout, range: AllocRange, - val: ScalarMaybeUninit<Tag>, + val: ScalarMaybeUninit<Prov>, ) -> AllocResult { assert!(self.mutability == Mutability::Mut); @@ -546,9 +549,9 @@ impl<Tag: Provenance, Extra> Allocation<Tag, Extra> { } /// Relocations. -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { /// Returns all relocations overlapping with the given pointer-offset pair. - fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Tag)] { + fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1); @@ -578,7 +581,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// immediately in that case. fn clear_relocations(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult where - Tag: Provenance, + Prov: Provenance, { // Find the start and end of the given range and its outermost relocations. let (first, last) = { @@ -600,7 +603,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { // FIXME: Miri should preserve partial relocations; see // https://github.com/rust-lang/miri/issues/2181. if first < start { - if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE { + if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE { return Err(AllocError::PartialPointerOverwrite(first)); } warn!( @@ -609,7 +612,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { self.init_mask.set_range(first, start, false); } if last > end { - if Tag::ERR_ON_PARTIAL_PTR_OVERWRITE { + if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE { return Err(AllocError::PartialPointerOverwrite( last - cx.data_layout().pointer_size, )); @@ -640,22 +643,22 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// "Relocations" stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -pub struct Relocations<Tag = AllocId>(SortedMap<Size, Tag>); +pub struct Relocations<Prov = AllocId>(SortedMap<Size, Prov>); -impl<Tag> Relocations<Tag> { +impl<Prov> Relocations<Prov> { pub fn new() -> Self { Relocations(SortedMap::new()) } // The caller must guarantee that the given relocations are already sorted // by address and contain no duplicates. - pub fn from_presorted(r: Vec<(Size, Tag)>) -> Self { + pub fn from_presorted(r: Vec<(Size, Prov)>) -> Self { Relocations(SortedMap::from_presorted_elements(r)) } } -impl<Tag> Deref for Relocations<Tag> { - type Target = SortedMap<Size, Tag>; +impl<Prov> Deref for Relocations<Prov> { + type Target = SortedMap<Size, Prov>; fn deref(&self) -> &Self::Target { &self.0 @@ -665,18 +668,18 @@ impl<Tag> Deref for Relocations<Tag> { /// A partial, owned list of relocations to transfer into another allocation. /// /// Offsets are already adjusted to the destination allocation. -pub struct AllocationRelocations<Tag> { - dest_relocations: Vec<(Size, Tag)>, +pub struct AllocationRelocations<Prov> { + dest_relocations: Vec<(Size, Prov)>, } -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { pub fn prepare_relocation_copy( &self, cx: &impl HasDataLayout, src: AllocRange, dest: Size, count: u64, - ) -> AllocationRelocations<Tag> { + ) -> AllocationRelocations<Prov> { let relocations = self.get_relocations(cx, src); if relocations.is_empty() { return AllocationRelocations { dest_relocations: Vec::new() }; @@ -686,7 +689,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { let mut new_relocations = Vec::with_capacity(relocations.len() * (count as usize)); // If `count` is large, this is rather wasteful -- we are allocating a big array here, which - // is mostly filled with redundant information since it's just N copies of the same `Tag`s + // is mostly filled with redundant information since it's just N copies of the same `Prov`s // at slightly adjusted offsets. The reason we do this is so that in `mark_relocation_range` // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces // the right sequence of relocations for all N copies. @@ -711,7 +714,7 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. - pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Tag>) { + pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations<Prov>) { self.relocations.0.insert_presorted(relocations.dest_relocations); } } @@ -1176,7 +1179,7 @@ impl<'a> Iterator for InitChunkIter<'a> { } /// Uninitialized bytes. -impl<Tag: Copy, Extra> Allocation<Tag, Extra> { +impl<Prov: Copy, Extra> Allocation<Prov, Extra> { /// Checks whether the given range is entirely initialized. /// /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte @@ -1224,7 +1227,7 @@ impl InitMaskCompressed { } /// Transferring the initialization mask to other allocations. -impl<Tag, Extra> Allocation<Tag, Extra> { +impl<Prov, Extra> Allocation<Prov, Extra> { /// Creates a run-length encoding of the initialization mask; panics if range is empty. /// /// This is essentially a more space-efficient version of diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 795f23edb31..cecb55578d3 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,7 +1,7 @@ use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; -use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; +use crate::ty::{layout, query::TyCtxtAt, tls, Ty, ValTree}; use rustc_data_structures::sync::Lock; use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; @@ -219,7 +219,7 @@ pub struct ScalarSizeMismatch { } /// Error information for when the program caused Undefined Behavior. -pub enum UndefinedBehaviorInfo<'tcx> { +pub enum UndefinedBehaviorInfo { /// Free-form case. Only for errors that are never caught! Ub(String), /// Unreachable code was executed. @@ -241,12 +241,6 @@ pub enum UndefinedBehaviorInfo<'tcx> { PointerArithOverflow, /// Invalid metadata in a wide pointer (using `str` to avoid allocations). InvalidMeta(&'static str), - /// Invalid drop function in vtable. - InvalidVtableDropFn(FnSig<'tcx>), - /// Invalid size in a vtable: too large. - InvalidVtableSize, - /// Invalid alignment in a vtable: too large, or not a power of 2. - InvalidVtableAlignment(String), /// Reading a C string that does not end within its allocation. UnterminatedCString(Pointer), /// Dereferencing a dangling pointer after it got freed. @@ -271,6 +265,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { WriteToReadOnly(AllocId), // Trying to access the data behind a function pointer. DerefFunctionPointer(AllocId), + // Trying to access the data behind a vtable pointer. + DerefVTablePointer(AllocId), /// The value validity check found a problem. /// Should only be thrown by `validity.rs` and always point out which part of the value /// is the problem. @@ -288,6 +284,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidTag(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), + /// Using a pointer-not-to-a-vtable as vtable pointer. + InvalidVTablePointer(Pointer), /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), /// Using uninitialized data where it is not allowed. @@ -300,7 +298,7 @@ pub enum UndefinedBehaviorInfo<'tcx> { UninhabitedEnumVariantWritten, } -impl fmt::Display for UndefinedBehaviorInfo<'_> { +impl fmt::Display for UndefinedBehaviorInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use UndefinedBehaviorInfo::*; match self { @@ -315,14 +313,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"), - InvalidVtableDropFn(sig) => write!( - f, - "invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type", - ), - InvalidVtableSize => { - write!(f, "invalid vtable: size is bigger than largest supported object") - } - InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"), UnterminatedCString(p) => write!( f, "reading a null-terminated string starting at {p:?} with no null found before end of allocation", @@ -359,6 +349,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { ), WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"), DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"), + DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"), ValidationFailure { path: None, msg } => { write!(f, "constructing invalid value: {msg}") } @@ -375,6 +366,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { InvalidFunctionPointer(p) => { write!(f, "using {p:?} as function pointer but it does not point to a function") } + InvalidVTablePointer(p) => { + write!(f, "using {p:?} as vtable pointer but it does not point to a vtable") + } InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"), InvalidUninitBytes(Some((alloc, info))) => write!( f, @@ -494,7 +488,7 @@ impl dyn MachineStopType { pub enum InterpError<'tcx> { /// The program caused undefined behavior. - UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + UndefinedBehavior(UndefinedBehaviorInfo), /// The program did something the interpreter does not support (some of these *might* be UB /// but the interpreter is not sure). Unsupported(UnsupportedOpInfo), diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 698024b2330..967f8ece16c 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -196,6 +196,7 @@ impl fmt::Debug for AllocId { enum AllocDiscriminant { Alloc, Fn, + VTable, Static, } @@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>( AllocDiscriminant::Fn.encode(encoder); fn_instance.encode(encoder); } + GlobalAlloc::VTable(ty, poly_trait_ref) => { + trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id); + AllocDiscriminant::VTable.encode(encoder); + ty.encode(encoder); + poly_trait_ref.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -305,7 +312,9 @@ impl<'s> AllocDecodingSession<'s> { State::InProgress(TinyList::new_single(self.session_id), alloc_id); Some(alloc_id) } - AllocDiscriminant::Fn | AllocDiscriminant::Static => { + AllocDiscriminant::Fn + | AllocDiscriminant::Static + | AllocDiscriminant::VTable => { // Fns and statics cannot be cyclic, and their `AllocId` // is determined later by interning. *entry = @@ -355,6 +364,16 @@ impl<'s> AllocDecodingSession<'s> { let alloc_id = decoder.interner().create_fn_alloc(instance); alloc_id } + AllocDiscriminant::VTable => { + assert!(alloc_id.is_none()); + trace!("creating vtable alloc ID"); + let ty = <Ty<'_> as Decodable<D>>::decode(decoder); + let poly_trait_ref = + <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder); + trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); + let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref); + alloc_id + } AllocDiscriminant::Static => { assert!(alloc_id.is_none()); trace!("creating extern static alloc ID"); @@ -380,6 +399,8 @@ impl<'s> AllocDecodingSession<'s> { pub enum GlobalAlloc<'tcx> { /// The alloc ID is used as a function pointer. Function(Instance<'tcx>), + /// This alloc ID points to a symbolic (not-reified) vtable. + VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), /// The alloc ID points to a "lazy" static variable that did not get computed (yet). /// This is also used to break the cycle in recursive statics. Static(DefId), @@ -407,6 +428,16 @@ impl<'tcx> GlobalAlloc<'tcx> { _ => bug!("expected function, got {:?}", self), } } + + /// Panics if the `GlobalAlloc` is not `GlobalAlloc::VTable` + #[track_caller] + #[inline] + pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) { + match *self { + GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref), + _ => bug!("expected vtable, got {:?}", self), + } + } } pub(crate) struct AllocMap<'tcx> { @@ -454,12 +485,12 @@ impl<'tcx> TyCtxt<'tcx> { } /// Reserves a new ID *if* this allocation has not been dedup-reserved before. - /// Should only be used for function pointers and statics, we don't want - /// to dedup IDs for "real" memory! + /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we + /// don't want to dedup IDs for "real" memory! fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { let mut alloc_map = self.alloc_map.lock(); match alloc { - GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} + GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {} GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), } if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { @@ -504,6 +535,15 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated. + pub fn create_vtable_alloc( + self, + ty: Ty<'tcx>, + poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + ) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref)) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., @@ -521,7 +561,7 @@ impl<'tcx> TyCtxt<'tcx> { /// This function exists to allow const eval to detect the difference between evaluation- /// local dangling pointers and allocations in constants/statics. #[inline] - pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { + pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { self.alloc_map.lock().alloc_map.get(&id).cloned() } @@ -532,7 +572,7 @@ impl<'tcx> TyCtxt<'tcx> { /// ids), this function is frequently used throughout rustc, but should not be used within /// the miri engine. pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { - match self.get_global_alloc(id) { + match self.try_get_global_alloc(id) { Some(alloc) => alloc, None => bug!("could not find allocation for {id:?}"), } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index d4cdf45d186..384954cbbd5 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -159,34 +159,34 @@ impl Provenance for AllocId { /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] -pub struct Pointer<Tag = AllocId> { - pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Tag` type) - pub provenance: Tag, +pub struct Pointer<Prov = AllocId> { + pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type) + pub provenance: Prov, } static_assert_size!(Pointer, 16); -// `Option<Tag>` pointers are also passed around quite a bit +// `Option<Prov>` pointers are also passed around quite a bit // (but not stored in permanent machine state). static_assert_size!(Pointer<Option<AllocId>>, 16); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for Pointer<Tag> { +impl<Prov: Provenance> fmt::Debug for Pointer<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Provenance::fmt(self, f) } } -impl<Tag: Provenance> fmt::Debug for Pointer<Option<Tag>> { +impl<Prov: Provenance> fmt::Debug for Pointer<Option<Prov>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.provenance { - Some(tag) => Provenance::fmt(&Pointer::new(tag, self.offset), f), + Some(prov) => Provenance::fmt(&Pointer::new(prov, self.offset), f), None => write!(f, "{:#x}[noalloc]", self.offset.bytes()), } } } -impl<Tag: Provenance> fmt::Display for Pointer<Option<Tag>> { +impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.provenance.is_none() && self.offset.bytes() == 0 { write!(f, "null pointer") @@ -204,38 +204,38 @@ impl From<AllocId> for Pointer { } } -impl<Tag> From<Pointer<Tag>> for Pointer<Option<Tag>> { +impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> { #[inline(always)] - fn from(ptr: Pointer<Tag>) -> Self { - let (tag, offset) = ptr.into_parts(); - Pointer::new(Some(tag), offset) + fn from(ptr: Pointer<Prov>) -> Self { + let (prov, offset) = ptr.into_parts(); + Pointer::new(Some(prov), offset) } } -impl<Tag> Pointer<Option<Tag>> { - /// Convert this pointer that *might* have a tag into a pointer that *definitely* has a tag, or - /// an absolute address. +impl<Prov> Pointer<Option<Prov>> { + /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a + /// provenance, or an absolute address. /// /// This is rarely what you want; call `ptr_try_get_alloc_id` instead. - pub fn into_pointer_or_addr(self) -> Result<Pointer<Tag>, Size> { + pub fn into_pointer_or_addr(self) -> Result<Pointer<Prov>, Size> { match self.provenance { - Some(tag) => Ok(Pointer::new(tag, self.offset)), + Some(prov) => Ok(Pointer::new(prov, self.offset)), None => Err(self.offset), } } /// Returns the absolute address the pointer points to. - /// Only works if Tag::OFFSET_IS_ADDR is true! + /// Only works if Prov::OFFSET_IS_ADDR is true! pub fn addr(self) -> Size where - Tag: Provenance, + Prov: Provenance, { - assert!(Tag::OFFSET_IS_ADDR); + assert!(Prov::OFFSET_IS_ADDR); self.offset } } -impl<Tag> Pointer<Option<Tag>> { +impl<Prov> Pointer<Option<Prov>> { #[inline(always)] pub fn from_addr(addr: u64) -> Self { Pointer { provenance: None, offset: Size::from_bytes(addr) } @@ -247,21 +247,21 @@ impl<Tag> Pointer<Option<Tag>> { } } -impl<'tcx, Tag> Pointer<Tag> { +impl<'tcx, Prov> Pointer<Prov> { #[inline(always)] - pub fn new(provenance: Tag, offset: Size) -> Self { + pub fn new(provenance: Prov, offset: Size) -> Self { Pointer { provenance, offset } } - /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Tag`! + /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`! /// This function must only be used in the implementation of `Machine::ptr_get_alloc`, /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`. #[inline(always)] - pub fn into_parts(self) -> (Tag, Size) { + pub fn into_parts(self) -> (Prov, Size) { (self.provenance, self.offset) } - pub fn map_provenance(self, f: impl FnOnce(Tag) -> Tag) -> Self { + pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { Pointer { provenance: f(self.provenance), ..self } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 22bbe29c105..834c114ee1c 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -126,7 +126,7 @@ impl<'tcx> ConstValue<'tcx> { /// Do *not* match on a `Scalar`! Use the various `to_*` methods instead. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] -pub enum Scalar<Tag = AllocId> { +pub enum Scalar<Prov = AllocId> { /// The raw bytes of a simple value. Int(ScalarInt), @@ -137,7 +137,7 @@ pub enum Scalar<Tag = AllocId> { /// We also store the size of the pointer, such that a `Scalar` always knows how big it is. /// The size is always the pointer size of the current target, but this is not information /// that we always have readily available. - Ptr(Pointer<Tag>, u8), + Ptr(Pointer<Prov>, u8), } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -145,7 +145,7 @@ static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for Scalar<Tag> { +impl<Prov: Provenance> fmt::Debug for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr), @@ -154,7 +154,7 @@ impl<Tag: Provenance> fmt::Debug for Scalar<Tag> { } } -impl<Tag: Provenance> fmt::Display for Scalar<Tag> { +impl<Prov: Provenance> fmt::Display for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), @@ -163,7 +163,7 @@ impl<Tag: Provenance> fmt::Display for Scalar<Tag> { } } -impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { +impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), @@ -172,37 +172,38 @@ impl<Tag: Provenance> fmt::LowerHex for Scalar<Tag> { } } -impl<Tag> From<Single> for Scalar<Tag> { +impl<Prov> From<Single> for Scalar<Prov> { #[inline(always)] fn from(f: Single) -> Self { Scalar::from_f32(f) } } -impl<Tag> From<Double> for Scalar<Tag> { +impl<Prov> From<Double> for Scalar<Prov> { #[inline(always)] fn from(f: Double) -> Self { Scalar::from_f64(f) } } -impl<Tag> From<ScalarInt> for Scalar<Tag> { +impl<Prov> From<ScalarInt> for Scalar<Prov> { #[inline(always)] fn from(ptr: ScalarInt) -> Self { Scalar::Int(ptr) } } -impl<Tag> Scalar<Tag> { +impl<Prov> Scalar<Prov> { #[inline(always)] - pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { + pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap()) } - /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer). - pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + /// Create a Scalar from a pointer with an `Option<_>` provenance (where `None` represents a + /// plain integer / "invalid" pointer). + pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { match ptr.into_parts() { - (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx), + (Some(prov), offset) => Scalar::from_pointer(Pointer::new(prov, offset), cx), (None, offset) => { Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) } @@ -310,7 +311,7 @@ impl<Tag> Scalar<Tag> { pub fn to_bits_or_ptr_internal( self, target_size: Size, - ) -> Result<Result<u128, Pointer<Tag>>, ScalarSizeMismatch> { + ) -> Result<Result<u128, Pointer<Prov>>, ScalarSizeMismatch> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); Ok(match self { Scalar::Int(int) => Ok(int.to_bits(target_size).map_err(|size| { @@ -329,7 +330,20 @@ impl<Tag> Scalar<Tag> { } } -impl<'tcx, Tag: Provenance> Scalar<Tag> { +impl<'tcx, Prov: Provenance> Scalar<Prov> { + pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> { + match self + .to_bits_or_ptr_internal(cx.pointer_size()) + .map_err(|s| err_ub!(ScalarSizeMismatch(s)))? + { + Err(ptr) => Ok(ptr.into()), + Ok(bits) => { + let addr = u64::try_from(bits).unwrap(); + Ok(Pointer::from_addr(addr)) + } + } + } + /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you /// likely want to use instead. /// @@ -341,13 +355,13 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> { match self { Scalar::Int(int) => Ok(int), Scalar::Ptr(ptr, sz) => { - if Tag::OFFSET_IS_ADDR { + if Prov::OFFSET_IS_ADDR { Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap()) } else { // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. - let (tag, offset) = ptr.into_parts(); + let (prov, offset) = ptr.into_parts(); // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. - Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz)) + Err(Scalar::Ptr(Pointer::new(prov.get_alloc_id().unwrap(), offset), sz)) } } } @@ -489,24 +503,24 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> { } #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] -pub enum ScalarMaybeUninit<Tag = AllocId> { - Scalar(Scalar<Tag>), +pub enum ScalarMaybeUninit<Prov = AllocId> { + Scalar(Scalar<Prov>), Uninit, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ScalarMaybeUninit, 24); -impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> { +impl<Prov> From<Scalar<Prov>> for ScalarMaybeUninit<Prov> { #[inline(always)] - fn from(s: Scalar<Tag>) -> Self { + fn from(s: Scalar<Prov>) -> Self { ScalarMaybeUninit::Scalar(s) } } // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> { +impl<Prov: Provenance> fmt::Debug for ScalarMaybeUninit<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, "<uninitialized>"), @@ -515,7 +529,7 @@ impl<Tag: Provenance> fmt::Debug for ScalarMaybeUninit<Tag> { } } -impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> { +impl<Prov: Provenance> fmt::LowerHex for ScalarMaybeUninit<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"), @@ -524,19 +538,19 @@ impl<Tag: Provenance> fmt::LowerHex for ScalarMaybeUninit<Tag> { } } -impl<Tag> ScalarMaybeUninit<Tag> { +impl<Prov> ScalarMaybeUninit<Prov> { #[inline] - pub fn from_pointer(ptr: Pointer<Tag>, cx: &impl HasDataLayout) -> Self { + pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx)) } #[inline] - pub fn from_maybe_pointer(ptr: Pointer<Option<Tag>>, cx: &impl HasDataLayout) -> Self { + pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx)) } #[inline] - pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Tag>> { + pub fn check_init<'tcx>(self) -> InterpResult<'tcx, Scalar<Prov>> { match self { ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), @@ -544,7 +558,12 @@ impl<Tag> ScalarMaybeUninit<Tag> { } } -impl<'tcx, Tag: Provenance> ScalarMaybeUninit<Tag> { +impl<'tcx, Prov: Provenance> ScalarMaybeUninit<Prov> { + #[inline(always)] + pub fn to_pointer(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, Pointer<Option<Prov>>> { + self.check_init()?.to_pointer(cx) + } + #[inline(always)] pub fn to_bool(self) -> InterpResult<'tcx, bool> { self.check_init()?.to_bool() diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 333a88ba520..f7311ebdabf 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1461,6 +1461,14 @@ impl<'tcx> Place<'tcx> { self.projection.iter().any(|elem| elem.is_indirect()) } + /// If MirPhase >= Derefered and if projection contains Deref, + /// It's guaranteed to be in the first place + pub fn has_deref(&self) -> bool { + // To make sure this is not accidently used in wrong mir phase + debug_assert!(!self.projection[1..].contains(&PlaceElem::Deref)); + self.projection.first() == Some(&PlaceElem::Deref) + } + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. #[inline(always)] @@ -1533,6 +1541,12 @@ impl<'tcx> PlaceRef<'tcx> { } } + /// If MirPhase >= Derefered and if projection contains Deref, + /// It's guaranteed to be in the first place + pub fn has_deref(&self) -> bool { + self.projection.first() == Some(&PlaceElem::Deref) + } + /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. #[inline] @@ -1662,6 +1676,22 @@ impl SourceScope { ClearCrossCrate::Clear => None, } } + + /// The instance this source scope was inlined from, if any. + #[inline] + pub fn inlined_instance<'tcx>( + self, + source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>, + ) -> Option<ty::Instance<'tcx>> { + let scope_data = &source_scopes[self]; + if let Some((inlined_instance, _)) = scope_data.inlined { + Some(inlined_instance) + } else if let Some(inlined_scope) = scope_data.inlined_parent_scope { + Some(source_scopes[inlined_scope].inlined.unwrap().0) + } else { + None + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] @@ -1884,7 +1914,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { // When printing regions, add trailing space if necessary. let print_region = ty::tls::with(|tcx| { - tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions + tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions }); let region = if print_region { let mut region = region.to_string(); @@ -1954,7 +1984,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { if let Some(def_id) = def_id.as_local() { - let name = if tcx.sess.opts.debugging_opts.span_free_formats { + let name = if tcx.sess.opts.unstable_opts.span_free_formats { let substs = tcx.lift(substs).unwrap(); format!( "[closure@{}]", diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 021f2782736..21ae121e1ce 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -90,7 +90,7 @@ impl<'tcx> MonoItem<'tcx> { let generate_cgu_internal_copies = tcx .sess .opts - .debugging_opts + .unstable_opts .inline_in_all_cgus .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) && !tcx.sess.link_dead_code(); @@ -362,7 +362,7 @@ impl<'tcx> CodegenUnit<'tcx> { // the codegen tests and can even make item order // unstable. InstanceDef::Item(def) => def.did.as_local().map(Idx::index), - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) | InstanceDef::FnPtrShim(..) @@ -459,7 +459,7 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> { { let cgu_name = self.build_cgu_name_no_mangle(cnum, components, special_suffix); - if self.tcx.sess.opts.debugging_opts.human_readable_cgu_names { + if self.tcx.sess.opts.unstable_opts.human_readable_cgu_names { cgu_name } else { Symbol::intern(&CodegenUnit::mangle_name(cgu_name.as_str())) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e2fa37ee7be..0ce41337b91 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -90,7 +90,7 @@ pub fn dump_mir<'tcx, F>( } pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { - let Some(ref filters) = tcx.sess.opts.debugging_opts.dump_mir else { + let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else { return false; }; // see notes on #41697 below @@ -141,7 +141,7 @@ fn dump_matched_mir_node<'tcx, F>( extra_data(PassWhere::AfterCFG, &mut file)?; }; - if tcx.sess.opts.debugging_opts.dump_mir_graphviz { + if tcx.sess.opts.unstable_opts.dump_mir_graphviz { let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body.source)?; @@ -149,7 +149,7 @@ fn dump_matched_mir_node<'tcx, F>( }; } - if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview { + if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview { let _: io::Result<()> = try { let file_basename = dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source); @@ -175,7 +175,7 @@ fn dump_file_basename<'tcx>( None => String::new(), }; - let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { + let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number { String::new() } else { match pass_num { @@ -214,7 +214,7 @@ fn dump_file_basename<'tcx>( /// 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.debugging_opts.dump_mir_dir)); + file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir)); let file_name = format!("{}.{}", basename, extension,); @@ -360,7 +360,7 @@ where "{:A$} // {}{}", indented_body, if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, statement.source_info), + comment(tcx, statement.source_info, body.span), A = ALIGN, )?; @@ -381,7 +381,7 @@ where "{:A$} // {}{}", indented_terminator, if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, - comment(tcx, data.terminator().source_info), + comment(tcx, data.terminator().source_info, body.span), A = ALIGN, )?; @@ -518,8 +518,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { } } -fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { - format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span)) +fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo, function_span: Span) -> String { + let location = if tcx.sess.opts.unstable_opts.mir_pretty_relative_line_numbers { + tcx.sess.source_map().span_to_relative_line_string(span, function_span) + } else { + tcx.sess.source_map().span_to_embeddable_string(span) + }; + + format!("scope {} at {}", scope.index(), location,) } /// Prints local variables in a scope tree. @@ -550,7 +556,7 @@ fn write_scope_tree( "{0:1$} // in {2}", indented_debug_info, ALIGN, - comment(tcx, var_debug_info.source_info), + comment(tcx, var_debug_info.source_info, body.span), )?; } @@ -585,7 +591,7 @@ fn write_scope_tree( indented_decl, ALIGN, local_name, - comment(tcx, local_decl.source_info), + comment(tcx, local_decl.source_info, body.span), )?; } @@ -720,11 +726,17 @@ pub fn write_allocations<'tcx>( write!(w, "{}", display_allocation(tcx, alloc.inner())) }; write!(w, "\n{id:?}")?; - match tcx.get_global_alloc(id) { + match tcx.try_get_global_alloc(id) { // This can't really happen unless there are bugs, but it doesn't cost us anything to // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?, + Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { + write!(w, " (vtable: impl {trait_ref} for {ty})")? + } + Some(GlobalAlloc::VTable(ty, None)) => { + write!(w, " (vtable: impl <auto trait> for {ty})")? + } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { match tcx.eval_static_initializer(did) { Ok(alloc) => { @@ -767,21 +779,21 @@ pub fn write_allocations<'tcx>( /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control /// characters or characters whose value is larger than 127) with a `.` /// This also prints relocations adequately. -pub fn display_allocation<'a, 'tcx, Tag, Extra>( +pub fn display_allocation<'a, 'tcx, Prov, Extra>( tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Tag, Extra>, -) -> RenderAllocation<'a, 'tcx, Tag, Extra> { + alloc: &'a Allocation<Prov, Extra>, +) -> RenderAllocation<'a, 'tcx, Prov, Extra> { RenderAllocation { tcx, alloc } } #[doc(hidden)] -pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { +pub struct RenderAllocation<'a, 'tcx, Prov, Extra> { tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Tag, Extra>, + alloc: &'a Allocation<Prov, Extra>, } -impl<'a, 'tcx, Tag: Provenance, Extra> std::fmt::Display - for RenderAllocation<'a, 'tcx, Tag, Extra> +impl<'a, 'tcx, Prov: Provenance, Extra> std::fmt::Display + for RenderAllocation<'a, 'tcx, Prov, Extra> { fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let RenderAllocation { tcx, alloc } = *self; @@ -825,9 +837,9 @@ fn write_allocation_newline( /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there /// is only one line). Note that your prefix should contain a trailing space as the lines are /// printed directly after it. -fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( +fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>( tcx: TyCtxt<'tcx>, - alloc: &Allocation<Tag, Extra>, + alloc: &Allocation<Prov, Extra>, w: &mut dyn std::fmt::Write, prefix: &str, ) -> std::fmt::Result { @@ -861,7 +873,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( if i != line_start { write!(w, " ")?; } - if let Some(&tag) = alloc.relocations().get(&i) { + if let Some(&prov) = alloc.relocations().get(&i) { // Memory with a relocation must be defined assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok()); let j = i.bytes_usize(); @@ -870,7 +882,7 @@ fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>( let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); let offset = Size::from_bytes(offset); let relocation_width = |bytes| bytes * 3; - let ptr = Pointer::new(tag, offset); + let ptr = Pointer::new(prov, offset); let mut target = format!("{:?}", ptr); if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { // This is too long, try to save some space. diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 620f0380d53..dd9f8795f94 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -2,7 +2,7 @@ use crate::mir::{Body, ConstantKind, Promoted}; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -35,7 +35,6 @@ pub enum UnsafetyViolationDetails { UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, - AssignToDroppingUnionField, AccessToUnionField, MutationOfLayoutConstrainedField, BorrowOfLayoutConstrainedField, @@ -78,11 +77,6 @@ impl UnsafetyViolationDetails { "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \ and cause data races: all of these are undefined behavior", ), - AssignToDroppingUnionField => ( - "assignment to union field that might need dropping", - "the previous content of the field will be dropped, which causes undefined \ - behavior if the field was not properly initialized", - ), AccessToUnionField => ( "access to union field", "the field may not be properly initialized: using uninitialized data will cause \ @@ -241,7 +235,7 @@ pub struct BorrowCheckResult<'tcx> { /// All the opaque types that are restricted to concrete types /// by this function. Unlike the value in `TypeckResults`, this has /// unerased regions. - pub concrete_opaque_types: VecMap<DefId, OpaqueHiddenType<'tcx>>, + pub concrete_opaque_types: VecMap<LocalDefId, OpaqueHiddenType<'tcx>>, pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>, pub used_mut_upvars: SmallVec<[Field; 8]>, pub tainted_by_errors: Option<ErrorGuaranteed>, diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs index d1f3e6b6fe6..b91c0c25782 100644 --- a/compiler/rustc_middle/src/mir/switch_sources.rs +++ b/compiler/rustc_middle/src/mir/switch_sources.rs @@ -1,8 +1,8 @@ //! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after //! `Predecessors`/`PredecessorCache`. +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::sync::OnceCell; use rustc_index::vec::IndexVec; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 1cfda71baf8..89160876401 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -394,7 +394,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::Item(_def_id) => {} ty::InstanceDef::Intrinsic(_def_id) | - ty::InstanceDef::VtableShim(_def_id) | + ty::InstanceDef::VTableShim(_def_id) | ty::InstanceDef::ReifyShim(_def_id) | ty::InstanceDef::Virtual(_def_id, _) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | @@ -1118,11 +1118,9 @@ macro_rules! visit_place_fns { context: PlaceContext, location: Location, ) { - // FIXME: Use PlaceRef::iter_projections, once that exists. - let mut cursor = place_ref.projection; - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - self.visit_projection_elem(place_ref.local, cursor, elem, context, location); + for (base, elem) in place_ref.iter_projections().rev() { + let base_proj = base.projection; + self.visit_projection_elem(place_ref.local, base_proj, elem, context, location); } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cbc45526e89..466a0fc25f7 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -351,7 +351,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for {}", tcx.def_path_str(key), } @@ -360,7 +360,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const_of_const_arg( key: (LocalDefId, DefId) - ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { + ) -> Result<Option<&'tcx [ty::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for the const argument {}", @@ -1634,11 +1634,15 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) desc { "calculating the lib features map" } } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option<Symbol>)] { + query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option<Symbol>)] { desc { "calculating the lib features defined in a crate" } separate_provide_extern } + query stability_implications(_: CrateNum) -> FxHashMap<Symbol, Symbol> { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the implications between `#[unstable]` features defined in a crate" } + separate_provide_extern + } /// Whether the function is an intrinsic query is_intrinsic(def_id: DefId) -> bool { desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) } @@ -2053,4 +2057,12 @@ rustc_queries! { desc { |tcx| "looking up generator diagnostic data of `{}`", tcx.def_path_str(key) } separate_provide_extern } + + query permits_uninit_init(key: TyAndLayout<'tcx>) -> bool { + desc { "checking to see if {:?} permits being left uninit", key.ty } + } + + query permits_zero_init(key: TyAndLayout<'tcx>) -> bool { + desc { "checking to see if {:?} permits being left zeroed", key.ty } + } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3fe6394ad7e..36db8d04918 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -30,7 +30,6 @@ use rustc_target::asm::InlineAsmRegOrRegClass; use std::fmt; use std::ops::Index; -pub mod abstract_const; pub mod visit; newtype_index! { @@ -182,6 +181,9 @@ pub enum StmtKind<'tcx> { /// `let pat: ty = <INIT>` initializer: Option<ExprId>, + /// `let pat: ty = <INIT> else { <ELSE> } + else_block: Option<Block>, + /// The lint level for this `let` statement. lint_level: LintLevel, }, diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs deleted file mode 100644 index 527dbd1cd09..00000000000 --- a/compiler/rustc_middle/src/thir/abstract_const.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! A subset of a mir body used for const evaluatability checking. -use crate::mir; -use crate::ty::{self, Ty, TyCtxt}; -use rustc_errors::ErrorGuaranteed; - -rustc_index::newtype_index! { - /// An index into an `AbstractConst`. - pub struct NodeId { - derive [HashStable] - DEBUG_FORMAT = "n{}", - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum CastKind { - /// thir::ExprKind::As - As, - /// thir::ExprKind::Use - Use, -} - -/// A node of an `AbstractConst`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum Node<'tcx> { - Leaf(ty::Const<'tcx>), - Binop(mir::BinOp, NodeId, NodeId), - UnaryOp(mir::UnOp, NodeId), - FunctionCall(NodeId, &'tcx [NodeId]), - Cast(CastKind, NodeId, Ty<'tcx>), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -pub enum NotConstEvaluatable { - Error(ErrorGuaranteed), - MentionsInfer, - MentionsParam, -} - -impl From<ErrorGuaranteed> for NotConstEvaluatable { - fn from(e: ErrorGuaranteed) -> NotConstEvaluatable { - NotConstEvaluatable::Error(e) - } -} - -TrivialTypeTraversalAndLiftImpls! { - NotConstEvaluatable, -} - -impl<'tcx> TyCtxt<'tcx> { - #[inline] - pub fn thir_abstract_const_opt_const_arg( - self, - def: ty::WithOptConstParam<rustc_hir::def_id::DefId>, - ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { - if let Some((did, param_did)) = def.as_const_arg() { - self.thir_abstract_const_of_const_arg((did, param_did)) - } else { - self.thir_abstract_const(def.did) - } - } -} diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c8d09875c28..97249fdd175 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm init_scope: _, ref pattern, lint_level: _, + else_block, } => { if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } visitor.visit_pat(pattern); + if let Some(block) = else_block { + visitor.visit_block(block) + } } } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index fe7f72024d3..72b848c3ee2 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -10,9 +10,9 @@ mod structural_impls; pub mod util; use crate::infer::canonical::Canonical; -use crate::thir::abstract_const::NotConstEvaluatable; +use crate::ty::abstract_const::NotConstEvaluatable; use crate::ty::subst::SubstsRef; -use crate::ty::{self, AdtKind, Ty, TyCtxt}; +use crate::ty::{self, AdtKind, Predicate, Ty, TyCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -139,13 +139,8 @@ impl<'tcx> ObligationCause<'tcx> { ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: Default::default() } } - pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { + pub fn span(&self) -> Span { match *self.code() { - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType => { - tcx.sess.source_map().guess_head_span(self.span) - } ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { arm_span, .. @@ -316,18 +311,10 @@ pub enum ObligationCauseCode<'tcx> { }, /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplConstObligation, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplMethodObligation { - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - }, - - /// Error derived when matching traits/impls; see ObligationCause for more details - CompareImplTypeObligation { + CompareImplItemObligation { impl_item_def_id: LocalDefId, trait_item_def_id: DefId, + kind: ty::AssocKind, }, /// Checking that the bounds of a trait's associated type hold for a given impl @@ -356,7 +343,7 @@ pub enum ObligationCauseCode<'tcx> { ConstPatternStructural, /// Computing common supertype in an if expression - IfExpression(Box<IfExpressionCause>), + IfExpression(Box<IfExpressionCause<'tcx>>), /// Computing common supertype of an if expression with no else counter-part IfExpressionWithNoElse, @@ -419,6 +406,7 @@ pub enum ObligationCauseCode<'tcx> { BinOp { rhs_span: Option<Span>, is_lit: bool, + output_pred: Option<Predicate<'tcx>>, }, } @@ -492,22 +480,27 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression { #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] pub struct MatchExpressionArmCause<'tcx> { + pub arm_block_id: Option<hir::HirId>, + pub arm_ty: Ty<'tcx>, pub arm_span: Span, + pub prior_arm_block_id: Option<hir::HirId>, + pub prior_arm_ty: Ty<'tcx>, + pub prior_arm_span: Span, pub scrut_span: Span, - pub semi_span: Option<(Span, StatementAsExpression)>, pub source: hir::MatchSource, pub prior_arms: Vec<Span>, - pub last_ty: Ty<'tcx>, pub scrut_hir_id: hir::HirId, pub opt_suggest_box_span: Option<Span>, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct IfExpressionCause { - pub then: Span, - pub else_sp: Span, - pub outer: Option<Span>, - pub semicolon: Option<(Span, StatementAsExpression)>, +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Lift, TypeFoldable, TypeVisitable)] +pub struct IfExpressionCause<'tcx> { + pub then_id: hir::HirId, + pub else_id: hir::HirId, + pub then_ty: Ty<'tcx>, + pub else_ty: Ty<'tcx>, + pub outer_span: Option<Span>, pub opt_suggest_box_span: Option<Span>, } @@ -546,17 +539,9 @@ pub enum SelectionError<'tcx> { ErrorReporting, /// Multiple applicable `impl`s where found. The `DefId`s correspond to /// all the `impl`s' Items. - Ambiguous(Vec<AmbiguousSelection>), + Ambiguous(Vec<DefId>), } -#[derive(Copy, Clone, Debug)] -pub enum AmbiguousSelection { - Impl(DefId), - ParamEnv(Span), -} - -TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, } - /// When performing resolution, it is typically the case that there /// can be one of three outcomes: /// diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 8f1a1564fc8..7fbd57ac735 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> { // Lift implementations TrivialTypeTraversalAndLiftImpls! { - super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, } diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs index 8ce428c9799..d54b8c599d9 100644 --- a/compiler/rustc_middle/src/traits/util.rs +++ b/compiler/rustc_middle/src/traits/util.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use crate::ty::{PolyTraitRef, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs new file mode 100644 index 00000000000..bed809930da --- /dev/null +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -0,0 +1,194 @@ +//! A subset of a mir body used for const evaluatability checking. +use crate::mir; +use crate::ty::visit::TypeVisitable; +use crate::ty::{self, subst::Subst, DelaySpanBugEmitted, EarlyBinder, SubstsRef, Ty, TyCtxt}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::DefId; +use std::cmp; +use std::ops::ControlFlow; + +rustc_index::newtype_index! { + /// An index into an `AbstractConst`. + pub struct NodeId { + derive [HashStable] + DEBUG_FORMAT = "n{}", + } +} + +/// A tree representing an anonymous constant. +/// +/// This is only able to represent a subset of `MIR`, +/// and should not leak any information about desugarings. +#[derive(Debug, Clone, Copy)] +pub struct AbstractConst<'tcx> { + // FIXME: Consider adding something like `IndexSlice` + // and use this here. + inner: &'tcx [Node<'tcx>], + substs: SubstsRef<'tcx>, +} + +impl<'tcx> AbstractConst<'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + uv: ty::Unevaluated<'tcx, ()>, + ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { + let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; + debug!("AbstractConst::new({:?}) = {:?}", uv, inner); + Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) })) + } + + pub fn from_const( + tcx: TyCtxt<'tcx>, + ct: ty::Const<'tcx>, + ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { + match ct.kind() { + ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), + ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported), + _ => Ok(None), + } + } + + #[inline] + pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { + AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } + } + + #[inline] + pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { + let node = self.inner.last().copied().unwrap(); + match node { + Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)), + Node::Cast(kind, operand, ty) => { + Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs)) + } + // Don't perform substitution on the following as they can't directly contain generic params + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, + } + } + + pub fn unify_failure_kind(self, tcx: TyCtxt<'tcx>) -> FailureKind { + let mut failure_kind = FailureKind::Concrete; + walk_abstract_const::<!, _>(tcx, self, |node| { + match node.root(tcx) { + Node::Leaf(leaf) => { + if leaf.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if leaf.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + } + Node::Cast(_, _, ty) => { + if ty.has_infer_types_or_consts() { + failure_kind = FailureKind::MentionsInfer; + } else if ty.has_param_types_or_consts() { + failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); + } + } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {} + } + ControlFlow::CONTINUE + }); + failure_kind + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum CastKind { + /// thir::ExprKind::As + As, + /// thir::ExprKind::Use + Use, +} + +/// A node of an `AbstractConst`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum Node<'tcx> { + Leaf(ty::Const<'tcx>), + Binop(mir::BinOp, NodeId, NodeId), + UnaryOp(mir::UnOp, NodeId), + FunctionCall(NodeId, &'tcx [NodeId]), + Cast(CastKind, NodeId, Ty<'tcx>), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum NotConstEvaluatable { + Error(ErrorGuaranteed), + MentionsInfer, + MentionsParam, +} + +impl From<ErrorGuaranteed> for NotConstEvaluatable { + fn from(e: ErrorGuaranteed) -> NotConstEvaluatable { + NotConstEvaluatable::Error(e) + } +} + +TrivialTypeTraversalAndLiftImpls! { + NotConstEvaluatable, +} + +impl<'tcx> TyCtxt<'tcx> { + #[inline] + pub fn thir_abstract_const_opt_const_arg( + self, + def: ty::WithOptConstParam<DefId>, + ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { + if let Some((did, param_did)) = def.as_const_arg() { + self.thir_abstract_const_of_const_arg((did, param_did)) + } else { + self.thir_abstract_const(def.did) + } + } +} + +#[instrument(skip(tcx, f), level = "debug")] +pub fn walk_abstract_const<'tcx, R, F>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + mut f: F, +) -> ControlFlow<R> +where + F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, +{ + #[instrument(skip(tcx, f), level = "debug")] + fn recurse<'tcx, R>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, + ) -> ControlFlow<R> { + f(ct)?; + let root = ct.root(tcx); + debug!(?root); + match root { + Node::Leaf(_) => ControlFlow::CONTINUE, + Node::Binop(_, l, r) => { + recurse(tcx, ct.subtree(l), f)?; + recurse(tcx, ct.subtree(r), f) + } + Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), + Node::FunctionCall(func, args) => { + recurse(tcx, ct.subtree(func), f)?; + args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) + } + Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), + } + } + + recurse(tcx, ct, &mut f) +} + +// We were unable to unify the abstract constant with +// a constant found in the caller bounds, there are +// now three possible cases here. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum FailureKind { + /// The abstract const still references an inference + /// variable, in this case we return `TooGeneric`. + MentionsInfer, + /// The abstract const references a generic parameter, + /// this means that we emit an error here. + MentionsParam, + /// The substs are concrete enough that we can simply + /// try and evaluate the given constant. + Concrete, +} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 4cac7670735..809406aff18 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -52,6 +52,8 @@ bitflags! { /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. /// (i.e., this flag is never set unless this ADT is an enum). const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + /// Indicates whether the type is `UnsafeCell`. + const IS_UNSAFE_CELL = 1 << 9; } } @@ -247,6 +249,9 @@ impl AdtDefData { if Some(did) == tcx.lang_items().manually_drop() { flags |= AdtFlags::IS_MANUALLY_DROP; } + if Some(did) == tcx.lang_items().unsafe_cell_type() { + flags |= AdtFlags::IS_UNSAFE_CELL; + } AdtDefData { did, variants, flags, repr } } @@ -333,6 +338,12 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_BOX) } + /// Returns `true` if this is UnsafeCell<T>. + #[inline] + pub fn is_unsafe_cell(self) -> bool { + self.flags().contains(AdtFlags::IS_UNSAFE_CELL) + } + /// Returns `true` if this is `ManuallyDrop<T>`. #[inline] pub fn is_manually_drop(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 2c93af50667..eb732148e3e 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -105,6 +105,16 @@ impl AssocKind { } } +impl std::fmt::Display for AssocKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AssocKind::Fn => write!(f, "method"), + AssocKind::Const => write!(f, "associated const"), + AssocKind::Type => write!(f, "associated type"), + } + } +} + /// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. /// /// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index f5ce43f3afb..27b9d27b8bb 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -128,6 +128,14 @@ impl<'tcx> ClosureKind { None } } + + pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId { + match self { + ClosureKind::Fn => tcx.lang_items().fn_once_trait().unwrap(), + ClosureKind::FnMut => tcx.lang_items().fn_mut_trait().unwrap(), + ClosureKind::FnOnce => tcx.lang_items().fn_trait().unwrap(), + } + } } /// A composite describing a `Place` that is captured by a closure. @@ -174,7 +182,11 @@ impl<'tcx> CapturedPlace<'tcx> { .unwrap(); } ty => { - bug!("Unexpected type {:?} for `Field` projection", ty) + span_bug!( + self.get_capture_kind_span(tcx), + "Unexpected type {:?} for `Field` projection", + ty + ) } }, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 9a363914dc3..51137c52659 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -12,7 +12,6 @@ use crate::mir::{ self, interpret::{AllocId, ConstAllocation}, }; -use crate::thir; use crate::traits; use crate::ty::subst::SubstsRef; use crate::ty::{self, AdtDef, Ty}; @@ -346,7 +345,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for [thir::abstract_const::Node<'tcx>] + for [ty::abstract_const::Node<'tcx>] { fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc_from_iter( @@ -356,7 +355,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> } impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> - for [thir::abstract_const::NodeId] + for [ty::abstract_const::NodeId] { fn decode(decoder: &mut D) -> &'tcx Self { decoder.interner().arena.alloc_from_iter( @@ -524,4 +523,5 @@ impl_binder_encode_decode! { ty::ExistentialPredicate<'tcx>, ty::TraitRef<'tcx>, Vec<ty::GeneratorInteriorTypeCause<'tcx>>, + ty::ExistentialTraitRef<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index a4e7a12bba3..f8792edc017 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> { pub fn to_valtree(self) -> ty::ValTree<'tcx> { match self.kind() { ty::ConstKind::Value(valtree) => valtree, - _ => bug!("expected ConstKind::Value"), + _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 973dc3dd4a1..93707bb18ce 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -80,33 +80,25 @@ impl<'tcx> ValTree<'tcx> { } /// Get the values inside the ValTree as a slice of bytes. This only works for - /// constants with types &str and &[u8]. + /// constants with types &str, &[u8], or [u8; _]. pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { match ty.kind() { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { - ty::Str => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) - .collect::<Vec<_>>(); - - return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); - } - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) - .collect::<Vec<_>>(); - - return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); - } - _ => {} + // `&str` can be interpreted as raw bytes + ty::Str => {} + // `&[u8]` can be interpreted as raw bytes + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // other `&_` can't be interpreted as raw bytes + _ => return None, }, - _ => {} + // `[u8; N]` can be interpreted as raw bytes + ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // Otherwise, type cannot be interpreted as raw bytes + _ => return None, } - None + Some(tcx.arena.alloc_from_iter( + self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()), + )) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a594dab2e20..0f98d19820e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -542,7 +542,7 @@ pub struct TypeckResults<'tcx> { /// even if they are only set in dead code (which doesn't show up in MIR). /// For type-alias-impl-trait, this map is only used to prevent query cycles, /// so the hidden types are all `None`. - pub concrete_opaque_types: VecMap<DefId, Option<Ty<'tcx>>>, + pub concrete_opaque_types: VecMap<LocalDefId, Option<Ty<'tcx>>>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 25bc6dc6167..dd2f4321060 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use crate::ty::{ - visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy, + visit::TypeVisitable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst, InferTy, PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; @@ -82,15 +82,18 @@ pub trait IsSuggestable<'tcx> { /// meaningful rendered suggestions when pretty-printed. We leave some /// nonsense, such as region vars, since those render as `'_` and are /// usually okay to reinterpret as elided lifetimes. - fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool; + /// + /// Only if `infer_suggestable` is true, we consider type and const + /// inference variables to be suggestable. + fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; } impl<'tcx, T> IsSuggestable<'tcx> for T where T: TypeVisitable<'tcx>, { - fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool { - self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue() + fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool { + self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue() } } @@ -100,7 +103,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( err: &mut Diagnostic, trait_pred: PolyTraitPredicate<'tcx>, ) -> bool { - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return false; } @@ -419,6 +422,7 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> { pub struct IsSuggestableVisitor<'tcx> { tcx: TyCtxt<'tcx>, + infer_suggestable: bool, } impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { @@ -426,6 +430,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match t.kind() { + Infer(InferTy::TyVar(_)) if self.infer_suggestable => {} + FnDef(..) | Closure(..) | Infer(..) @@ -479,6 +485,8 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> { match c.kind() { + ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => {} + ConstKind::Infer(..) | ConstKind::Bound(..) | ConstKind::Placeholder(..) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 49a518b101d..91246051316 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -660,12 +660,8 @@ impl<T> Trait<T> for X { | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), ) ); - let impl_comparison = matches!( - cause_code, - ObligationCauseCode::CompareImplMethodObligation { .. } - | ObligationCauseCode::CompareImplTypeObligation { .. } - | ObligationCauseCode::CompareImplConstObligation - ); + let impl_comparison = + matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); let assoc = self.associated_item(proj_ty.item_def_id); if !callable_scope || impl_comparison { // We do not want to suggest calling functions when the reason of the diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index f8893ae29f5..878f31af00f 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -445,10 +445,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { let ct = (self.fld_c)(bound_const, ct.ty()); ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32()) } - _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self), - _ => ct, + _ => ct.super_fold_with(self), } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } } impl<'tcx> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 84547dca453..add2df25884 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -85,10 +85,10 @@ impl GenericParamDef { ) -> Option<EarlyBinder<ty::GenericArg<'tcx>>> { match self.kind { GenericParamDefKind::Type { has_default, .. } if has_default => { - Some(EarlyBinder(tcx.type_of(self.def_id).into())) + Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into())) } GenericParamDefKind::Const { has_default } if has_default => { - Some(EarlyBinder(tcx.const_param_default(self.def_id).into())) + Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into())) } _ => None, } diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 88397a2bb56..263d64a5777 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -147,15 +147,15 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId { ty::tls::with_opt(|tcx| { trace!("hashing {:?}", *self); let tcx = tcx.expect("can't hash AllocIds during hir lowering"); - tcx.get_global_alloc(*self).hash_stable(hcx, hasher); + tcx.try_get_global_alloc(*self).hash_stable(hcx, hasher); }); } } // `Relocations` with default type parameters is a sorted map. -impl<'a, Tag> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Tag> +impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Prov> where - Tag: HashStable<StableHashingContext<'a>>, + Prov: HashStable<StableHashingContext<'a>>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.len().hash_stable(hcx, hasher); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 391abdbe84c..33a46f809b0 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -49,7 +49,7 @@ pub enum InstanceDef<'tcx> { /// /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - /// and dereference the argument to call the original function. - VtableShim(DefId), + VTableShim(DefId), /// `fn()` pointer where the function itself cannot be turned into a pointer. /// @@ -145,7 +145,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id(self) -> DefId { match self { InstanceDef::Item(def) => def.did, - InstanceDef::VtableShim(def_id) + InstanceDef::VTableShim(def_id) | InstanceDef::ReifyShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) @@ -161,7 +161,7 @@ impl<'tcx> InstanceDef<'tcx> { match self { ty::InstanceDef::Item(def) => Some(def.did), ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id), - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) @@ -176,7 +176,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> { match self { InstanceDef::Item(def) => def, - InstanceDef::VtableShim(def_id) + InstanceDef::VTableShim(def_id) | InstanceDef::ReifyShim(def_id) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) @@ -273,7 +273,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::Intrinsic(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) - | InstanceDef::VtableShim(..) => true, + | InstanceDef::VTableShim(..) => true, } } } @@ -290,7 +290,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { match self.def { InstanceDef::Item(_) => Ok(()), - InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), + InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), @@ -434,7 +434,7 @@ impl<'tcx> Instance<'tcx> { && tcx.generics_of(def_id).has_self; if is_vtable_shim { debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) + Some(Instance { def: InstanceDef::VTableShim(def_id), substs }) } else { Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { match resolved.def { @@ -605,7 +605,7 @@ impl<'tcx> Instance<'tcx> { /// identity parameters if they are determined to be unused in `instance.def`. pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { debug!("polymorphize: running polymorphization analysis"); - if !tcx.sess.opts.debugging_opts.polymorphize { + if !tcx.sess.opts.unstable_opts.polymorphize { return self; } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f87b6e4212d..dde55dd9655 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -542,14 +542,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { debug!("univariant offset: {:?} field: {:#?}", offset, field); offsets[i as usize] = offset; - if !repr.hide_niche() { - if let Some(mut niche) = field.largest_niche { - let available = niche.available(dl); - if available > largest_niche_available { - largest_niche_available = available; - niche.offset += offset; - largest_niche = Some(niche); - } + if let Some(mut niche) = field.largest_niche { + let available = niche.available(dl); + if available > largest_niche_available { + largest_niche_available = available; + niche.offset += offset; + largest_niche = Some(niche); } } @@ -1078,6 +1076,29 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let mut st = self.univariant_uninterned(ty, &variants[v], &def.repr(), kind)?; st.variants = Variants::Single { index: v }; + + if def.is_unsafe_cell() { + let hide_niches = |scalar: &mut _| match scalar { + Scalar::Initialized { value, valid_range } => { + *valid_range = WrappingRange::full(value.size(dl)) + } + // Already doesn't have any niches + Scalar::Union { .. } => {} + }; + match &mut st.abi { + Abi::Uninhabited => {} + Abi::Scalar(scalar) => hide_niches(scalar), + Abi::ScalarPair(a, b) => { + hide_niches(a); + hide_niches(b); + } + Abi::Vector { element, count: _ } => hide_niches(element), + Abi::Aggregate { sized: _ } => {} + } + st.largest_niche = None; + return Ok(tcx.intern_layout(st)); + } + let (start, end) = self.tcx.layout_scalar_valid_range(def.did()); match st.abi { Abi::Scalar(ref mut scalar) | Abi::ScalarPair(ref mut scalar, _) => { @@ -1106,11 +1127,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } // Update `largest_niche` if we have introduced a larger niche. - let niche = if def.repr().hide_niche() { - None - } else { - Niche::from_scalar(dl, Size::ZERO, *scalar) - }; + let niche = Niche::from_scalar(dl, Size::ZERO, *scalar); if let Some(niche) = niche { match st.largest_niche { Some(largest_niche) => { @@ -1897,7 +1914,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { // If we are running with `-Zprint-type-sizes`, maybe record layouts // for dumping later. - if self.tcx.sess.opts.debugging_opts.print_type_sizes { + if self.tcx.sess.opts.unstable_opts.print_type_sizes { self.record_layout_for_printing_outlined(layout) } } @@ -1958,7 +1975,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { min_size = field_end; } FieldInfo { - name: name.to_string(), + name, offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), @@ -1967,7 +1984,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .collect(); VariantInfo { - name: n.map(|n| n.to_string()), + name: n, kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, align: layout.align.abi.bytes(), size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, @@ -2601,14 +2618,14 @@ where // Use conservative pointer kind if not optimizing. This saves us the // Freeze/Unpin queries, and can save time in the codegen backend (noalias // attributes in LLVM have compile-time cost even in unoptimized builds). - PointerKind::Shared + PointerKind::SharedMutable } else { match mt { hir::Mutability::Not => { if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) { PointerKind::Frozen } else { - PointerKind::Shared + PointerKind::SharedMutable } } hir::Mutability::Mut => { @@ -2619,7 +2636,7 @@ where if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) { PointerKind::UniqueBorrowed } else { - PointerKind::Shared + PointerKind::UniqueBorrowedPinned } } } @@ -2754,7 +2771,7 @@ impl<'tcx> ty::Instance<'tcx> { _ => unreachable!(), }; - if let ty::InstanceDef::VtableShim(..) = self.def { + if let ty::InstanceDef::VTableShim(..) = self.def { // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. sig = sig.map_bound(|mut sig| { let mut inputs_and_output = sig.inputs_and_output.to_vec(); @@ -2899,7 +2916,7 @@ pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: Spe // // This is not part of `codegen_fn_attrs` as it can differ between crates // and therefore cannot be computed in core. - if tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Abort { + if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort { if Some(did) == tcx.lang_items().drop_in_place_fn() { return false; } @@ -3238,10 +3255,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable // for the entire duration of the function as they can be deallocated - // at any time. Set their valid size to 0. + // at any time. Same for shared mutable references. If LLVM had a + // way to say "dereferenceable on entry" we could use it here. attrs.pointee_size = match kind { - PointerKind::UniqueOwned => Size::ZERO, - _ => pointee.size, + PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned + | PointerKind::Frozen => pointee.size, + PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO, }; // `Box`, `&T`, and `&mut T` cannot be undef. @@ -3249,7 +3269,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // this attribute doesn't make it UB for the pointed-to data to be undef. attrs.set(ArgAttribute::NoUndef); - // `Box` pointer parameters never alias because ownership is transferred + // The aliasing rules for `Box<T>` are still not decided, but currently we emit + // `noalias` for it. This can be turned off using an unstable flag. + // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 + let noalias_for_box = + self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true); + // `&mut` pointer parameters never alias other parameters, // or mutable global data // @@ -3263,8 +3288,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // or not to actually emit the attribute. It can also be controlled with the // `-Zmutable-noalias` debugging option. let no_alias = match kind { - PointerKind::Shared | PointerKind::UniqueBorrowed => false, - PointerKind::UniqueOwned => true, + PointerKind::SharedMutable + | PointerKind::UniqueBorrowed + | PointerKind::UniqueBorrowedPinned => false, + PointerKind::UniqueOwned => noalias_for_box, PointerKind::Frozen => !is_return, }; if no_alias { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f15108fb750..31c523aaca9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -92,6 +92,7 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub mod _match; +pub mod abstract_const; pub mod adjustment; pub mod binding; pub mod cast; @@ -574,6 +575,19 @@ impl<'tcx> Predicate<'tcx> { Some(tcx.mk_predicate(kind)) } + + pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> Self { + if let PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) = self.kind().skip_binder() + && constness != BoundConstness::NotConst + { + self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Trait(TraitPredicate { + trait_ref, + constness: BoundConstness::NotConst, + polarity, + }))); + } + self + } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> { @@ -789,22 +803,15 @@ pub struct TraitPredicate<'tcx> { pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; impl<'tcx> TraitPredicate<'tcx> { - pub fn remap_constness(&mut self, tcx: TyCtxt<'tcx>, param_env: &mut ParamEnv<'tcx>) { - if std::intrinsics::unlikely(Some(self.trait_ref.def_id) == tcx.lang_items().drop_trait()) { - // remap without changing constness of this predicate. - // this is because `T: ~const Drop` has a different meaning to `T: Drop` - // FIXME(fee1-dead): remove this logic after beta bump - param_env.remap_constness_with(self.constness) - } else { - *param_env = param_env.with_constness(self.constness.and(param_env.constness())) - } + pub fn remap_constness(&mut self, param_env: &mut ParamEnv<'tcx>) { + *param_env = param_env.with_constness(self.constness.and(param_env.constness())) } /// Remap the constness of this predicate before emitting it for diagnostics. pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { // this is different to `remap_constness` that callees want to print this predicate // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the - // param_env is not const because we it is always satisfied in non-const contexts. + // param_env is not const because it is always satisfied in non-const contexts. if let hir::Constness::NotConst = param_env.constness() { self.constness = ty::BoundConstness::NotConst; } @@ -822,6 +829,14 @@ impl<'tcx> TraitPredicate<'tcx> { pub fn is_const_if_const(self) -> bool { self.constness == BoundConstness::ConstIfConst } + + pub fn is_constness_satisfied_by(self, constness: hir::Constness) -> bool { + match (self.constness, constness) { + (BoundConstness::NotConst, _) + | (BoundConstness::ConstIfConst, hir::Constness::Const) => true, + (BoundConstness::ConstIfConst, hir::Constness::NotConst) => false, + } + } } impl<'tcx> PolyTraitPredicate<'tcx> { @@ -1032,6 +1047,24 @@ impl<'tcx> Predicate<'tcx> { } } + pub fn to_opt_poly_projection_pred(self) -> Option<PolyProjectionPredicate<'tcx>> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Projection(t) => Some(predicate.rebind(t)), + PredicateKind::Trait(..) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::TypeOutlives(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) + | PredicateKind::TypeWellFormedFromEnv(..) => None, + } + } + pub fn to_opt_type_outlives(self) -> Option<PolyTypeOutlivesPredicate<'tcx>> { let predicate = self.kind(); match predicate.skip_binder() { @@ -1089,8 +1122,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, Lift)] #[derive(TypeFoldable, TypeVisitable)] pub struct OpaqueTypeKey<'tcx> { - // FIXME(oli-obk): make this a LocalDefId - pub def_id: DefId, + pub def_id: LocalDefId, pub substs: SubstsRef<'tcx>, } @@ -1720,11 +1752,9 @@ bitflags! { const IS_TRANSPARENT = 1 << 2; // Internal only for now. If true, don't reorder fields. const IS_LINEAR = 1 << 3; - // If true, don't expose any niche to type's context. - const HIDE_NICHE = 1 << 4; // If true, the type's layout can be randomized using // the seed stored in `ReprOptions.layout_seed` - const RANDOMIZE_LAYOUT = 1 << 5; + const RANDOMIZE_LAYOUT = 1 << 4; // Any of these flags being set prevent field reordering optimisation. const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | ReprFlags::IS_SIMD.bits @@ -1763,7 +1793,7 @@ impl ReprOptions { // If the user defined a custom seed for layout randomization, xor the item's // path hash with the user defined seed, this will allowing determinism while // still allowing users to further randomize layout generation for e.g. fuzzing - if let Some(user_seed) = tcx.sess.opts.debugging_opts.layout_seed { + if let Some(user_seed) = tcx.sess.opts.unstable_opts.layout_seed { field_shuffle_seed ^= user_seed; } @@ -1781,7 +1811,6 @@ impl ReprOptions { ReprFlags::empty() } attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, - attr::ReprNoNiche => ReprFlags::HIDE_NICHE, attr::ReprSimd => ReprFlags::IS_SIMD, attr::ReprInt(i) => { size = Some(i); @@ -1797,7 +1826,7 @@ impl ReprOptions { // If `-Z randomize-layout` was enabled for the type definition then we can // consider performing layout randomization - if tcx.sess.opts.debugging_opts.randomize_layout { + if tcx.sess.opts.unstable_opts.randomize_layout { flags.insert(ReprFlags::RANDOMIZE_LAYOUT); } @@ -1834,11 +1863,6 @@ impl ReprOptions { self.flags.contains(ReprFlags::IS_LINEAR) } - #[inline] - pub fn hide_niche(&self) -> bool { - self.flags.contains(ReprFlags::HIDE_NICHE) - } - /// Returns the discriminant type, given these `repr` options. /// This must only be called on enums! pub fn discr_type(&self) -> attr::IntType { @@ -2125,7 +2149,7 @@ impl<'tcx> TyCtxt<'tcx> { } } } - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::FnPtrShim(..) diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 54ba9e84fdb..e189ee2fc4d 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -3,7 +3,7 @@ use rustc_index::vec::{Idx, IndexVec}; use crate::middle::exported_symbols::ExportedSymbol; use crate::mir::Body; -use crate::thir::abstract_const::Node; +use crate::ty::abstract_const::Node; use crate::ty::{ self, Const, FnSig, GeneratorDiagnosticData, GenericPredicates, Predicate, TraitRef, Ty, }; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f721a175c98..7f2e81a71a9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -297,7 +297,7 @@ pub trait PrettyPrinter<'tcx>: mut self, def_id: DefId, ) -> Result<(Self::Path, bool), Self::Error> { - if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths + if !self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) || NO_TRIMMED_PATH.with(|flag| flag.get()) || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) @@ -712,7 +712,7 @@ pub trait PrettyPrinter<'tcx>: p!(write("closure")); // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { - if self.tcx().sess.opts.debugging_opts.span_free_formats { + if self.tcx().sess.opts.unstable_opts.span_free_formats { p!("@", print_def_path(did.to_def_id(), substs)); } else { let span = self.tcx().def_span(did); @@ -1030,11 +1030,11 @@ pub trait PrettyPrinter<'tcx>: } } - fn ty_infer_name(&self, _: ty::TyVid) -> Option<String> { + fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> { None } - fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<String> { + fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option<Symbol> { None } @@ -1269,7 +1269,7 @@ pub trait PrettyPrinter<'tcx>: if let ty::Array(elem, len) = inner.kind() { if let ty::Uint(ty::UintTy::U8) = elem.kind() { if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() { - match self.tcx().get_global_alloc(alloc_id) { + match self.tcx().try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { let len = int.assert_bits(self.tcx().data_layout.pointer_size); let range = @@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>: p!("<too short allocation>") } } - // FIXME: for statics and functions, we could in principle print more detail. + // FIXME: for statics, vtables, and functions, we could in principle print more detail. Some(GlobalAlloc::Static(def_id)) => { p!(write("<static({:?})>", def_id)) } Some(GlobalAlloc::Function(_)) => p!("<function>"), + Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), None => p!("<dangling pointer>"), } return Ok(self); @@ -1297,7 +1298,8 @@ pub trait PrettyPrinter<'tcx>: ty::FnPtr(_) => { // FIXME: We should probably have a helper method to share code with the "Byte strings" // printing above (which also has to handle pointers to all sorts of things). - if let Some(GlobalAlloc::Function(instance)) = self.tcx().get_global_alloc(alloc_id) + if let Some(GlobalAlloc::Function(instance)) = + self.tcx().try_get_global_alloc(alloc_id) { self = self.typed_value( |this| this.print_value_path(instance.def_id(), instance.substs), @@ -1377,9 +1379,9 @@ pub trait PrettyPrinter<'tcx>: /// This is overridden for MIR printing because we only want to hide alloc ids from users, not /// from MIR where it is actually useful. - fn pretty_print_const_pointer<Tag: Provenance>( + fn pretty_print_const_pointer<Prov: Provenance>( mut self, - _: Pointer<Tag>, + _: Pointer<Prov>, ty: Ty<'tcx>, print_ty: bool, ) -> Result<Self::Const, Self::Error> { @@ -1450,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>: } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); @@ -1550,8 +1552,8 @@ pub struct FmtPrinterData<'a, 'tcx> { pub region_highlight_mode: RegionHighlightMode<'tcx>, - pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<String> + 'a>>, - pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<String> + 'a>>, + pub ty_infer_name_resolver: Option<Box<dyn Fn(ty::TyVid) -> Option<Symbol> + 'a>>, + pub const_infer_name_resolver: Option<Box<dyn Fn(ty::ConstVid<'tcx>) -> Option<Symbol> + 'a>>, } impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> { @@ -1727,7 +1729,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> { - self.pretty_print_const(ct, true) + self.pretty_print_const(ct, false) } fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { @@ -1841,11 +1843,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { - fn ty_infer_name(&self, id: ty::TyVid) -> Option<String> { + fn ty_infer_name(&self, id: ty::TyVid) -> Option<Symbol> { self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id)) } - fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<String> { + fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<Symbol> { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } @@ -1919,7 +1921,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { return true; } - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions; match *region { ty::ReEarlyBound(ref data) => { @@ -1952,9 +1954,9 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { } } - fn pretty_print_const_pointer<Tag: Provenance>( + fn pretty_print_const_pointer<Prov: Provenance>( self, - p: Pointer<Tag>, + p: Pointer<Prov>, ty: Ty<'tcx>, print_ty: bool, ) -> Result<Self::Const, Self::Error> { @@ -1992,7 +1994,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { return Ok(self); } - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions; // These printouts are concise. They do not contain all the information // the user might want to diagnose an error, but there is basically no way @@ -2554,7 +2556,7 @@ define_print_and_forward_display! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); - if let ty::BoundConstness::ConstIfConst = self.constness { + if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl { p!("~const "); } p!(print(self.trait_ref.print_only_trait_path())) diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 3d662ed5de4..2452bcf6a61 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -28,6 +28,7 @@ use crate::traits::query::{ use crate::traits::specialization_graph; use crate::traits::{self, ImplSource}; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::TyAndLayout; use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::GeneratorDiagnosticData; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 391a0a20c96..7660a2f3af6 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -224,6 +224,7 @@ TrivialTypeTraversalAndLiftImpls! { // general `Region`. crate::ty::BoundRegionKind, crate::ty::AssocItem, + crate::ty::AssocKind, crate::ty::Placeholder<crate::ty::BoundRegionKind>, crate::ty::ClosureKind, crate::ty::FreeRegion, @@ -624,7 +625,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { match self { ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), - ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), + ty::InstanceDef::VTableShim(def_id) => Some(ty::InstanceDef::VTableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -927,7 +928,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { substs: self.substs.try_fold_with(folder)?, def: match self.def { Item(def) => Item(def.try_fold_with(folder)?), - VtableShim(did) => VtableShim(did.try_fold_with(folder)?), + VTableShim(did) => VTableShim(did.try_fold_with(folder)?), ReifyShim(did) => ReifyShim(did.try_fold_with(folder)?), Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?), FnPtrShim(did, ty) => { @@ -954,7 +955,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> { self.substs.visit_with(visitor)?; match self.def { Item(def) => def.visit_with(visitor), - VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { did.visit_with(visitor) } FnPtrShim(did, ty) | CloneShim(did, ty) => { @@ -1122,6 +1123,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::Predicate<'tcx> { visitor.visit_predicate(*self) } + #[inline] fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { self.outer_exclusive_binder() > binder } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4b51daadabf..9f622ad6cd2 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -932,6 +932,10 @@ impl<T> EarlyBinder<T> { let value = f(self.0)?; Ok(EarlyBinder(value)) } + + pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> { + EarlyBinder(value) + } } impl<T> EarlyBinder<Option<T>> { @@ -1703,13 +1707,6 @@ impl<'tcx> Ty<'tcx> { } } - pub fn expect_opaque_type(self) -> ty::OpaqueTypeKey<'tcx> { - match *self.kind() { - Opaque(def_id, substs) => ty::OpaqueTypeKey { def_id, substs }, - _ => bug!("`expect_opaque_type` called on non-opaque type: {}", self), - } - } - pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { match self.kind() { Adt(def, substs) => { diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 3a524d7b0f3..b4fa9837aed 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -11,7 +11,6 @@ use rustc_data_structures::intern::{Interned, WithStableHash}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_serialize::{self, Decodable, Encodable}; -use rustc_span::DUMMY_SP; use smallvec::SmallVec; use core::intrinsics; @@ -525,6 +524,7 @@ struct SubstFolder<'a, 'tcx> { } impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { + #[inline] fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } @@ -540,6 +540,16 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + #[cold] + #[inline(never)] + fn region_param_out_of_range(data: ty::EarlyBoundRegion) -> ! { + bug!( + "Region parameter out of range when substituting in region {} (index={})", + data.name, + data.index + ) + } + // Note: This routine only handles regions that are bound on // type declarations and other outer declarations, not those // bound in *fn types*. Region substitution of the bound @@ -550,14 +560,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { let rk = self.substs.get(data.index as usize).map(|k| k.unpack()); match rk { Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), - _ => { - let msg = format!( - "Region parameter out of range \ - when substituting in region {} (index={})", - data.name, data.index - ); - span_bug!(DUMMY_SP, "{}", msg); - } + _ => region_param_out_of_range(data), } } _ => r, @@ -595,67 +598,80 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { let opt_ty = self.substs.get(p.index as usize).map(|k| k.unpack()); let ty = match opt_ty { Some(GenericArgKind::Type(ty)) => ty, - Some(kind) => { - span_bug!( - DUMMY_SP, - "expected type for `{:?}` ({:?}/{}) but found {:?} \ - when substituting, substs={:?}", - p, - source_ty, - p.index, - kind, - self.substs, - ); - } - None => { - span_bug!( - DUMMY_SP, - "type parameter `{:?}` ({:?}/{}) out of range \ - when substituting, substs={:?}", - p, - source_ty, - p.index, - self.substs, - ); - } + Some(kind) => self.type_param_expected(p, source_ty, kind), + None => self.type_param_out_of_range(p, source_ty), }; self.shift_vars_through_binders(ty) } + #[cold] + #[inline(never)] + fn type_param_expected(&self, p: ty::ParamTy, ty: Ty<'tcx>, kind: GenericArgKind<'tcx>) -> ! { + bug!( + "expected type for `{:?}` ({:?}/{}) but found {:?} when substituting, substs={:?}", + p, + ty, + p.index, + kind, + self.substs, + ) + } + + #[cold] + #[inline(never)] + fn type_param_out_of_range(&self, p: ty::ParamTy, ty: Ty<'tcx>) -> ! { + bug!( + "type parameter `{:?}` ({:?}/{}) out of range when substituting, substs={:?}", + p, + ty, + p.index, + self.substs, + ) + } + fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> { // Look up the const in the substitutions. It really should be in there. let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack()); let ct = match opt_ct { Some(GenericArgKind::Const(ct)) => ct, - Some(kind) => { - span_bug!( - DUMMY_SP, - "expected const for `{:?}` ({:?}/{}) but found {:?} \ - when substituting substs={:?}", - p, - source_ct, - p.index, - kind, - self.substs, - ); - } - None => { - span_bug!( - DUMMY_SP, - "const parameter `{:?}` ({:?}/{}) out of range \ - when substituting substs={:?}", - p, - source_ct, - p.index, - self.substs, - ); - } + Some(kind) => self.const_param_expected(p, source_ct, kind), + None => self.const_param_out_of_range(p, source_ct), }; self.shift_vars_through_binders(ct) } + #[cold] + #[inline(never)] + fn const_param_expected( + &self, + p: ty::ParamConst, + ct: ty::Const<'tcx>, + kind: GenericArgKind<'tcx>, + ) -> ! { + bug!( + "expected const for `{:?}` ({:?}/{}) but found {:?} when substituting substs={:?}", + p, + ct, + p.index, + kind, + self.substs, + ) + } + + #[cold] + #[inline(never)] + fn const_param_out_of_range(&self, p: ty::ParamConst, ct: ty::Const<'tcx>) -> ! { + bug!( + "const parameter `{:?}` ({:?}/{}) out of range when substituting substs={:?}", + p, + ct, + p.index, + self.substs, + ) + } + /// It is sometimes necessary to adjust the De Bruijn indices during substitution. This occurs /// when we are substituting a type with escaping bound vars into a context where we have /// passed through binders. That's quite a mouthful. Let's see an example: diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 826c16dda4a..541dace5cc2 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -74,6 +74,10 @@ impl TraitImpls { pub fn blanket_impls(&self) -> &[DefId] { self.blanket_impls.as_slice() } + + pub fn non_blanket_impls(&self) -> &FxIndexMap<SimplifiedType, Vec<DefId>> { + &self.non_blanket_impls + } } impl<'tcx> TraitDef { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 52da6c3a8c0..4d2f69b23fa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -676,6 +676,10 @@ impl<'tcx> TyCtxt<'tcx> { ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> { ty::EarlyBinder(self.item_bounds(def_id)) } + + pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> { + ty::EarlyBinder(self.const_param_default(def_id)) + } } struct OpaqueTypeExpander<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index a83328c0cab..cb8be51a085 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref pattern, initializer, lint_level, + else_block, } => { let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let scope = (*init_scope, source_info); this.in_scope(scope, *lint_level, |this| { - this.declare_bindings( - visibility_scope, - remainder_span, - pattern, - ArmHasGuard(false), - Some((None, initializer_span)), - ); - this.expr_into_pattern(block, pattern.clone(), init) + if let Some(else_block) = else_block { + this.ast_let_else( + block, + init, + initializer_span, + else_block, + visibility_scope, + remainder_span, + pattern, + ) + } else { + this.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern + } }) - } + }, ) - ); + ) } else { let scope = (*init_scope, source_info); unpack!(this.in_scope(scope, *lint_level, |this| { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 1628f1a4b85..7067a48b783 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // those N possible outcomes, create a (initially empty) // vector of candidates. Those are the candidates that still // apply if the test has that particular outcome. - debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); + debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair); let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![]; target_candidates.resize_with(test.targets(), Default::default); @@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // at least the first candidate ought to be tested assert!(total_candidate_count > candidates.len()); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); + debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("test_candidates: untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("declare_binding: vars={:?}", locals); self.var_indices.insert(var_id, locals); } + + pub(crate) fn ast_let_else( + &mut self, + mut block: BasicBlock, + init: &Expr<'tcx>, + initializer_span: Span, + else_block: &Block, + visibility_scope: Option<SourceScope>, + remainder_span: Span, + pattern: &Pat<'tcx>, + ) -> BlockAnd<()> { + let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); + let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; + let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); + self.declare_bindings( + visibility_scope, + remainder_span, + pattern, + ArmHasGuard(false), + Some((None, initializer_span)), + ); + let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); + let fake_borrow_temps = self.lower_match_tree( + block, + initializer_span, + pattern.span, + false, + &mut [&mut candidate, &mut wildcard], + ); + // This block is for the matching case + let matching = self.bind_pattern( + self.source_info(pattern.span), + candidate, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This block is for the failure case + let failure = self.bind_pattern( + self.source_info(else_block.span), + wildcard, + None, + &fake_borrow_temps, + initializer_span, + None, + None, + None, + ); + // This place is not really used because this destination place + // should never be used to take values at the end of the failure + // block. + let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() }; + let failure_block; + unpack!( + failure_block = self.ast_block( + dummy_place, + failure, + else_block, + self.source_info(else_block.span), + ) + ); + self.cfg.terminate( + failure_block, + self.source_info(else_block.span), + TerminatorKind::Unreachable, + ); + matching.unit() + } } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index e6d42af9817..7ae26cccb38 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -68,9 +68,10 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ // Figure out what primary body this item has. let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) { - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { fn_decl, body, .. }, .. }) => { - (*body, fn_decl.output.span(), None) - } + Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl, body, .. }), + .. + }) => (*body, fn_decl.output.span(), None), Node::Item(hir::Item { kind: hir::ItemKind::Fn(hir::FnSig { decl, .. }, _, body_id), span, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 8585199faaf..1f0d0ce04aa 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -431,16 +431,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { let lhs = &self.thir[lhs]; if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() { if let Some((assigned_ty, assignment_span)) = self.assignment_info { - // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping. - if !(assigned_ty - .ty_adt_def() - .map_or(false, |adt| adt.is_manually_drop()) - || assigned_ty - .is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env)) - { - self.requires_unsafe(assignment_span, AssignToDroppingUnionField); - } else { - // write to non-drop union field, safe + if assigned_ty.needs_drop(self.tcx, self.param_env) { + // This would be unsafe, but should be outright impossible since we reject such unions. + self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}")); } } else { self.requires_unsafe(expr.span, AccessToUnionField); @@ -537,7 +530,6 @@ enum UnsafeOpKind { UseOfMutableStatic, UseOfExternStatic, DerefOfRawPointer, - AssignToDroppingUnionField, AccessToUnionField, MutationOfLayoutConstrainedField, BorrowOfLayoutConstrainedField, @@ -555,7 +547,6 @@ impl UnsafeOpKind { UseOfMutableStatic => "use of mutable static", UseOfExternStatic => "use of extern static", DerefOfRawPointer => "dereference of raw pointer", - AssignToDroppingUnionField => "assignment to union field that might need dropping", AccessToUnionField => "access to union field", MutationOfLayoutConstrainedField => "mutation of layout constrained field", BorrowOfLayoutConstrainedField => { @@ -600,11 +591,6 @@ impl UnsafeOpKind { "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \ and cause data races: all of these are undefined behavior", ), - AssignToDroppingUnionField => ( - Cow::Borrowed(self.simple_description()), - "the previous content of the field will be dropped, which causes undefined \ - behavior if the field was not properly initialized", - ), AccessToUnionField => ( Cow::Borrowed(self.simple_description()), "the field may not be properly initialized: using uninitialized data will cause \ @@ -632,7 +618,7 @@ impl UnsafeOpKind { pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) { // THIR unsafeck is gated under `-Z thir-unsafeck` - if !tcx.sess.opts.debugging_opts.thir_unsafeck { + if !tcx.sess.opts.unstable_opts.thir_unsafeck { return; } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 11cd2a9aa4d..a7d8431b6c7 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -5,7 +5,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(once_cell)] diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 59750d5d0b8..dccaa61ed89 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> { )), }; + let else_block = local.els.map(|els| self.mirror_block(els)); + let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); @@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> { }, pattern, initializer: local.init.map(|init| self.mirror_expr(init)), + else_block, lint_level: LintLevel::Explicit(local.hir_id), }, opt_destruction_scope: opt_dxn_ext, 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 75fd156ebfd..3435f127c72 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; +use rustc_span::{BytePos, Span}; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -77,6 +77,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) { intravisit::walk_local(self, loc); + let els = loc.els; + if let Some(init) = loc.init && els.is_some() { + self.check_let(&loc.pat, init, loc.span); + } let (msg, sp) = match loc.source { hir::LocalSource::Normal => ("local binding", Some(loc.span)), @@ -84,7 +88,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { hir::LocalSource::AwaitDesugar => ("`await` future binding", None), hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; - self.check_irrefutable(&loc.pat, msg, sp); + if els.is_none() { + self.check_irrefutable(&loc.pat, msg, sp); + } } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { @@ -945,7 +951,7 @@ fn adt_defined_here<'p, 'tcx>( let mut span: MultiSpan = if spans.is_empty() { def_span.into() } else { spans.clone().into() }; - span.push_span_label(def_span, String::new()); + span.push_span_label(def_span, ""); for pat in spans { span.push_span_label(pat, "not covered"); } @@ -1125,17 +1131,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L }) if Some(*hir_id) == pat_id => { return LetSource::IfLetGuard; } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => { - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind { - return LetSource::LetElse(expn_data.call_site); - } - } _ => {} } let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); + if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), span, .. }) = + parent_parent_node + { + return LetSource::LetElse(*span); + } let parent_parent_parent = hir.get_parent_node(parent_parent); let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index e32e0b11ba4..d6dd0f01794 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map( - |non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind { - traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTyKind::Dynamic => { - "trait objects cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Opaque => { - "opaque types cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Closure => { - "closures cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Generator => { - "generators cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Float => { - "floating-point numbers cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTyKind::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTyKind::Foreign => { - bug!("use of a value of a foreign type inside a pattern") - } - }) - }, - ) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { + with_no_trimmed_paths!(match non_sm_ty.kind() { + ty::Adt(adt, _) => self.adt_derive_msg(*adt), + ty::Dynamic(..) => { + "trait objects cannot be used in patterns".to_string() + } + ty::Opaque(..) => { + "opaque types cannot be used in patterns".to_string() + } + ty::Closure(..) => { + "closures cannot be used in patterns".to_string() + } + ty::Generator(..) | ty::GeneratorWitness(..) => { + "generators cannot be used in patterns".to_string() + } + ty::Float(..) => { + "floating-point numbers cannot be used in patterns".to_string() + } + ty::FnPtr(..) => { + "function pointers cannot be used in patterns".to_string() + } + ty::RawPtr(..) => { + "raw pointers cannot be used in patterns".to_string() + } + _ => { + bug!("use of a value of `{non_sm_ty}` inside a pattern") + } + }) + }) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 180376d648a..f374658ceb6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -289,7 +289,7 @@ where io::BufWriter::new(fs::File::create(&path)?) } - None if tcx.sess.opts.debugging_opts.dump_mir_dataflow + None if tcx.sess.opts.unstable_opts.dump_mir_dataflow && dump_enabled(tcx, A::NAME, def_id) => { create_dump_file( @@ -314,8 +314,8 @@ where let graphviz = graphviz::Formatter::new(body, results, style); let mut render_opts = - vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())]; - if tcx.sess.opts.debugging_opts.graphviz_dark_mode { + vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())]; + if tcx.sess.opts.unstable_opts.graphviz_dark_mode { render_opts.push(dot::RenderOption::DarkTheme); } dot::render_opts(&graphviz, &mut buf, &render_opts)?; diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index af6a1bb1545..fd1e492779f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -317,7 +317,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { Self::update_bits(trans, path, s) }); - if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { return; } @@ -340,7 +340,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { Self::update_bits(trans, path, s) }); - if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { return; } @@ -379,7 +379,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects<G>, ) { - if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { return; } @@ -495,7 +495,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects<G>, ) { - if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration { return; } diff --git a/compiler/rustc_mir_dataflow/src/storage.rs b/compiler/rustc_mir_dataflow/src/storage.rs index 566c9d2d505..c909648ea01 100644 --- a/compiler/rustc_mir_dataflow/src/storage.rs +++ b/compiler/rustc_mir_dataflow/src/storage.rs @@ -4,9 +4,6 @@ use rustc_middle::mir::{self, Local}; /// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. /// /// These locals have fixed storage for the duration of the body. -// -// FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it -// as a field in the `LocalDecl` for each `Local`. pub fn always_storage_live_locals(body: &mir::Body<'_>) -> BitSet<Local> { let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); diff --git a/compiler/rustc_mir_dataflow/src/un_derefer.rs b/compiler/rustc_mir_dataflow/src/un_derefer.rs index 04f626b49e8..7e6e25cc603 100644 --- a/compiler/rustc_mir_dataflow/src/un_derefer.rs +++ b/compiler/rustc_mir_dataflow/src/un_derefer.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 2c8389e532d..2502e8b603c 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -75,7 +75,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls { layout::fn_can_unwind(tcx, fn_def_id, sig.abi()) } TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => { - tcx.sess.opts.debugging_opts.panic_in_drop == PanicStrategy::Unwind + tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Unwind && layout::fn_can_unwind(tcx, None, Abi::Rust) } TerminatorKind::Assert { .. } | TerminatorKind::FalseUnwind { .. } => { diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 13ec1e7bb72..9c5896c4e4a 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -15,22 +15,9 @@ pub struct AddRetag; /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic /// copies. Data races are UB.) fn is_stable(place: PlaceRef<'_>) -> bool { - place.projection.iter().all(|elem| { - match elem { - // Which place this evaluates to can change with any memory write, - // so cannot assume this to be stable. - ProjectionElem::Deref => false, - // Array indices are interesting, but MIR building generates a *fresh* - // temporary for every array access, so the index cannot be changed as - // a side-effect. - ProjectionElem::Index { .. } | - // The rest is completely boring, they just offset by a constant. - ProjectionElem::Field { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Subslice { .. } | - ProjectionElem::Downcast { .. } => true, - } - }) + // Which place this evaluates to can change with any memory write, + // so cannot assume deref to be stable. + !place.has_deref() } /// Determine whether this type may contain a reference (or box), and thus needs retagging. @@ -72,7 +59,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b impl<'tcx> MirPass<'tcx> for AddRetag { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.debugging_opts.mir_emit_retag + sess.opts.unstable_opts.mir_emit_retag } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -91,11 +78,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag { }; let place_base_raw = |place: &Place<'tcx>| { // If this is a `Deref`, get the type of what we are deref'ing. - let deref_base = - place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref)); - if let Some(deref_base) = deref_base { - let base_proj = &place.projection[..deref_base]; - let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty; + if place.has_deref() { + let ty = &local_decls[place.local].ty; ty.is_unsafe_ptr() } else { // Not a deref, and thus not raw. diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index 2eb38941f1a..3b7ba3f9a67 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -36,13 +36,16 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| { // FIXME: when we make this a hard error, this should have its // own error code. - let message = if tcx.generics_of(def_id).own_requires_monomorphization() { - "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ - type or const parameters (error E0133)" + let extra = if tcx.generics_of(def_id).own_requires_monomorphization() { + "with type or const parameters" } else { - "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ - does not derive Copy (error E0133)" + "that does not derive `Copy`" }; + let message = format!( + "`{}` can't be derived on this `#[repr(packed)]` struct {}", + tcx.item_name(tcx.trait_id_of_impl(def_id.to_def_id()).expect("derived trait name")), + extra + ); lint.build(message).emit(); }); } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 1f73b7da815..a2ad96cfc16 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -219,22 +219,12 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { // We have to check the actual type of the assignment, as that determines if the // old value is being dropped. let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; - // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping. - let manually_drop = assigned_ty - .ty_adt_def() - .map_or(false, |adt_def| adt_def.is_manually_drop()); - let nodrop = manually_drop - || assigned_ty.is_copy_modulo_regions( - self.tcx.at(self.source_info.span), - self.param_env, + if assigned_ty.needs_drop(self.tcx, self.param_env) { + // This would be unsafe, but should be outright impossible since we reject such unions. + self.tcx.sess.delay_span_bug( + self.source_info.span, + format!("union fields that need dropping should be impossible: {assigned_ty}") ); - if !nodrop { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::AssignToDroppingUnionField, - ); - } else { - // write to non-drop union field, safe } } else { self.require_unsafe( diff --git a/compiler/rustc_mir_transform/src/const_debuginfo.rs b/compiler/rustc_mir_transform/src/const_debuginfo.rs index 48aea61b191..6f0ae4f07ab 100644 --- a/compiler/rustc_mir_transform/src/const_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/const_debuginfo.rs @@ -16,7 +16,7 @@ pub struct ConstDebugInfo; impl<'tcx> MirPass<'tcx> for ConstDebugInfo { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.debugging_opts.unsound_mir_opts && sess.mir_opt_level() > 0 + sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() > 0 } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index acd9e605353..85ad6b8f2fe 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -155,18 +155,18 @@ impl<'tcx> MirPass<'tcx> for ConstProp { } } -struct ConstPropMachine<'mir, 'tcx> { +pub struct ConstPropMachine<'mir, 'tcx> { /// The virtual call stack. stack: Vec<Frame<'mir, 'tcx>>, /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. - written_only_inside_own_block_locals: FxHashSet<Local>, + pub written_only_inside_own_block_locals: FxHashSet<Local>, /// Locals that need to be cleared after every block terminates. - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, + pub only_propagate_inside_block_locals: BitSet<Local>, + pub can_const_prop: IndexVec<Local, ConstPropMode>, } impl ConstPropMachine<'_, '_> { - fn new( + pub fn new( only_propagate_inside_block_locals: BitSet<Local>, can_const_prop: IndexVec<Local, ConstPropMode>, ) -> Self { @@ -234,9 +234,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> } fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, + frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, local: Local, - ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a interpret::Operand<Self::Provenance>> { let l = &frame.locals[local]; if matches!( @@ -255,7 +255,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> ecx: &'a mut InterpCx<'mir, 'tcx, Self>, frame: usize, local: Local, - ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { + ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> { if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") } @@ -274,7 +274,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> _tcx: TyCtxt<'tcx>, _machine: &Self, _alloc_id: AllocId, - alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>, + alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>, _static_def_id: Option<DefId>, is_write: bool, ) -> InterpResult<'tcx> { @@ -309,14 +309,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> #[inline(always)] fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { &ecx.machine.stack } #[inline(always)] fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { + ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { &mut ecx.machine.stack } } @@ -516,7 +516,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); // Check for exceeding shifts *even if* we cannot evaluate the LHS. if op == BinOp::Shr || op == BinOp::Shl { - let r = r?; + let r = r.clone()?; // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(self.local_decls, self.tcx); @@ -816,7 +816,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// The mode that `ConstProp` is allowed to run in for a given `Local`. #[derive(Clone, Copy, Debug, PartialEq)] -enum ConstPropMode { +pub enum ConstPropMode { /// The `Local` can be propagated into and reads of this `Local` can also be propagated. FullConstProp, /// The `Local` can only be propagated into and from its own block. @@ -828,7 +828,7 @@ enum ConstPropMode { NoPropagation, } -struct CanConstProp { +pub struct CanConstProp { can_const_prop: IndexVec<Local, ConstPropMode>, // False at the beginning. Once set, no more assignments are allowed to that local. found_assignment: BitSet<Local>, @@ -838,7 +838,7 @@ struct CanConstProp { impl CanConstProp { /// Returns true if `local` can be propagated - fn check<'tcx>( + pub fn check<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 49db140c474..3ae6a88a140 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -1,19 +1,24 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::cell::Cell; - -use rustc_ast::Mutability; -use rustc_data_structures::fx::FxHashSet; +use crate::const_prop::CanConstProp; +use crate::const_prop::ConstPropMachine; +use crate::const_prop::ConstPropMode; +use crate::MirLint; +use rustc_const_eval::const_eval::ConstEvalErr; +use rustc_const_eval::interpret::{ + self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, + ScalarMaybeUninit, StackPopCleanup, +}; use rustc_hir::def::DefKind; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind, - Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, - StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, + AssertKind, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, Location, Operand, Place, + Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, + TerminatorKind, UnOp, RETURN_PLACE, }; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -22,42 +27,15 @@ use rustc_middle::ty::{ TypeVisitable, }; use rustc_session::lint; -use rustc_span::{def_id::DefId, Span}; +use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use rustc_target::spec::abi::Abi as CallAbi; use rustc_trait_selection::traits; - -use crate::MirLint; -use rustc_const_eval::const_eval::ConstEvalErr; -use rustc_const_eval::interpret::{ - self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, - StackPopCleanup, StackPopUnwind, -}; +use std::cell::Cell; /// The maximum number of bytes that we'll allocate space for a local or the return value. /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just /// Severely regress performance. const MAX_ALLOC_LIMIT: u64 = 1024; - -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -macro_rules! throw_machine_stop_str { - ($($tt:tt)*) => {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - impl rustc_middle::mir::interpret::MachineStopType for Zst {} - throw_machine_stop!(Zst) - }}; -} - pub struct ConstProp; impl<'tcx> MirLint<'tcx> for ConstProp { @@ -151,172 +129,6 @@ impl<'tcx> MirLint<'tcx> for ConstProp { } } -struct ConstPropMachine<'mir, 'tcx> { - /// The virtual call stack. - stack: Vec<Frame<'mir, 'tcx>>, - /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. - written_only_inside_own_block_locals: FxHashSet<Local>, - /// Locals that need to be cleared after every block terminates. - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, -} - -impl ConstPropMachine<'_, '_> { - fn new( - only_propagate_inside_block_locals: BitSet<Local>, - can_const_prop: IndexVec<Local, ConstPropMode>, - ) -> Self { - Self { - stack: Vec::new(), - written_only_inside_own_block_locals: Default::default(), - only_propagate_inside_block_locals, - can_const_prop, - } - } -} - -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> { - compile_time_machine!(<'mir, 'tcx>); - const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`) - - type MemoryKind = !; - - fn load_mir( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _instance: ty::InstanceDef<'tcx>, - ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { - throw_machine_stop_str!("calling functions isn't supported in ConstProp") - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: CallAbi, - _args: &[OpTy<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: StackPopUnwind, - ) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - Ok(None) - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[OpTy<'tcx>], - _destination: &PlaceTy<'tcx>, - _target: Option<BasicBlock>, - _unwind: StackPopUnwind, - ) -> InterpResult<'tcx> { - throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: Option<rustc_middle::mir::BasicBlock>, - ) -> InterpResult<'tcx> { - bug!("panics terminators are not evaluated in ConstProp") - } - - fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _bin_op: BinOp, - _left: &ImmTy<'tcx>, - _right: &ImmTy<'tcx>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { - // We can't do this because aliasing of memory can differ between const eval and llvm - throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") - } - - fn access_local<'a>( - frame: &'a Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>, - local: Local, - ) -> InterpResult<'tcx, &'a interpret::Operand<Self::PointerTag>> { - let l = &frame.locals[local]; - - if matches!( - l.value, - LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)) - ) { - // For us "uninit" means "we don't know its value, might be initiailized or not". - // So stop here. - throw_machine_stop_str!("tried to access a local with unknown value") - } - - l.access() - } - - fn access_local_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - frame: usize, - local: Local, - ) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::PointerTag>> { - if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { - throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") - } - if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { - trace!( - "mutating local {:?} which is restricted to its block. \ - Will remove it from const-prop after block is finished.", - local - ); - ecx.machine.written_only_inside_own_block_locals.insert(local); - } - ecx.machine.stack[frame].locals[local].access_mut() - } - - fn before_access_global( - _tcx: TyCtxt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>, - _static_def_id: Option<DefId>, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability == Mutability::Mut { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - #[inline(always)] - fn expose_ptr( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _ptr: Pointer<AllocId>, - ) -> InterpResult<'tcx> { - throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") - } - - #[inline(always)] - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { - Ok(frame) - } - - #[inline(always)] - fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { - &ecx.machine.stack - } - - #[inline(always)] - fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> { - &mut ecx.machine.stack - } -} - /// Finds optimization opportunities on the MIR. struct ConstPropagator<'mir, 'tcx> { ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, @@ -584,7 +396,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { }); // Check for exceeding shifts *even if* we cannot evaluate the LHS. if op == BinOp::Shr || op == BinOp::Shl { - let r = r?; + let r = r.clone()?; // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(self.local_decls, self.tcx); @@ -616,10 +428,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - if let (Some(l), Some(r)) = (&l, &r) { + if let (Some(l), Some(r)) = (l, r) { // The remaining operators are handled through `overflowing_binary_op`. if self.use_ecx(source_info, |this| { - let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, &l, &r)?; Ok(overflow) })? { self.report_assert_as_lint( @@ -711,139 +523,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } -/// The mode that `ConstProp` is allowed to run in for a given `Local`. -#[derive(Clone, Copy, Debug, PartialEq)] -enum ConstPropMode { - /// The `Local` can be propagated into and reads of this `Local` can also be propagated. - FullConstProp, - /// The `Local` can only be propagated into and from its own block. - OnlyInsideOwnBlock, - /// The `Local` can be propagated into but reads cannot be propagated. - OnlyPropagateInto, - /// The `Local` cannot be part of propagation at all. Any statement - /// referencing it either for reading or writing will not get propagated. - NoPropagation, -} - -struct CanConstProp { - can_const_prop: IndexVec<Local, ConstPropMode>, - // False at the beginning. Once set, no more assignments are allowed to that local. - found_assignment: BitSet<Local>, - // Cache of locals' information - local_kinds: IndexVec<Local, LocalKind>, -} - -impl CanConstProp { - /// Returns true if `local` can be propagated - fn check<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - body: &Body<'tcx>, - ) -> IndexVec<Local, ConstPropMode> { - let mut cpv = CanConstProp { - can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: BitSet::new_empty(body.local_decls.len()), - local_kinds: IndexVec::from_fn_n( - |local| body.local_kind(local), - body.local_decls.len(), - ), - }; - for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { - let ty = body.local_decls[local].ty; - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} - // Either the layout fails to compute, then we can't use this local anyway - // or the local is too large, then we don't want to. - _ => { - *val = ConstPropMode::NoPropagation; - continue; - } - } - // Cannot use args at all - // Cannot use locals because if x < y { y - x } else { x - y } would - // lint for x != y - // FIXME(oli-obk): lint variables until they are used in a condition - // FIXME(oli-obk): lint if return value is constant - if cpv.local_kinds[local] == LocalKind::Arg { - *val = ConstPropMode::OnlyPropagateInto; - trace!( - "local {:?} can't be const propagated because it's a function argument", - local - ); - } else if cpv.local_kinds[local] == LocalKind::Var { - *val = ConstPropMode::OnlyInsideOwnBlock; - trace!( - "local {:?} will only be propagated inside its block, because it's a user variable", - local - ); - } - } - cpv.visit_body(&body); - cpv.can_const_prop - } -} - -impl Visitor<'_> for CanConstProp { - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - use rustc_middle::mir::visit::PlaceContext::*; - match context { - // Projections are fine, because `&mut foo.x` will be caught by - // `MutatingUseContext::Borrow` elsewhere. - MutatingUse(MutatingUseContext::Projection) - // These are just stores, where the storing is not propagatable, but there may be later - // mutations of the same local via `Store` - | MutatingUse(MutatingUseContext::Call) - | MutatingUse(MutatingUseContext::AsmOutput) - | MutatingUse(MutatingUseContext::Deinit) - // Actual store that can possibly even propagate a value - | MutatingUse(MutatingUseContext::SetDiscriminant) - | MutatingUse(MutatingUseContext::Store) => { - if !self.found_assignment.insert(local) { - match &mut self.can_const_prop[local] { - // If the local can only get propagated in its own block, then we don't have - // to worry about multiple assignments, as we'll nuke the const state at the - // end of the block anyway, and inside the block we overwrite previous - // states as applicable. - ConstPropMode::OnlyInsideOwnBlock => {} - ConstPropMode::NoPropagation => {} - ConstPropMode::OnlyPropagateInto => {} - other @ ConstPropMode::FullConstProp => { - trace!( - "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", - local, other, - ); - *other = ConstPropMode::OnlyInsideOwnBlock; - } - } - } - } - // Reading constants is allowed an arbitrary number of times - NonMutatingUse(NonMutatingUseContext::Copy) - | NonMutatingUse(NonMutatingUseContext::Move) - | NonMutatingUse(NonMutatingUseContext::Inspect) - | NonMutatingUse(NonMutatingUseContext::Projection) - | NonUse(_) => {} - - // These could be propagated with a smarter analysis or just some careful thinking about - // whether they'd be fine right now. - MutatingUse(MutatingUseContext::Yield) - | MutatingUse(MutatingUseContext::Drop) - | MutatingUse(MutatingUseContext::Retag) - // These can't ever be propagated under any scheme, as we can't reason about indirect - // mutation. - | NonMutatingUse(NonMutatingUseContext::SharedBorrow) - | NonMutatingUse(NonMutatingUseContext::ShallowBorrow) - | NonMutatingUse(NonMutatingUseContext::UniqueBorrow) - | NonMutatingUse(NonMutatingUseContext::AddressOf) - | MutatingUse(MutatingUseContext::Borrow) - | MutatingUse(MutatingUseContext::AddressOf) => { - trace!("local {:?} can't be propagaged because it's used: {:?}", local, context); - self.can_const_prop[local] = ConstPropMode::NoPropagation; - } - } - } -} - impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_body(&mut self, body: &Body<'tcx>) { for (bb, data) in body.basic_blocks().iter_enumerated() { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 88ad5b7ef14..2619626a567 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -160,8 +160,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let mut debug_used_expressions = debug::UsedExpressions::new(); let dump_mir = dump_enabled(tcx, self.pass_name, def_id); - let dump_graphviz = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_graphviz; - let dump_spanview = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_spanview.is_some(); + let dump_graphviz = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_graphviz; + let dump_spanview = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_spanview.is_some(); if dump_graphviz { graphviz_data.enable(); diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 84c7aada5e5..33572068f5c 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -122,7 +122,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { // // Only run at mir-opt-level=3 or higher for now (we don't fix up debuginfo and remove // storage statements at the moment). - sess.opts.debugging_opts.unsound_mir_opts && sess.mir_opt_level() >= 3 + sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() >= 3 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 33f201cbd28..dba42f7aff0 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -95,7 +95,7 @@ pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 3 && sess.opts.debugging_opts.unsound_mir_opts + sess.mir_opt_level() >= 3 && sess.opts.unstable_opts.unsound_mir_opts } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 9fdea835967..91ecf387922 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -957,7 +957,7 @@ fn create_generator_drop_shim<'tcx>( tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }), source_info, ); - if tcx.sess.opts.debugging_opts.mir_emit_retag { + if tcx.sess.opts.unstable_opts.mir_emit_retag { // Alias tracking must know we changed the type body.basic_blocks_mut()[START_BLOCK].statements.insert( 0, @@ -1386,7 +1386,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { sanitize_witness(tcx, body, interior, upvars, &liveness_info.saved_locals); - if tcx.sess.opts.debugging_opts.validate_mir { + if tcx.sess.opts.unstable_opts.validate_mir { let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias { assigned_local: None, saved_locals: &liveness_info.saved_locals, diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 8f049a182ee..1e46b0a0e81 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -40,7 +40,7 @@ struct CallSite<'tcx> { impl<'tcx> MirPass<'tcx> for Inline { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - if let Some(enabled) = sess.opts.debugging_opts.inline_mir { + if let Some(enabled) = sess.opts.unstable_opts.inline_mir { return enabled; } @@ -246,7 +246,7 @@ impl<'tcx> Inliner<'tcx> { // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. - InstanceDef::VtableShim(_) + InstanceDef::VTableShim(_) | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } @@ -395,9 +395,9 @@ impl<'tcx> Inliner<'tcx> { let tcx = self.tcx; let mut threshold = if callee_attrs.requests_inline() { - self.tcx.sess.opts.debugging_opts.inline_mir_hint_threshold.unwrap_or(100) + self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) } else { - self.tcx.sess.opts.debugging_opts.inline_mir_threshold.unwrap_or(50) + self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) }; // Give a bonus functions with a small number of blocks, diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index a3a35f95071..7810218fd67 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -79,7 +79,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( // These have MIR and if that MIR is inlined, substituted and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way - InstanceDef::VtableShim(_) + InstanceDef::VTableShim(_) | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9e5c22308e7..40dc9fe9a05 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,6 +1,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -217,7 +217,7 @@ fn mir_const<'tcx>( } // Unsafety check uses the raw mir, so make sure it is run. - if !tcx.sess.opts.debugging_opts.thir_unsafeck { + if !tcx.sess.opts.unstable_opts.thir_unsafeck { if let Some(param_did) = def.const_param_did { tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did)); } else { diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 47848cfa497..2f02d00ec9f 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls; impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() > 0 + sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 2380391d09a..e27d4ab1688 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -76,8 +76,8 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn let start_phase = body.phase; let mut cnt = 0; - let validate = tcx.sess.opts.debugging_opts.validate_mir; - let overridden_passes = &tcx.sess.opts.debugging_opts.mir_enable_passes; + let validate = tcx.sess.opts.unstable_opts.validate_mir; + let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); if validate { diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index 4c33f839f9b..96b715402e7 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>( let field_needs_drop_and_init = |(f, f_ty, mpi)| { let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f)); let Some(mpi) = child else { - return f_ty.needs_drop(tcx, param_env); + return Ty::needs_drop(f_ty, tcx, param_env); }; is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi) diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 8ea550fa123..4919ad40098 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -9,7 +9,7 @@ pub struct RevealAll; impl<'tcx> MirPass<'tcx> for RevealAll { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) + sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 3be1783ae33..3620e94bec7 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -32,7 +32,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::VtableShim(def_id) => { + ty::InstanceDef::VTableShim(def_id) => { build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id)) } ty::InstanceDef::FnPtrShim(def_id, ty) => { @@ -113,7 +113,7 @@ enum Adjustment { /// We get passed `&[mut] self` and call the target with `*self`. /// /// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it - /// (for `VtableShim`, which effectively is passed `&own Self`). + /// (for `VTableShim`, which effectively is passed `&own Self`). Deref, /// We get passed `self: Self` and call the target with `&mut self`. @@ -176,7 +176,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) if ty.is_some() { // The first argument (index 0), but add 1 for the return value. let dropee_ptr = Place::from(Local::new(1 + 0)); - if tcx.sess.opts.debugging_opts.mir_emit_retag { + if tcx.sess.opts.unstable_opts.mir_emit_retag { // Function arguments should be retagged, and we make this one raw. body.basic_blocks_mut()[START_BLOCK].statements.insert( 0, @@ -537,13 +537,12 @@ fn build_call_shim<'tcx>( }; let def_id = instance.def_id(); - let sig = tcx.fn_sig(def_id); - let mut sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.bound_fn_sig(def_id); + let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig)); assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body()); - if let Some(sig_substs) = sig_substs { - sig = EarlyBinder(sig).subst(tcx, sig_substs); - } + let mut sig = + if let Some(sig_substs) = sig_substs { sig.subst(tcx, sig_substs) } else { sig.0 }; if let CallKind::Indirect(fnty) = call_kind { // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This @@ -570,7 +569,7 @@ fn build_call_shim<'tcx>( // FIXME(eddyb) avoid having this snippet both here and in // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?). - if let ty::InstanceDef::VtableShim(..) = instance { + if let ty::InstanceDef::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); let self_arg = &mut inputs_and_output[0]; diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 980af984362..180f4c7dcd6 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -28,6 +28,7 @@ //! return. use crate::MirPass; +use rustc_data_structures::fx::FxHashSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -267,7 +268,8 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { return; } - let basic_blocks = body.basic_blocks_mut(); + let basic_blocks = body.basic_blocks.as_mut(); + let source_scopes = &body.source_scopes; let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); let mut used_blocks = 0; for alive_index in reachable.iter() { @@ -282,7 +284,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } if tcx.sess.instrument_coverage() { - save_unreachable_coverage(basic_blocks, used_blocks); + save_unreachable_coverage(basic_blocks, source_scopes, used_blocks); } basic_blocks.raw.truncate(used_blocks); @@ -311,56 +313,72 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// `Unreachable` coverage statements. These are non-executable statements whose /// code regions are still recorded in the coverage map, representing regions /// with `0` executions. +/// +/// If there are no live `Counter` `Coverage` statements remaining, we remove +/// `Coverage` statements along with the dead blocks. Since at least one +/// counter per function is required by LLVM (and necessary, to add the +/// `function_hash` to the counter's call to the LLVM intrinsic +/// `instrprof.increment()`). +/// +/// The `generator::StateTransform` MIR pass and MIR inlining can create +/// atypical conditions, where all live `Counter`s are dropped from the MIR. +/// +/// With MIR inlining we can have coverage counters belonging to different +/// instances in a single body, so the strategy described above is applied to +/// coverage counters from each instance individually. fn save_unreachable_coverage( basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>, + source_scopes: &IndexVec<SourceScope, SourceScopeData<'_>>, first_dead_block: usize, ) { - let has_live_counters = basic_blocks.raw[0..first_dead_block].iter().any(|live_block| { - live_block.statements.iter().any(|statement| { - if let StatementKind::Coverage(coverage) = &statement.kind { - matches!(coverage.kind, CoverageKind::Counter { .. }) - } else { - false + // Identify instances that still have some live coverage counters left. + let mut live = FxHashSet::default(); + for basic_block in &basic_blocks.raw[0..first_dead_block] { + for statement in &basic_block.statements { + let StatementKind::Coverage(coverage) = &statement.kind else { continue }; + let CoverageKind::Counter { .. } = coverage.kind else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + live.insert(instance); + } + } + + for block in &mut basic_blocks.raw[..first_dead_block] { + for statement in &mut block.statements { + let StatementKind::Coverage(_) = &statement.kind else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + if !live.contains(&instance) { + statement.make_nop(); } - }) - }); - if !has_live_counters { - // If there are no live `Counter` `Coverage` statements anymore, don't - // move dead coverage to the `START_BLOCK`. Just allow the dead - // `Coverage` statements to be dropped with the dead blocks. - // - // The `generator::StateTransform` MIR pass can create atypical - // conditions, where all live `Counter`s are dropped from the MIR. - // - // At least one Counter per function is required by LLVM (and necessary, - // to add the `function_hash` to the counter's call to the LLVM - // intrinsic `instrprof.increment()`). + } + } + + if live.is_empty() { return; } - // Retain coverage info for dead blocks, so coverage reports will still - // report `0` executions for the uncovered code regions. - let mut dropped_coverage = Vec::new(); - for dead_block in basic_blocks.raw[first_dead_block..].iter() { - for statement in dead_block.statements.iter() { - if let StatementKind::Coverage(coverage) = &statement.kind { - if let Some(code_region) = &coverage.code_region { - dropped_coverage.push((statement.source_info, code_region.clone())); - } + // Retain coverage for instances that still have some live counters left. + let mut retained_coverage = Vec::new(); + for dead_block in &basic_blocks.raw[first_dead_block..] { + for statement in &dead_block.statements { + let StatementKind::Coverage(coverage) = &statement.kind else { continue }; + let Some(code_region) = &coverage.code_region else { continue }; + let instance = statement.source_info.scope.inlined_instance(source_scopes); + if live.contains(&instance) { + retained_coverage.push((statement.source_info, code_region.clone())); } } } let start_block = &mut basic_blocks[START_BLOCK]; - for (source_info, code_region) in dropped_coverage { - start_block.statements.push(Statement { + start_block.statements.extend(retained_coverage.into_iter().map( + |(source_info, code_region)| Statement { source_info, kind: StatementKind::Coverage(Box::new(Coverage { kind: CoverageKind::Unreachable, code_region: Some(code_region), })), - }) - } + }, + )); } pub struct SimplifyLocals; diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index fca9f7eeb24..d52f1261b23 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -378,7 +378,7 @@ fn optimization_applies<'tcx>( impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // FIXME(77359): This optimization can result in unsoundness. - if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + if !tcx.sess.opts.unstable_opts.unsound_mir_opts { return; } @@ -551,7 +551,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // This optimization is disabled by default for now due to // soundness concerns; see issue #89485 and PR #89489. - if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + if !tcx.sess.opts.unstable_opts.unsound_mir_opts { return; } diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs index bd196f11879..30be64f5b2f 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs @@ -1,7 +1,7 @@ //! A pass that eliminates branches on uninhabited enum variants. use crate::MirPass; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{ BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator, TerminatorKind, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b3ac0f4cbea..68b65658c72 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -25,7 +25,7 @@ //! codegen unit: //! //! - Constants -//! - Vtables +//! - VTables //! - Object Shims //! //! @@ -180,7 +180,7 @@ //! regardless of whether it is actually needed or not. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator}; +use rustc_data_structures::sync::{par_for_each_in, MTLock, MTRef}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; @@ -346,7 +346,7 @@ pub fn collect_crate_mono_items( let inlining_map: MTRef<'_, _> = &mut inlining_map; tcx.sess.time("monomorphization_collector_graph_walk", || { - par_iter(roots).for_each(|root| { + par_for_each_in(roots, |root| { let mut recursion_depths = DefIdMap::default(); collect_items_rec( tcx, @@ -992,7 +992,7 @@ fn visit_instance_use<'tcx>( } } ty::InstanceDef::DropGlue(_, Some(_)) - | ty::InstanceDef::VtableShim(..) + | ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) @@ -1141,8 +1141,7 @@ fn create_fn_mono_item<'tcx>( debug!("create_fn_mono_item(instance={})", instance); let def_id = instance.def_id(); - if tcx.sess.opts.debugging_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) - { + if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) { crate::util::dump_closure_profile(tcx, instance); } @@ -1428,6 +1427,10 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); } } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + collect_miri(tcx, alloc_id, output) + } } } diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 9190e5fe4eb..15276569c32 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -271,7 +271,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( MonoItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def) => def.did, - ty::InstanceDef::VtableShim(..) + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } @@ -303,7 +303,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( // When polymorphization is enabled, methods which do not depend on their generic // parameters, but the self-type of their impl block do will fail to normalize. - if !tcx.sess.opts.debugging_opts.polymorphize || !instance.needs_subst() { + if !tcx.sess.opts.unstable_opts.polymorphize || !instance.needs_subst() { // This is a method within an impl, find out what the self-type is: let impl_self_ty = tcx.subst_and_normalize_erasing_regions( instance.substs, @@ -425,7 +425,7 @@ fn mono_item_visibility<'tcx>( InstanceDef::DropGlue(def_id, Some(_)) => def_id, // These are all compiler glue and such, never exported, always hidden. - InstanceDef::VtableShim(..) + InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) diff --git a/compiler/rustc_monomorphize/src/partitioning/merging.rs b/compiler/rustc_monomorphize/src/partitioning/merging.rs index 09cadc907b1..02bb8dea0c0 100644 --- a/compiler/rustc_monomorphize/src/partitioning/merging.rs +++ b/compiler/rustc_monomorphize/src/partitioning/merging.rs @@ -83,7 +83,7 @@ pub fn merge_codegen_units<'tcx>( for cgu in codegen_units.iter_mut() { if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { - if cx.tcx.sess.opts.debugging_opts.human_readable_cgu_names { + if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names { cgu.set_name(Symbol::intern(&new_cgu_name)); } else { // If we don't require CGU names to be human-readable, we diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index c1992137575..ff2d3869328 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -98,6 +98,7 @@ mod merging; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync; use rustc_hir::def_id::DefIdSet; +use rustc_middle::mir; use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{CodegenUnit, Linkage}; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -141,7 +142,7 @@ trait Partitioner<'tcx> { } fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> { - let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy { + let strategy = match &tcx.sess.opts.unstable_opts.cgu_partitioning_strategy { None => "default", Some(s) => &s[..], }; @@ -345,7 +346,7 @@ fn collect_and_partition_mono_items<'tcx>( tcx: TyCtxt<'tcx>, (): (), ) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { - let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { + let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode_string = s.to_lowercase(); let mode_string = mode_string.trim(); @@ -413,7 +414,7 @@ fn collect_and_partition_mono_items<'tcx>( }) .collect(); - if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { + if tcx.sess.opts.unstable_opts.print_mono_items.is_some() { let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { @@ -479,9 +480,14 @@ fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSe if !visited.insert(did) { continue; } - for scope in &tcx.instance_mir(instance.def).source_scopes { - if let Some((ref inlined, _)) = scope.inlined { - result.insert(inlined.def_id()); + let body = tcx.instance_mir(instance.def); + for block in body.basic_blocks() { + for statement in &block.statements { + let mir::StatementKind::Coverage(_) = statement.kind else { continue }; + let scope = statement.source_info.scope; + if let Some(inlined) = scope.inlined_instance(&body.source_scopes) { + result.insert(inlined.def_id()); + } } } } diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index aaa924d7fa0..394843e510d 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -36,7 +36,7 @@ fn unused_generic_params<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, ) -> FiniteBitSet<u32> { - if !tcx.sess.opts.debugging_opts.polymorphize { + if !tcx.sess.opts.unstable_opts.polymorphize { // If polymorphization disabled, then all parameters are used. return FiniteBitSet::new_empty(); } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 113af328a91..261adbfc346 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,7 +3,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -117,12 +117,12 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Spa source_file_to_parser(sess, file_to_source_file(sess, path, sp)) } -/// Given a `source_file` and config, returns a parser. +/// Given a session and a `source_file`, returns a parser. fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> { panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) } -/// Given a `source_file` and config, return a parser. Returns any buffered errors from lexing the +/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the /// initial token stream. fn maybe_source_file_to_parser( sess: &ParseSess, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2c43563b104..43b48613976 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,10 +15,10 @@ use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::visit::Visitor; -use rustc_ast::StmtKind; use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; +use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult}; @@ -1343,11 +1343,7 @@ impl<'a> Parser<'a> { self.parse_if_expr(attrs) } else if self.check_keyword(kw::For) { if self.choose_generics_over_qpath(1) { - // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery, - // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns. - // When `for <Foo as Bar>::Proj in $expr $block` is wanted, - // you can disambiguate in favor of a pattern with `(...)`. - self.recover_quantified_closure_expr(attrs) + self.parse_closure_expr(attrs) } else { assert!(self.eat_keyword(kw::For)); self.parse_for_expr(None, self.prev_token.span, attrs) @@ -1393,7 +1389,9 @@ impl<'a> Parser<'a> { self.parse_yield_expr(attrs) } else if self.is_do_yeet() { self.parse_yeet_expr(attrs) - } else if self.eat_keyword(kw::Let) { + } else if self.check_keyword(kw::Let) { + self.manage_let_chains_context(); + self.bump(); self.parse_let_expr(attrs) } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) @@ -2094,29 +2092,21 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) } - /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`. - fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + /// Parses a closure expression (e.g., `move |args| expr`). + fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.token.span; - let _ = self.parse_late_bound_lifetime_defs()?; - let span_for = lo.to(self.prev_token.span); - let closure = self.parse_closure_expr(attrs)?; - self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure") - .span_label(closure.span, "the parameters are attached to this closure") - .span_suggestion( - span_for, - "remove the parameters", - "", - Applicability::MachineApplicable, - ) - .emit(); + let binder = if self.check_keyword(kw::For) { + let lo = self.token.span; + let lifetime_defs = self.parse_late_bound_lifetime_defs()?; + let span = lo.to(self.prev_token.span); - Ok(self.mk_expr_err(lo.to(closure.span))) - } + self.sess.gated_spans.gate(sym::closure_lifetime_binder, span); - /// Parses a closure expression (e.g., `move |args| expr`). - fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { - let lo = self.token.span; + ClosureBinder::For { span, generic_params: P::from_vec(lifetime_defs) } + } else { + ClosureBinder::NotPresent + }; let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; @@ -2160,7 +2150,15 @@ impl<'a> Parser<'a> { let closure = self.mk_expr( lo.to(body.span), - ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), + ExprKind::Closure( + binder, + capture_clause, + asyncness, + movability, + decl, + body, + lo.to(decl_hi), + ), attrs, ); @@ -2343,28 +2341,32 @@ impl<'a> Parser<'a> { /// Parses the condition of a `if` or `while` expression. fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> { - let cond = self.with_let_management(true, |local_self| { + self.with_let_management(true, |local_self| { local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) - })?; + }) + } - if let ExprKind::Let(..) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - self.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + // Checks if `let` is in an invalid position like `let x = let y = 1;` or + // if the current `let` is in a let_chains context but nested in another + // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`. + // + // This method expects that the current token is `let`. + fn manage_let_chains_context(&mut self) { + debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _))); + let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed + && !matches!( + self.prev_token.kind, + TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _) + ); + if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr { + self.struct_span_err(self.token.span, "expected expression, found `let` statement") + .emit(); } - - Ok(cond) } /// Parses a `let $pat = $expr` pseudo-expression. /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { - if !self.let_expr_allowed { - self.struct_span_err( - self.prev_token.span, - "expected expression, found `let` statement", - ) - .emit(); - } let lo = self.prev_token.span; let pat = self.parse_pat_allow_top_alt( None, @@ -2377,7 +2379,6 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; let span = lo.to(expr.span); - self.sess.gated_spans.gate(sym::let_chains, span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span), attrs)) } @@ -2683,15 +2684,11 @@ impl<'a> Parser<'a> { pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { // Used to check the `let_chains` and `if_let_guard` features mostly by scaning // `&&` tokens. - fn check_let_expr(expr: &Expr) -> (bool, bool) { + fn check_let_expr(expr: &Expr) -> bool { match expr.kind { - ExprKind::Binary(_, ref lhs, ref rhs) => { - let lhs_rslt = check_let_expr(lhs); - let rhs_rslt = check_let_expr(rhs); - (lhs_rslt.0 || rhs_rslt.0, false) - } - ExprKind::Let(..) => (true, true), - _ => (false, true), + ExprKind::Binary(_, ref lhs, ref rhs) => check_let_expr(lhs) || check_let_expr(rhs), + ExprKind::Let(..) => true, + _ => false, } } let attrs = self.parse_outer_attributes()?; @@ -2706,12 +2703,8 @@ impl<'a> Parser<'a> { let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?; - let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + let has_let_expr = check_let_expr(&cond); if has_let_expr { - if does_not_have_bin_op { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); - } let span = if_span.to(cond.span); this.sess.gated_spans.gate(sym::if_let_guard, span); } @@ -3012,6 +3005,11 @@ impl<'a> Parser<'a> { } }; + let is_shorthand = parsed_field.as_ref().map_or(false, |f| f.is_shorthand); + // A shorthand field can be turned into a full field with `:`. + // We should point this out. + self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon)); + match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) { Ok(_) => { if let Some(f) = parsed_field.or(recovery_field) { @@ -3032,6 +3030,19 @@ impl<'a> Parser<'a> { ",", Applicability::MachineApplicable, ); + } else if is_shorthand + && (AssocOp::from_token(&self.token).is_some() + || matches!(&self.token.kind, token::OpenDelim(_)) + || self.token.kind == token::Dot) + { + // Looks like they tried to write a shorthand, complex expression. + let ident = parsed_field.expect("is_shorthand implies Some").ident; + e.span_suggestion( + ident.span.shrink_to_lo(), + "try naming a field", + &format!("{ident}: "), + Applicability::HasPlaceholders, + ); } } if !recover { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 87bc0d9762e..a14a7fc0610 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1677,7 +1677,6 @@ impl<'a> Parser<'a> { } /// Is this a possibly malformed start of a `macro_rules! foo` item definition? - fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { if self.check_keyword(kw::MacroRules) { let macro_rules_span = self.token.span; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index f6fa19030ac..5deb17b8651 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -104,8 +104,8 @@ pub struct FormatSpec<'a> { pub enum Position<'a> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), - /// The argument is located at a specific index given in the format - ArgumentIs(usize), + /// The argument is located at a specific index given in the format, + ArgumentIs(usize, Option<InnerSpan>), /// The argument has a name. ArgumentNamed(&'a str, InnerSpan), } @@ -113,7 +113,7 @@ pub enum Position<'a> { impl Position<'_> { pub fn index(&self) -> Option<usize> { match self { - ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), + ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i), _ => None, } } @@ -502,8 +502,15 @@ impl<'a> Parser<'a> { /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. fn position(&mut self) -> Option<Position<'a>> { + let start_position = self.cur.peek().map(|item| item.0); if let Some(i) = self.integer() { - Some(ArgumentIs(i)) + let inner_span = start_position.and_then(|start| { + self.cur + .peek() + .cloned() + .and_then(|item| Some(self.to_span_index(start).to(self.to_span_index(item.0)))) + }); + Some(ArgumentIs(i, inner_span)) } else { match self.cur.peek() { Some(&(start, c)) if rustc_lexer::is_id_start(c) => { @@ -572,9 +579,14 @@ impl<'a> Parser<'a> { // '0' flag and then an ill-formatted format string with just a '$' // and no count, but this is better if we instead interpret this as // no '0' flag and '0$' as the width instead. - if self.consume('$') { + if let Some(end) = self.consume_pos('$') { spec.width = CountIsParam(0); + + if let Some((pos, _)) = self.cur.peek().cloned() { + spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos))); + } havewidth = true; + spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1))); } else { spec.flags |= 1 << (FlagSignAwareZeroPad as u32); } @@ -585,6 +597,7 @@ impl<'a> Parser<'a> { spec.width = w; spec.width_span = sp; } + if let Some(start) = self.consume_pos('.') { if let Some(end) = self.consume_pos('*') { // Resolve `CountIsNextParam`. diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index c9667922ee7..a98f816644b 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -62,18 +62,30 @@ fn format_nothing() { } #[test] fn format_position() { - same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); + same( + "{3}", + &[NextArgument(Argument { + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), + format: fmtdflt(), + })], + ); } #[test] fn format_position_nothing_else() { - same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); + same( + "{3:}", + &[NextArgument(Argument { + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), + format: fmtdflt(), + })], + ); } #[test] fn format_type() { same( "{3:x}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: None, align: AlignUnknown, @@ -93,7 +105,7 @@ fn format_align_fill() { same( "{3:>}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: None, align: AlignRight, @@ -110,7 +122,7 @@ fn format_align_fill() { same( "{3:0<}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: Some('0'), align: AlignLeft, @@ -127,7 +139,7 @@ fn format_align_fill() { same( "{3:*<abcd}", &[NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })), format: FormatSpec { fill: Some('*'), align: AlignLeft, @@ -179,6 +191,23 @@ fn format_counts() { })], ); same( + "{1:0$.10x}", + &[NextArgument(Argument { + position: ArgumentIs(1, Some(InnerSpan { start: 2, end: 3 })), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIs(10), + width: CountIsParam(0), + precision_span: None, + width_span: Some(InnerSpan::new(4, 6)), + ty: "x", + ty_span: None, + }, + })], + ); + same( "{:.*x}", &[NextArgument(Argument { position: ArgumentImplicitlyIs(1), @@ -274,7 +303,7 @@ fn format_mixture() { &[ String("abcd "), NextArgument(Argument { - position: ArgumentIs(3), + position: ArgumentIs(3, Some(InnerSpan { start: 7, end: 8 })), format: FormatSpec { fill: None, align: AlignUnknown, diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 676812db59a..faa9c493d88 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros" } rustc_ast = { path = "../rustc_ast" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0723c68a77..fde12b9eee6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,9 +4,10 @@ //! conflicts between multiple such attributes attached to the same //! item. +use crate::errors; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -120,6 +121,10 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), + sym::rustc_lint_opt_deny_field_access => { + self.check_rustc_lint_opt_deny_field_access(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -139,6 +144,7 @@ impl CheckAttrVisitor<'_> { | sym::rustc_const_stable | sym::unstable | sym::stable + | sym::rustc_allowed_through_unstable_modules | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), _ => true, }; @@ -174,16 +180,20 @@ impl CheckAttrVisitor<'_> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let msg = match attr.style { - ast::AttrStyle::Outer => { - "crate-level attribute should be an inner attribute: add an exclamation \ - mark: `#![foo]`" - } - ast::AttrStyle::Inner => "crate-level attribute should be in the root module", - }; - lint.build(msg).emit(); - }); + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } } } @@ -208,37 +218,21 @@ impl CheckAttrVisitor<'_> { } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[{sym}]` is ignored on struct fields, match arms and macro defs", - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttrWithMacro { sym }, + ); } fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms")) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttr { sym }, + ); } /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. @@ -248,9 +242,12 @@ impl CheckAttrVisitor<'_> { | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrFnProto, + ); true } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with @@ -258,19 +255,12 @@ impl CheckAttrVisitor<'_> { // accidentally, to to be compatible with crates depending on them, we can't throw an // error here. Target::AssocConst => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on constants") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrConstants, + ); true } // FIXME(#80564): Same for fields, arms, and macro defs @@ -279,14 +269,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure", - ) - .span_label(span, "not a function or closure") - .emit(); + self.tcx.sess.emit_err(errors::InlineNotFnOrClosure { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -308,36 +294,40 @@ impl CheckAttrVisitor<'_> { // function prototypes can't be covered Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnProto, + ); true } Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoveragePropagate, + ); true } Target::Expression | Target::Statement | Target::Arm => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` may only be applied to function definitions") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnDefn, + ); true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0788, - "`#[no_coverage]` must be applied to coverable code", - ) - .span_label(span, "not coverable code") - .emit(); + self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -388,14 +378,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -407,14 +393,10 @@ impl CheckAttrVisitor<'_> { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -431,13 +413,7 @@ impl CheckAttrVisitor<'_> { ) -> bool { match target { _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0736, - "cannot use `#[track_caller]` with `#[naked]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span }); false } Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, @@ -452,14 +428,9 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0739, - "attribute should be applied to function" - ) - .span_label(span, "not a function") - .emit(); + self.tcx + .sess + .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span }); false } } @@ -484,14 +455,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0701, - "attribute can only be applied to a struct or enum" - ) - .span_label(span, "not a struct or enum") - .emit(); + self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -510,11 +477,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -530,11 +496,10 @@ impl CheckAttrVisitor<'_> { match target { Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -554,16 +519,12 @@ impl CheckAttrVisitor<'_> { // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::TargetFeatureOnStatement, + ); true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an @@ -575,11 +536,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -590,24 +550,17 @@ impl CheckAttrVisitor<'_> { match target { Target::ForeignStatic | Target::Static => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a static") - .span_label(span, "not a static") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic { + attr_span: attr.span, + defn_span: span, + }); false } } } fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name }); } fn check_doc_alias_value( @@ -620,22 +573,12 @@ impl CheckAttrVisitor<'_> { aliases: &mut FxHashMap<String, Span>, ) -> bool { let tcx = self.tcx; - let err_fn = move |span: Span, msg: &str| { - tcx.sess.span_err( - span, - &format!( - "`#[doc(alias{})]` {}", - if is_list { "(\"...\")" } else { " = \"...\"" }, - msg, - ), - ); - false - }; + let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); + let attr_str = + &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); if doc_alias == kw::Empty { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "attribute cannot have empty value", - ); + tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str }); + return false; } let doc_alias_str = doc_alias.as_str(); @@ -643,23 +586,16 @@ impl CheckAttrVisitor<'_> { .chars() .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { - self.tcx.sess.span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!( - "{:?} character isn't allowed in `#[doc(alias{})]`", - c, - if is_list { "(\"...\")" } else { " = \"...\"" }, - ), - ); + tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); return false; } if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "cannot start or end with ' '", - ); + tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str }); + return false; } - if let Some(err) = match target { + + let span = meta.span(); + if let Some(location) = match target { Target::Impl => Some("implementation block"), Target::ForeignMod => Some("extern block"), Target::AssocTy => { @@ -685,19 +621,21 @@ impl CheckAttrVisitor<'_> { Target::Param => return false, _ => None, } { - return err_fn(meta.span(), &format!("isn't allowed on {}", err)); + tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); + return false; } let item_name = self.tcx.hir().name(hir_id); if item_name == doc_alias { - return err_fn(meta.span(), "is the same as the item's name"); + tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str }); + return false; } - let span = meta.span(); if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| { - lint.build("doc alias is duplicated") - .span_label(*entry.entry.get(), "first defined here") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + span, + errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, + ); } true } @@ -722,22 +660,12 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } }, None => { - self.tcx - .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } } @@ -746,14 +674,7 @@ impl CheckAttrVisitor<'_> { } else if let Some(doc_alias) = meta.value_str() { self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) } else { - self.tcx - .sess - .struct_span_err( - meta.span(), - "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \ - strings `#[doc(alias(\"a\", \"b\"))]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() }); false } } @@ -770,65 +691,44 @@ impl CheckAttrVisitor<'_> { }) { Some(ItemKind::Mod(ref module)) => { if !module.item_ids.is_empty() { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on empty modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() }); return false; } } if !rustc_lexer::is_ident(doc_keyword.as_str()) { - self.tcx - .sess - .struct_span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("`{doc_keyword}` is not a valid identifier"), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent { + span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + doc_keyword, + }); return false; } true } - fn check_doc_tuple_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { match self.tcx.hir().find(hir_id).and_then(|node| match node { hir::Node::Item(item) => Some(&item.kind), _ => None, }) { Some(ItemKind::Impl(ref i)) => { - if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity", - ) - .emit(); + let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) + || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind { + bare_fn_ty.decl.inputs.len() == 1 + } else { + false + }; + if !is_valid { + self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on impl blocks", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); return false; } } @@ -857,13 +757,9 @@ impl CheckAttrVisitor<'_> { if let Some((prev_inline, prev_span)) = *specified_inline { if do_inline != prev_inline { let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); - spans.push_span_label(prev_span, "this attribute..."); - spans.push_span_label(meta.span(), "...conflicts with this attribute"); - self.tcx - .sess - .struct_span_err(spans, "conflicting doc inlining attributes") - .help("remove one of the conflicting attributes") - .emit(); + spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first); + spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second); + self.tcx.sess.emit_err(errors::DocKeywordConflict { spans }); return false; } true @@ -872,23 +768,14 @@ impl CheckAttrVisitor<'_> { true } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied to a `use` item", - ); - err.span_label(meta.span(), "only applicable on `use` items"); - if attr.style == AttrStyle::Outer { - err.span_label( - self.tcx.hir().span(hir_id), - "not a `use` item", - ); - } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information") - .emit(); + errors::DocInlineOnlyUse { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), }, ); false @@ -903,15 +790,7 @@ impl CheckAttrVisitor<'_> { attr_name: &str, ) -> bool { if CRATE_HIR_ID == hir_id { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!( - "`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute", - ), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name }); return false; } true @@ -925,36 +804,25 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied at the crate level", - ); - if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID { - if let Ok(mut src) = - self.tcx.sess.source_map().span_to_snippet(attr.span) - { - src.insert(1, '!'); - err.span_suggestion_verbose( - attr.span, - "to apply to the crate, use an inner attribute", - src, - Applicability::MaybeIncorrect, - ); - } else { - err.span_help( - attr.span, - "to apply to the crate, use an inner attribute", - ); - } + self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { + let mut err = lint.build(fluent::passes::attr_crate_level); + if attr.style == AttrStyle::Outer + && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID + { + if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { + src.insert(1, '!'); + err.span_suggestion_verbose( + attr.span, + fluent::passes::suggestion, + src, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help(attr.span, fluent::passes::help); } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information") - .emit(); - }, - ); + } + err.note(fluent::passes::note).emit(); + }); return false; } true @@ -969,18 +837,14 @@ impl CheckAttrVisitor<'_> { match i_meta.name_or_empty() { sym::attr | sym::no_crate_inject => {} _ => { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span(), - |lint| { - lint.build(&format!( - "unknown `doc(test)` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string( - &i_meta.meta_item().unwrap().path - ), - )) - .emit(); + errors::DocTestUnknown { + path: rustc_ast_pretty::pprust::path_to_string( + &i_meta.meta_item().unwrap().path, + ), }, ); is_valid = false; @@ -988,9 +852,12 @@ impl CheckAttrVisitor<'_> { } } } else { - self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { - lint.build("`#[doc(test(...)]` takes a list of attributes").emit(); - }); + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocTestTakesList, + ); is_valid = false; } is_valid @@ -1030,9 +897,9 @@ impl CheckAttrVisitor<'_> { is_valid = false } - sym::tuple_variadic - if !self.check_attr_not_crate_level(meta, hir_id, "tuple_variadic") - || !self.check_doc_tuple_variadic(meta, hir_id) => + sym::fake_variadic + if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") + || !self.check_doc_fake_variadic(meta, hir_id) => { is_valid = false } @@ -1082,7 +949,7 @@ impl CheckAttrVisitor<'_> { | sym::notable_trait | sym::passes | sym::plugins - | sym::tuple_variadic => {} + | sym::fake_variadic => {} sym::test => { if !self.check_test_attr(meta, hir_id) { @@ -1092,79 +959,66 @@ impl CheckAttrVisitor<'_> { sym::primitive => { if !self.tcx.features().rustdoc_internals { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span, - |lint| { - let mut diag = lint.build( - "`doc(primitive)` should never have been stable", - ); - diag.emit(); - }, + errors::DocPrimitive, ); } } _ => { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - i_meta.span, - |lint| { - let mut diag = lint.build(&format!( - "unknown `doc` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string(&i_meta.path), - )); - if i_meta.has_name(sym::spotlight) { - diag.note( - "`doc(spotlight)` was renamed to `doc(notable_trait)`", - ); - diag.span_suggestion_short( - i_meta.span, - "use `notable_trait` instead", - "notable_trait", - Applicability::MachineApplicable, - ); - diag.note("`doc(spotlight)` is now a no-op"); + let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); + if i_meta.has_name(sym::spotlight) { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownSpotlight { + path, + span: i_meta.span } - if i_meta.has_name(sym::include) { - if let Some(value) = i_meta.value_str() { - // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect - let applicability = if list.len() == 1 { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - let inner = if attr.style == AttrStyle::Inner { - "!" - } else { - "" - }; - diag.span_suggestion( - attr.meta().unwrap().span, - "use `doc = include_str!` instead", - format!( - "#{inner}[doc = include_str!(\"{value}\")]", - ), - applicability, - ); - } + ); + } else if i_meta.has_name(sym::include) && + let Some(value) = i_meta.value_str() { + let applicability = if list.len() == 1 { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + // If there are multiple attributes, the suggestion would suggest + // deleting all of them, which is incorrect. + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownInclude { + path, + value: value.to_string(), + inner: (attr.style == AttrStyle::Inner) + .then_some("!") + .unwrap_or(""), + sugg: (attr.meta().unwrap().span, applicability), } - diag.emit(); - }, - ); + ); + } else { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownAny { path } + ); + } is_valid = false; } } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - lint.build("invalid `doc` attribute").emit(); - }, + errors::DocInvalid, ); is_valid = false; } @@ -1179,14 +1033,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::TyAlias => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`pass_by_value` attribute should be applied to a struct, enum or type alias.", - ) - .span_label(span, "is not a struct, enum or type alias") - .emit(); + self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span }); false } } @@ -1196,14 +1043,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Method(MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`rustc_allow_incoherent_impl` attribute should be applied to impl items.", - ) - .span_label(span, "the only currently supported targets are inherent methods") - .emit(); + self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); false } } @@ -1222,12 +1062,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - attr.span, - "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.", - ) - .span_label(span, "only adts, extern types and traits are supported") - .emit(); + .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); false } } @@ -1237,19 +1072,12 @@ impl CheckAttrVisitor<'_> { fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "`must_use` attribute on `async` functions \ - applies to the anonymous `Future` returned by the \ - function, not the value within", - ) - .span_label( - span, - "this attribute does nothing, the `Future`s \ - returned by async functions are already `must_use`", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseAsync { span } + ); } if !matches!( @@ -1277,12 +1105,12 @@ impl CheckAttrVisitor<'_> { _ => "a", }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[must_use]` has no effect when applied to {article} {target}" - )) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseNoEffect { article, target }, + ); } // For now, its always valid @@ -1294,11 +1122,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait") - .span_label(span, "is not a struct, enum, or trait") - .emit(); + self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); false } } @@ -1318,16 +1142,12 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Cold { span }, + ); } } } @@ -1342,19 +1162,12 @@ impl CheckAttrVisitor<'_> { return; } - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = - lint.build("attribute should be applied to an `extern` block with non-Rust ABI"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); - if target != Target::ForeignMod { - diag.span_label(span, "not an `extern` block"); - } - diag.emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Link { span: (target != Target::ForeignMod).then_some(span) }, + ); } /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. @@ -1371,30 +1184,22 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates // used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = - lint.build("attribute should be applied to a foreign function or static"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", + let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span); + if let Some(s) = attr.value_str() { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkName { span, attr_span, value: s.as_str() }, ); - - // See issue #47725 - if let Target::ForeignMod = target { - if let Some(value) = attr.value_str() { - diag.span_help( - attr.span, - &format!(r#"try `#[link(name = "{value}")]` instead"#), - ); - } else { - diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#); - } - } - - diag.span_label(span, "not a foreign function or static"); - diag.emit(); - }); + } else { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkName { span, attr_span, value: "..." }, + ); + }; } } } @@ -1412,14 +1217,7 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to an `extern crate` item", - ) - .span_label(span, "not an `extern crate` item") - .emit(); + self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span }); false } } @@ -1449,14 +1247,7 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a free function, impl method or static", - ) - .span_label(span, "not a free function, impl method or static") - .emit(); + self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span }); false } } @@ -1469,11 +1260,10 @@ impl CheckAttrVisitor<'_> { target: Target, ) -> bool { if target != Target::Struct { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a struct") - .span_label(span, "not a struct") - .emit(); + self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct { + attr_span: attr.span, + span, + }); return false; } @@ -1484,10 +1274,7 @@ impl CheckAttrVisitor<'_> { if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) { true } else { - self.tcx - .sess - .struct_span_err(attr.span, "expected exactly one integer literal argument") - .emit(); + self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); false } } @@ -1502,11 +1289,10 @@ impl CheckAttrVisitor<'_> { ) -> bool { let is_function = matches!(target, Target::Fn); if !is_function { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); return false; } @@ -1526,29 +1312,20 @@ impl CheckAttrVisitor<'_> { match param.kind { hir::GenericParamKind::Const { .. } => {} _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "#[rustc_legacy_const_generics] functions must \ - only have const generics", - ) - .span_label(param.span, "non-const generic parameter") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly { + attr_span: attr.span, + param_span: param.span, + }); return false; } } } if list.len() != generics.params.len() { - self.tcx - .sess - .struct_span_err( - attr.span, - "#[rustc_legacy_const_generics] must have one index for each generic parameter", - ) - .span_label(generics.span, "generic parameters") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex { + attr_span: attr.span, + generics_span: generics.span, + }); return false; } @@ -1558,19 +1335,10 @@ impl CheckAttrVisitor<'_> { if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) { if *val >= arg_count { let span = meta.span(); - self.tcx - .sess - .struct_span_err(span, "index exceeds number of arguments") - .span_label( - span, - format!( - "there {} only {} argument{}", - pluralize!("is", arg_count), - arg_count, - pluralize!(arg_count) - ), - ) - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed { + span, + arg_count: arg_count as usize, + }); return false; } } else { @@ -1579,10 +1347,7 @@ impl CheckAttrVisitor<'_> { } if !invalid_args.is_empty() { - self.tcx - .sess - .struct_span_err(invalid_args, "arguments should be non-negative integers") - .emit(); + self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); false } else { true @@ -1594,11 +1359,10 @@ impl CheckAttrVisitor<'_> { fn check_applied_to_fn_or_method(&self, attr: &Attribute, span: Span, target: Target) -> bool { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } else { true @@ -1622,16 +1386,42 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct => true, + _ => { + self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + false + } + } + } + + /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. + fn check_rustc_lint_opt_deny_field_access( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Field => true, + _ => { + self.tcx + .sess + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + false + } + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { - if self.tcx.sess.opts.debugging_opts.query_dep_graph { + if self.tcx.sess.opts.unstable_opts.query_dep_graph { true } else { - self.tcx - .sess - .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled") - .emit(); + self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span }); false } } @@ -1650,16 +1440,12 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function or static") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function or static") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::LinkSection { span }, + ); } } } @@ -1685,41 +1471,22 @@ impl CheckAttrVisitor<'_> { Target::ForeignStatic => "static", _ => unreachable!(), }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[no_mangle]` has no effect on a foreign {foreign_item_kind}" - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, format!("foreign {foreign_item_kind}")) - .note("symbol names in extern blocks are not mangled") - .span_suggestion( - attr.span, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind }, + ); } _ => { // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "attribute should be applied to a free function, impl method or static", - ) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a free function, impl method or static") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::NoMangle { span }, + ); } } } @@ -1752,13 +1519,7 @@ impl CheckAttrVisitor<'_> { for hint in &hints { if !hint.is_meta_item() { - struct_span_err!( - self.tcx.sess, - hint.span(), - E0565, - "meta item in `repr` must be an identifier" - ) - .emit(); + self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() }); continue; } @@ -1808,21 +1569,6 @@ impl CheckAttrVisitor<'_> { _ => ("a", "struct, enum, or union"), } } - sym::no_niche => { - if !self.tcx.features().enabled(sym::no_niche) { - feature_err( - &self.tcx.sess.parse_sess, - sym::no_niche, - hint.span(), - "the attribute `repr(no_niche)` is currently unstable", - ) - .emit(); - } - match target { - Target::Struct | Target::Enum => continue, - _ => ("a", "struct or enum"), - } - } sym::i8 | sym::u8 | sym::i16 @@ -1870,10 +1616,8 @@ impl CheckAttrVisitor<'_> { // This is not ideal, but tracking precisely which ones are at fault is a huge hassle. let hint_spans = hints.iter().map(|hint| hint.span()); - // Error on repr(transparent, <anything else apart from no_niche>). - let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche; - let non_no_niche_count = hints.iter().filter(non_no_niche).count(); - if is_transparent && non_no_niche_count > 1 { + // Error on repr(transparent, <anything else>). + if is_transparent && hints.len() > 1 { let hint_spans: Vec<_> = hint_spans.clone().collect(); struct_span_err!( self.tcx.sess, @@ -1896,15 +1640,11 @@ impl CheckAttrVisitor<'_> { return false; })) { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( CONFLICTING_REPR_HINTS, hir_id, hint_spans.collect::<Vec<Span>>(), - |lint| { - lint.build("conflicting representation hints") - .code(rustc_errors::error_code!(E0566)) - .emit(); - }, + errors::ReprConflicting, ); } } @@ -1914,9 +1654,7 @@ impl CheckAttrVisitor<'_> { let mut used_compiler_span = None; for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { if target != Target::Static { - self.tcx - .sess - .span_err(attr.span, "attribute must be applied to a `static` variable"); + self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span }); } let inner = attr.meta_item_list(); match inner.as_deref() { @@ -1942,14 +1680,9 @@ impl CheckAttrVisitor<'_> { } } if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) { - let spans = vec![linker_span, compiler_span]; self.tcx .sess - .struct_span_err( - spans, - "`used(compiler)` and `used(linker)` can't be used together", - ) - .emit(); + .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] }); } } @@ -1991,9 +1724,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied to a macro") - .span_label(span, "not a macro") - .emit(); + .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); false } } @@ -2004,29 +1735,26 @@ impl CheckAttrVisitor<'_> { match target { Target::Mod => {} _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a module") - .emit(); + self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span }); return false; } } let Some(hints) = attr.meta_item_list() else { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; }; let hint = match hints.len() { 1 => &hints[0], _ => { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; } }; let Some(meta_item) = hint.meta_item() else { - self.emit_debugger_visualizer_err(attr.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span }); return false; }; @@ -2034,7 +1762,7 @@ impl CheckAttrVisitor<'_> { (sym::natvis_file, Some(value)) => value, (sym::gdb_script_file, Some(value)) => value, (_, _) => { - self.emit_debugger_visualizer_err(meta_item.span); + self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span }); return false; } }; @@ -2063,16 +1791,6 @@ impl CheckAttrVisitor<'_> { } } - fn emit_debugger_visualizer_err(&self, span: Span) { - self.tcx - .sess - .struct_span_err(span, "invalid argument") - .note(r#"expected: `natvis_file = "..."`"#) - .note(r#"OR"#) - .note(r#"expected: `gdb_script_file = "..."`"#) - .emit(); - } - /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( @@ -2099,9 +1817,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied to `const fn`") - .span_label(span, "not a `const fn`") - .emit(); + .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); false } } @@ -2118,9 +1834,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err(attr.span, "attribute should be applied functions or statics") - .span_label(span, "not a function or static") - .emit(); + .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); false } } @@ -2131,10 +1845,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a trait") - .emit(); + self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span }); false } } @@ -2143,10 +1854,7 @@ impl CheckAttrVisitor<'_> { fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool { match target { Target::Expression => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute cannot be applied to an expression") - .emit(); + self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span }); false } _ => true, @@ -2156,9 +1864,12 @@ impl CheckAttrVisitor<'_> { fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) { match target { Target::Closure | Target::Expression | Target::Statement | Target::Arm => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute is ignored here").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Deprecated, + ); } _ => {} } @@ -2169,29 +1880,30 @@ impl CheckAttrVisitor<'_> { match target { Target::ExternCrate | Target::Mod => {} _ => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[{name}]` only has an effect on `extern crate` and modules" - )) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MacroUse { name }, + ); } } } fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::MacroDef { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[macro_export]` only has an effect on macro definitions").emit(); - }); + self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport); } } fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::Fn { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[plugin_registrar]` only has an effect on functions").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::PluginRegistrar, + ); } } @@ -2210,10 +1922,7 @@ impl CheckAttrVisitor<'_> { | sym::target_feature ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) { - format!( - "attribute `{}` with an empty list has no effect", - attr.name_or_empty() - ) + errors::UnusedNote::EmptyList { name: attr.name_or_empty() } } else if matches!( attr.name_or_empty(), sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect @@ -2223,27 +1932,19 @@ impl CheckAttrVisitor<'_> { && let MetaItemKind::NameValue(_) = &item.kind && item.path == sym::reason { - format!( - "attribute `{}` without any lints has no effect", - attr.name_or_empty() - ) + errors::UnusedNote::NoLints { name: attr.name_or_empty() } } else if attr.name_or_empty() == sym::default_method_body_is_const { - format!("`default_method_body_is_const` has been replaced with `#[const_trait]` on traits") + errors::UnusedNote::DefaultMethodBodyConst } else { return; }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("unused attribute") - .span_suggestion( - attr.span, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .note(¬e) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Unused { attr_span: attr.span, note }, + ); } } @@ -2414,14 +2115,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) for attr in attrs { if attr.has_name(sym::inline) { - struct_span_err!( - tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure", - ) - .span_label(attr.span, "not a function or closure") - .emit(); + tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span }); } } } @@ -2461,23 +2155,20 @@ fn check_duplicates( } else { (attr.span, *entry.get()) }; - tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, this, |lint| { - let mut db = lint.build("unused attribute"); - db.span_note(other, "attribute also specified here").span_suggestion( + tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + this, + errors::UnusedDuplicate { this, - "remove this attribute", - "", - Applicability::MachineApplicable, - ); - if matches!(duplicates, FutureWarnFollowing | FutureWarnPreceding) { - db.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); - } - db.emit(); - }); + other, + warning: matches!( + duplicates, + FutureWarnFollowing | FutureWarnPreceding + ) + .then_some(()), + }, + ); } Entry::Vacant(entry) => { entry.insert(attr.span); @@ -2492,19 +2183,11 @@ fn check_duplicates( } else { (attr.span, *entry.get()) }; - tcx.sess - .struct_span_err( - this, - &format!("multiple `{}` attributes", attr.name_or_empty()), - ) - .span_note(other, "attribute also specified here") - .span_suggestion( - this, - "remove this attribute", - "", - Applicability::MachineApplicable, - ) - .emit(); + tcx.sess.emit_err(errors::UnusedMultiple { + this, + other, + name: attr.name_or_empty(), + }); } Entry::Vacant(entry) => { entry.insert(attr.span); diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 31c159c1f75..f1a81b65329 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -122,7 +122,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a // corresponding feature gate. This encourages nightly users to use feature gates when // possible. - None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => { + None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => { tcx.sess.span_warn(span, "skipping const checks"); return; } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 1add91fc9c5..7381019a620 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -77,7 +77,7 @@ fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) .sess .struct_span_err( attr.span, - &format!("`{}` attribute can only be used on functions", sym.as_str()), + &format!("`{}` attribute can only be used on functions", sym), ) .emit(); } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs new file mode 100644 index 00000000000..5feb0e2956b --- /dev/null +++ b/compiler/rustc_passes/src/errors.rs @@ -0,0 +1,645 @@ +use rustc_errors::{Applicability, MultiSpan}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(LintDiagnostic)] +#[lint(passes::outer_crate_level_attr)] +pub struct OuterCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::inner_crate_level_attr)] +pub struct InnerCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr_with_macro)] +pub struct IgnoredAttrWithMacro<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr)] +pub struct IgnoredAttr<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_function_prototype)] +pub struct IgnoredInlineAttrFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_constants)] +#[warn_] +#[note] +pub struct IgnoredInlineAttrConstants; + +#[derive(SessionDiagnostic)] +#[error(passes::inline_not_fn_or_closure, code = "E0518")] +pub struct InlineNotFnOrClosure { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_ignored_function_prototype)] +pub struct IgnoredNoCoverageFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_propagate)] +pub struct IgnoredNoCoveragePropagate; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_fn_defn)] +pub struct IgnoredNoCoverageFnDefn; + +#[derive(SessionDiagnostic)] +#[error(passes::no_coverage_not_coverable, code = "E0788")] +pub struct IgnoredNoCoverageNotCoverable { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn)] +pub struct AttrShouldBeAppliedToFn { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::naked_tracked_caller, code = "E0736")] +pub struct NakedTrackedCaller { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn, code = "E0739")] +pub struct TrackedCallerWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_struct_enum, code = "E0701")] +pub struct NonExhaustiveWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_trait)] +pub struct AttrShouldBeAppliedToTrait { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::target_feature_on_statement)] +pub struct TargetFeatureOnStatement; + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_static)] +pub struct AttrShouldBeAppliedToStatic { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_expect_str)] +pub struct DocExpectStr<'a> { + #[primary_span] + pub attr_span: Span, + pub attr_name: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_empty)] +pub struct DocAliasEmpty<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_char)] +pub struct DocAliasBadChar<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub char_: char, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_start_end)] +pub struct DocAliasStartEnd<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_location)] +pub struct DocAliasBadLocation<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub location: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_an_alias)] +pub struct DocAliasNotAnAlias<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_alias_duplicated)] +pub struct DocAliasDuplicated { + #[label] + pub first_defn: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_string_literal)] +pub struct DocAliasNotStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_malformed)] +pub struct DocAliasMalformed { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_empty_mod)] +pub struct DocKeywordEmptyMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_not_mod)] +pub struct DocKeywordNotMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_invalid_ident)] +pub struct DocKeywordInvalidIdent { + #[primary_span] + pub span: Span, + pub doc_keyword: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_fake_variadic_not_valid)] +pub struct DocFakeVariadicNotValid { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_only_impl)] +pub struct DocKeywordOnlyImpl { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_inline_conflict)] +#[help] +pub struct DocKeywordConflict { + #[primary_span] + pub spans: MultiSpan, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_inline_only_use)] +#[note] +pub struct DocInlineOnlyUse { + #[label] + pub attr_span: Span, + #[label(passes::not_a_use_item_label)] + pub item_span: Option<Span>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_attr_not_crate_level)] +pub struct DocAttrNotCrateLevel<'a> { + #[primary_span] + pub span: Span, + pub attr_name: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown)] +pub struct DocTestUnknown { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_takes_list)] +pub struct DocTestTakesList; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_primitive)] +pub struct DocPrimitive; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_any)] +pub struct DocTestUnknownAny { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_spotlight)] +#[note] +#[note(passes::no_op_note)] +pub struct DocTestUnknownSpotlight { + pub path: String, + #[suggestion_short(applicability = "machine-applicable", code = "notable_trait")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_include)] +pub struct DocTestUnknownInclude { + pub path: String, + pub value: String, + pub inner: &'static str, + #[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")] + pub sugg: (Span, Applicability), +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_invalid)] +pub struct DocInvalid; + +#[derive(SessionDiagnostic)] +#[error(passes::pass_by_value)] +pub struct PassByValue { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::allow_incoherent_impl)] +pub struct AllowIncoherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::has_incoherent_inherent_impl)] +pub struct HasIncoherentInherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_async)] +pub struct MustUseAsync { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_no_effect)] +pub struct MustUseNoEffect { + pub article: &'static str, + pub target: rustc_hir::Target, +} + +#[derive(SessionDiagnostic)] +#[error(passes::must_not_suspend)] +pub struct MustNotSuspend { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::cold)] +#[warn_] +pub struct Cold { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link)] +#[warn_] +pub struct Link { + #[label] + pub span: Option<Span>, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link_name)] +#[warn_] +pub struct LinkName<'a> { + #[help] + pub attr_span: Option<Span>, + #[label] + pub span: Span, + pub value: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::no_link)] +pub struct NoLink { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::export_name)] +pub struct ExportName { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_layout_scalar_valid_range_not_struct)] +pub struct RustcLayoutScalarValidRangeNotStruct { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_layout_scalar_valid_range_arg)] +pub struct RustcLayoutScalarValidRangeArg { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_only)] +pub struct RustcLegacyConstGenericsOnly { + #[primary_span] + pub attr_span: Span, + #[label] + pub param_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index)] +pub struct RustcLegacyConstGenericsIndex { + #[primary_span] + pub attr_span: Span, + #[label] + pub generics_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index_exceed)] +pub struct RustcLegacyConstGenericsIndexExceed { + #[primary_span] + #[label] + pub span: Span, + pub arg_count: usize, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_legacy_const_generics_index_negative)] +pub struct RustcLegacyConstGenericsIndexNegative { + #[primary_span] + pub invalid_args: Vec<Span>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_dirty_clean)] +pub struct RustcDirtyClean { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link_section)] +#[warn_] +pub struct LinkSection { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_mangle_foreign)] +#[warn_] +#[note] +pub struct NoMangleForeign { + #[label] + pub span: Span, + #[suggestion(applicability = "machine-applicable")] + pub attr_span: Span, + pub foreign_item_kind: &'static str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_mangle)] +#[warn_] +pub struct NoMangle { + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::repr_ident, code = "E0565")] +pub struct ReprIdent { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::repr_conflicting, code = "E0566")] +pub struct ReprConflicting; + +#[derive(SessionDiagnostic)] +#[error(passes::used_static)] +pub struct UsedStatic { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::used_compiler_linker)] +pub struct UsedCompilerLinker { + #[primary_span] + pub spans: Vec<Span>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::allow_internal_unstable)] +pub struct AllowInternalUnstable { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::debug_visualizer_placement)] +pub struct DebugVisualizerPlacement { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::debug_visualizer_invalid)] +#[note(passes::note_1)] +#[note(passes::note_2)] +#[note(passes::note_3)] +pub struct DebugVisualizerInvalid { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_allow_const_fn_unstable)] +pub struct RustcAllowConstFnUnstable { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_std_internal_symbol)] +pub struct RustcStdInternalSymbol { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::const_trait)] +pub struct ConstTrait { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::stability_promotable)] +pub struct StabilityPromotable { + #[primary_span] + pub attr_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::deprecated)] +pub struct Deprecated; + +#[derive(LintDiagnostic)] +#[lint(passes::macro_use)] +pub struct MacroUse { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[lint(passes::macro_export)] +pub struct MacroExport; + +#[derive(LintDiagnostic)] +#[lint(passes::plugin_registrar)] +pub struct PluginRegistrar; + +#[derive(SessionSubdiagnostic)] +pub enum UnusedNote { + #[note(passes::unused_empty_lints_note)] + EmptyList { name: Symbol }, + #[note(passes::unused_no_lints_note)] + NoLints { name: Symbol }, + #[note(passes::unused_default_method_body_const_note)] + DefaultMethodBodyConst, +} + +#[derive(LintDiagnostic)] +#[lint(passes::unused)] +pub struct Unused { + #[suggestion(applicability = "machine-applicable")] + pub attr_span: Span, + #[subdiagnostic] + pub note: UnusedNote, +} + +#[derive(SessionDiagnostic)] +#[error(passes::non_exported_macro_invalid_attrs, code = "E0518")] +pub struct NonExportedMacroInvalidAttrs { + #[primary_span] + #[label] + pub attr_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::unused_duplicate)] +pub struct UnusedDuplicate { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + #[warn_] + pub warning: Option<()>, +} + +#[derive(SessionDiagnostic)] +#[error(passes::unused_multiple)] +pub struct UnusedMultiple { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_ty)] +pub struct RustcLintOptTy { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_deny_field_access)] +pub struct RustcLintOptDenyFieldAccess { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 9deb0042ff3..212ea9e57a3 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; pub fn check_crate(tcx: TyCtxt<'_>) { tcx.dep_graph.assert_ignored(); - if tcx.sess.opts.debugging_opts.hir_stats { + if tcx.sess.opts.unstable_opts.hir_stats { crate::hir_stats::print_hir_stats(tcx); } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 497c0931c21..defa9d15296 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -7,8 +7,8 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(iter_intersperse)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(try_blocks)] @@ -27,6 +27,7 @@ pub mod dead; mod debugger_visualizer; mod diagnostic_items; pub mod entry; +mod errors; pub mod hir_id_validator; pub mod hir_stats; mod lang_items; diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 26bfa4737a7..e05994f13e4 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -1,8 +1,8 @@ -// Detecting lib features (i.e., features that are not lang features). -// -// These are declared using stability attributes (e.g., `#[stable (..)]` -// and `#[unstable (..)]`), but are not declared in one single location -// (unlike lang features), which means we need to collect them instead. +//! Detecting lib features (i.e., features that are not lang features). +//! +//! These are declared using stability attributes (e.g., `#[stable (..)]` and `#[unstable (..)]`), +//! but are not declared in one single location (unlike lang features), which means we need to +//! collect them instead. use rustc_ast::{Attribute, MetaItemKind}; use rustc_errors::struct_span_err; @@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> { fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) { let already_in_stable = self.lib_features.stable.contains_key(&feature); - let already_in_unstable = self.lib_features.unstable.contains(&feature); + let already_in_unstable = self.lib_features.unstable.contains_key(&feature); match (since, already_in_stable, already_in_unstable) { (Some(since), _, false) => { - if let Some(prev_since) = self.lib_features.stable.get(&feature) { + if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { if *prev_since != since { self.span_feature_error( span, @@ -89,10 +89,10 @@ impl<'tcx> LibFeatureCollector<'tcx> { } } - self.lib_features.stable.insert(feature, since); + self.lib_features.stable.insert(feature, (since, span)); } (None, false, _) => { - self.lib_features.unstable.insert(feature); + self.lib_features.unstable.insert(feature, span); } (Some(_), _, true) | (None, true, _) => { self.span_feature_error( diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 0070c0699a4..eed3e1579a2 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> { pats.extend(inner_pat.iter()); } Struct(_, fields, _) => { - let (short, not_short): (Vec<&_>, Vec<&_>) = + let (short, not_short): (Vec<_>, _) = fields.iter().partition(|f| f.is_shorthand); shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id)); pats.extend(not_short.iter().map(|f| f.pat)); @@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> { } } - return shorthand_field_ids; + shorthand_field_ids } fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { @@ -368,6 +368,9 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.add_from_pat(&local.pat); + if local.els.is_some() { + self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); + } intravisit::walk_local(self, local); } @@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // initialization, which is mildly more complex than checking // once at the func header but otherwise equivalent. - let succ = self.propagate_through_opt_expr(local.init, succ); - self.define_bindings_in_pat(&local.pat, succ) + if let Some(els) = local.els { + // Eventually, `let pat: ty = init else { els };` is mostly equivalent to + // `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };` + // except that extended lifetime applies at the `init` location. + // + // (e) + // | + // v + // (expr) + // / \ + // | | + // v v + // bindings els + // | + // v + // ( succ ) + // + if let Some(init) = local.init { + let else_ln = self.propagate_through_block(els, succ); + let ln = self.live_node(local.hir_id, local.span); + self.init_from_succ(ln, succ); + self.merge_from_succ(ln, else_ln); + let succ = self.propagate_through_expr(init, ln); + self.define_bindings_in_pat(&local.pat, succ) + } else { + span_bug!( + stmt.span, + "variable is uninitialized but an unexpected else branch is found" + ) + } + } else { + let succ = self.propagate_through_opt_expr(local.init, succ); + self.define_bindings_in_pat(&local.pat, succ) + } } hir::StmtKind::Item(..) => succ, hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { @@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // (rvalue) || (rvalue) // | || | // v || v - // (write of place) || (place components) + // (write of place) || (place components) // | || | // v || v // (succ) || (succ) diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 68b2a052391..cdda0e388dd 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -57,7 +57,13 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprKind::Loop(ref b, _, source, _) => { self.with_context(Loop(source), |v| v.visit_block(&b)); } - hir::ExprKind::Closure { ref fn_decl, body, fn_decl_span, movability, .. } => { + hir::ExprKind::Closure(&hir::Closure { + ref fn_decl, + body, + fn_decl_span, + movability, + .. + }) => { let cx = if let Some(Movability::Static) = movability { AsyncClosure(fn_decl_span) } else { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index c830ab11e8e..f7e3fac6b2e 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -273,7 +273,10 @@ impl<'tcx> ReachableContext<'tcx> { } hir::ImplItemKind::TyAlias(_) => {} }, - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) => { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), + .. + }) => { self.visit_nested_body(body); } // Nothing to recurse on for these diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 12050dceb60..ca6a2ac3db3 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,9 +1,10 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use rustc_attr::{self as attr, ConstStability, Stability}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::struct_span_err; +use attr::StabilityLevel; +use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -13,13 +14,12 @@ use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind, use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index}; -use rustc_middle::ty::{self, query::Providers, TyCtxt}; +use rustc_middle::ty::{query::Providers, TyCtxt}; use rustc_session::lint; use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; -use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; use std::cmp::Ordering; @@ -29,13 +29,13 @@ use std::num::NonZeroU32; #[derive(PartialEq)] enum AnnotationKind { - // Annotation is required if not inherited from unstable parents + /// Annotation is required if not inherited from unstable parents. Required, - // Annotation is useless, reject it + /// Annotation is useless, reject it. Prohibited, - // Deprecation annotation is useless, reject it. (Stability attribute is still required.) + /// Deprecation annotation is useless, reject it. (Stability attribute is still required.) DeprecationProhibited, - // Annotation itself is useless, but it can be propagated to children + /// Annotation itself is useless, but it can be propagated to children. Container, } @@ -83,7 +83,7 @@ impl InheritStability { } } -// A private tree-walker for producing an Index. +/// A private tree-walker for producing an `Index`. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, index: &'a mut Index, @@ -94,9 +94,9 @@ struct Annotator<'a, 'tcx> { } impl<'a, 'tcx> Annotator<'a, 'tcx> { - // Determine the stability for a node based on its attributes and inherited - // stability. The stability is recorded in the index and used as the parent. - // If the node is a function, `fn_sig` is its signature + /// Determine the stability for a node based on its attributes and inherited stability. The + /// stability is recorded in the index and used as the parent. If the node is a function, + /// `fn_sig` is its signature. fn annotate<F>( &mut self, def_id: LocalDefId, @@ -224,7 +224,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since }) = + if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly @@ -265,6 +265,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { + self.index.implications.insert(implied_by, feature); + } + self.index.stab_map.insert(def_id, stab); stab }); @@ -610,6 +614,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { stab_map: Default::default(), const_stab_map: Default::default(), depr_map: Default::default(), + implications: Default::default(), }; { @@ -628,15 +633,13 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { // compiling `librustc_*` crates themselves so we can leverage crates.io // while maintaining the invariant that all sysroot crates are unstable // by default and are unable to be used. - if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { - let reason = "this crate is being loaded from the sysroot, an \ - unstable location; did you mean to load this crate \ - from crates.io via `Cargo.toml` instead?"; + if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { let stability = Stability { level: attr::StabilityLevel::Unstable { - reason: Some(Symbol::intern(reason)), + reason: UnstableReason::Default, issue: NonZeroU32::new(27812), is_soft: false, + implied_by: None, }, feature: sym::rustc_private, }; @@ -667,6 +670,7 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_unstable_api_usage, stability_index, + stability_implications: |tcx, _| tcx.stability().implications.clone(), lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()), lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()), lookup_deprecation_entry: |tcx, id| { @@ -766,39 +770,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - // There's no good place to insert stability check for non-Copy unions, - // so semi-randomly perform it here in stability.rs - hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => { - let ty = self.tcx.type_of(item.def_id); - let ty::Adt(adt_def, substs) = ty.kind() else { bug!() }; - - // Non-`Copy` fields are unstable, except for `ManuallyDrop`. - let param_env = self.tcx.param_env(item.def_id); - for field in &adt_def.non_enum_variant().fields { - let field_ty = field.ty(self.tcx, substs); - if !field_ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop()) - && !field_ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), param_env) - { - if field_ty.needs_drop(self.tcx, param_env) { - // Avoid duplicate error: This will error later anyway because fields - // that need drop are not allowed. - self.tcx.sess.delay_span_bug( - item.span, - "union should have been rejected due to potentially dropping field", - ); - } else { - feature_err( - &self.tcx.sess.parse_sess, - sym::untagged_unions, - self.tcx.def_span(field.did), - "unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable", - ) - .emit(); - } - } - } - } - _ => (/* pass */), } intravisit::walk_item(self, item); @@ -807,7 +778,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) { if let Some(def_id) = path.res.opt_def_id() { let method_span = path.segments.last().map(|s| s.ident.span); - self.tcx.check_stability_allow_unstable( + let item_is_allowed = self.tcx.check_stability_allow_unstable( def_id, Some(id), path.span, @@ -817,8 +788,52 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } else { AllowUnstable::No }, - ) + ); + + let is_allowed_through_unstable_modules = |def_id| { + self.tcx + .lookup_stability(def_id) + .map(|stab| match stab.level { + StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { + allowed_through_unstable_modules + } + _ => false, + }) + .unwrap_or(false) + }; + + if item_is_allowed && !is_allowed_through_unstable_modules(def_id) { + // Check parent modules stability as well if the item the path refers to is itself + // stable. We only emit warnings for unstable path segments if the item is stable + // or allowed because stability is often inherited, so the most common case is that + // both the segments and the item are unstable behind the same feature flag. + // + // We check here rather than in `visit_path_segment` to prevent visiting the last + // path segment twice + // + // We include special cases via #[rustc_allowed_through_unstable_modules] for items + // that were accidentally stabilized through unstable paths before this check was + // added, such as `core::intrinsics::transmute` + let parents = path.segments.iter().rev().skip(1); + for path_segment in parents { + if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) { + // use `None` for id to prevent deprecation check + self.tcx.check_stability_allow_unstable( + def_id, + None, + path.span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + } + } + } } + intravisit::walk_path(self, path) } } @@ -884,7 +899,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { /// libraries, identify activated features that don't exist and error about them. pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let is_staged_api = - tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api; + tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api; if is_staged_api { let access_levels = &tcx.privacy_access_levels(()); let mut missing = MissingStabilityAnnotations { tcx, access_levels }; @@ -934,32 +949,51 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { remaining_lib_features.remove(&sym::libc); remaining_lib_features.remove(&sym::test); - let check_features = |remaining_lib_features: &mut FxIndexMap<_, _>, defined_features: &[_]| { - for &(feature, since) in defined_features { - if let Some(since) = since { - if let Some(span) = remaining_lib_features.get(&feature) { - // Warn if the user has enabled an already-stable lib feature. - unnecessary_stable_feature_lint(tcx, *span, feature, since); - } - } - remaining_lib_features.remove(&feature); - if remaining_lib_features.is_empty() { - break; - } - } - }; - // We always collect the lib features declared in the current crate, even if there are // no unknown features, because the collection also does feature attribute validation. - let local_defined_features = tcx.lib_features(()).to_vec(); - if !remaining_lib_features.is_empty() { - check_features(&mut remaining_lib_features, &local_defined_features); + let local_defined_features = tcx.lib_features(()); + let mut all_lib_features: FxHashMap<_, _> = + local_defined_features.to_vec().iter().map(|el| *el).collect(); + let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone(); + for &cnum in tcx.crates(()) { + implications.extend(tcx.stability_implications(cnum)); + all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el)); + } - for &cnum in tcx.crates(()) { + // Check that every feature referenced by an `implied_by` exists (for features defined in the + // local crate). + for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) { + // Only `implied_by` needs to be checked, `feature` is guaranteed to exist. + if !all_lib_features.contains_key(implied_by) { + let span = local_defined_features + .stable + .get(feature) + .map(|(_, span)| span) + .or_else(|| local_defined_features.unstable.get(feature)) + .expect("feature that implied another does not exist"); + tcx.sess + .struct_span_err( + *span, + format!("feature `{implied_by}` implying `{feature}` does not exist"), + ) + .emit(); + } + } + + if !remaining_lib_features.is_empty() { + for (feature, since) in all_lib_features.iter() { + if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) { + // Warn if the user has enabled an already-stable lib feature. + if let Some(implies) = implications.get(&feature) { + unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since); + } else { + unnecessary_stable_feature_lint(tcx, *span, *feature, *since); + } + } + remaining_lib_features.remove(&feature); if remaining_lib_features.is_empty() { break; } - check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum)); } } @@ -971,12 +1005,41 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // don't lint about unused features. We should re-enable this one day! } +fn unnecessary_partially_stable_feature_lint( + tcx: TyCtxt<'_>, + span: Span, + feature: Symbol, + implies: Symbol, + since: Symbol, +) { + tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { + lint.build(&format!( + "the feature `{feature}` has been partially stabilized since {since} and is succeeded \ + by the feature `{implies}`" + )) + .span_suggestion( + span, + &format!( + "if you are using features which are still unstable, change to using `{implies}`" + ), + implies, + Applicability::MaybeIncorrect, + ) + .span_suggestion( + tcx.sess.source_map().span_extend_to_line(span), + "if you are using features which are now stable, remove this line", + "", + Applicability::MaybeIncorrect, + ) + .emit(); + }); +} + fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { lint.build(&format!( - "the feature `{}` has been stable since {} and no longer requires \ - an attribute to enable", - feature, since + "the feature `{feature}` has been stable since {since} and no longer requires an \ + attribute to enable", )) .emit(); }); diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 3291be05807..c48b4ecf87a 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -68,7 +68,7 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } } - for (name, item) in WEAK_ITEMS_REFS.clone().into_sorted_vector().into_iter() { + for (name, &item) in WEAK_ITEMS_REFS.iter() { if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() { if item == LangItem::PanicImpl { tcx.sess.err("`#[panic_handler]` function required, but not found"); diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 482721d373a..b0fac91f6eb 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(SessionDiagnostic)] @@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> { #[label(privacy::visibility_label)] pub vis_span: Span, } + +#[derive(LintDiagnostic)] +#[lint(privacy::from_private_dep_in_public_interface)] +pub struct FromPrivateDependencyInPublicInterface<'a> { + pub kind: &'a str, + pub descr: String, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[lint(privacy::private_in_public_lint)] +pub struct PrivateInPublicLint<'a> { + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5560d44aa0d..390d6f5a856 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -23,7 +23,7 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; use rustc_middle::span_bug; -use rustc_middle::thir::abstract_const::Node as ACNode; +use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst, Node as ACNode}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind}; @@ -32,15 +32,14 @@ use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; -use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst}; use std::marker::PhantomData; use std::ops::ControlFlow; use std::{cmp, fmt, mem}; use errors::{ - FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, - UnnamedItemIsPrivate, + FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, + InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate, }; //////////////////////////////////////////////////////////////////////////////// @@ -164,7 +163,7 @@ where tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, ) -> ControlFlow<V::BreakTy> { - const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root(tcx) { + walk_abstract_const(tcx, ct, |node| match node.root(tcx) { ACNode::Leaf(leaf) => self.visit_const(leaf), ACNode::Cast(_, _, ty) => self.visit_ty(ty), ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { @@ -1717,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { if self.leaks_private_dep(def_id) { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, self.tcx.hir().local_def_id_to_hir_id(self.item_def_id), self.tcx.def_span(self.item_def_id.to_def_id()), - |lint| { - lint.build(&format!( - "{} `{}` from private dependency '{}' in public \ - interface", - kind, - descr, - self.tcx.crate_name(def_id.krate) - )) - .emit(); + FromPrivateDependencyInPublicInterface { + kind, + descr: descr.to_string(), + krate: self.tcx.crate_name(def_id.krate), }, ); } @@ -1755,11 +1749,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { } }; let span = self.tcx.def_span(self.item_def_id.to_def_id()); + let descr = descr.to_string(); if self.has_old_errors || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { - let descr = descr.to_string(); let vis_span = self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); if kind == "trait" { @@ -1780,19 +1774,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { }); } } else { - let err_code = if kind == "trait" { "E0445" } else { "E0446" }; - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::PRIVATE_IN_PUBLIC, hir_id, span, - |lint| { - lint.build(&format!( - "{} (error {})", - format!("{} {} `{}` in public interface", vis_descr, kind, descr), - err_code - )) - .emit(); - }, + PrivateInPublicLint { vis_descr, kind, descr }, ); } } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 6fbafeb1d32..54774314313 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -6,7 +6,7 @@ use rustc_middle::mir; use rustc_middle::traits; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::subst::{GenericArg, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -385,6 +385,16 @@ impl<'tcx> Key for Ty<'tcx> { } } +impl<'tcx> Key for TyAndLayout<'tcx> { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { #[inline(always)] fn query_crate_is_local(&self) -> bool { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 7c1fdc4e306..eda61df7700 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -37,7 +37,7 @@ mod values; use self::values::Value; pub use rustc_query_system::query::QueryConfig; -pub(crate) use rustc_query_system::query::{QueryDescription, QueryVtable}; +pub(crate) use rustc_query_system::query::{QueryDescription, QueryVTable}; mod on_disk_cache; pub use on_disk_cache::OnDiskCache; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 4c25075327f..56fd90c9855 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -9,7 +9,6 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::mir::{self, interpret}; -use rustc_middle::thir; use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_query_system::dep_graph::DepContext; @@ -766,7 +765,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> } } -impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [thir::abstract_const::Node<'tcx>] { +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [ty::abstract_const::Node<'tcx>] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 333dc5aa668..eda4401c81d 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -340,11 +340,11 @@ macro_rules! define_queries { #[inline] fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) -> - QueryVtable<QueryCtxt<$tcx>, Self::Key, Self::Value> + QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value> { let compute = get_provider!([$($modifiers)*][tcx, $name, key]); let cache_on_disk = Self::cache_on_disk(tcx.tcx, key); - QueryVtable { + QueryVTable { anon: is_anon!([$($modifiers)*]), eval_always: is_eval_always!([$($modifiers)*]), dep_kind: dep_graph::DepKind::$name, diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index c6210095b60..162c274d8a2 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -79,8 +79,8 @@ impl<K: DepKind> DepNode<K> { #[cfg(debug_assertions)] { if !tcx.fingerprint_style(kind).reconstructible() - && (tcx.sess().opts.debugging_opts.incremental_info - || tcx.sess().opts.debugging_opts.query_dep_graph) + && (tcx.sess().opts.unstable_opts.incremental_info + || tcx.sess().opts.unstable_opts.query_dep_graph) { tcx.dep_graph().register_dep_node_debug_str(dep_node, || arg.to_debug_str(tcx)); } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index e7026096e7b..8ff56132749 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -347,7 +347,7 @@ impl<K: DepKind> DepGraph<K> { let current_fingerprint = hash_result.map(|f| dcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, &result))); - let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks; + let print_status = cfg!(debug_assertions) && dcx.sess().opts.unstable_opts.dep_tasks; // Intern the new `DepNode`. let (dep_node_index, prev_and_color) = data.current.intern_node( diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 843f6f9d703..217fac341ed 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -24,7 +24,7 @@ pub struct StableHashingContext<'a> { cstore: &'a dyn CrateStore, source_span: &'a IndexVec<LocalDefId, Span>, // The value of `-Z incremental-ignore-spans`. - // This field should only be used by `debug_opts_incremental_ignore_span` + // 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 @@ -57,14 +57,14 @@ impl<'a> StableHashingContext<'a> { always_ignore_spans: bool, ) -> Self { let hash_spans_initial = - !always_ignore_spans && !sess.opts.debugging_opts.incremental_ignore_spans; + !always_ignore_spans && !sess.opts.unstable_opts.incremental_ignore_spans; StableHashingContext { body_resolver: BodyResolver::Forbidden, definitions, cstore, source_span, - incremental_ignore_spans: sess.opts.debugging_opts.incremental_ignore_spans, + incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, caching_source_map: None, raw_source_map: sess.source_map(), hashing_controls: HashingControls { hash_spans: hash_spans_initial }, @@ -186,7 +186,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { } #[inline] - fn debug_opts_incremental_ignore_spans(&self) -> bool { + fn unstable_opts_incremental_ignore_spans(&self) -> bool { self.incremental_ignore_spans } diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 7ca668f8a1f..964914a1326 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -19,7 +19,7 @@ pub trait QueryConfig { type Stored: Clone; } -pub struct QueryVtable<CTX: QueryContext, K, V> { +pub struct QueryVTable<CTX: QueryContext, K, V> { pub anon: bool, pub dep_kind: CTX::DepKind, pub eval_always: bool, @@ -31,7 +31,7 @@ pub struct QueryVtable<CTX: QueryContext, K, V> { pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>, } -impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> { +impl<CTX: QueryContext, K, V> QueryVTable<CTX, K, V> { pub(crate) fn to_dep_node(&self, tcx: CTX::DepContext, key: &K) -> DepNode<CTX::DepKind> where K: crate::dep_graph::DepNodeParams<CTX::DepContext>, @@ -69,7 +69,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryConfig { CTX: 'a; // Don't use this method to compute query results, instead use the methods on TyCtxt - fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVtable<CTX, Self::Key, Self::Value>; + fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVTable<CTX, Self::Key, Self::Value>; fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool; } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index f698a853d1e..fb2258434f4 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -12,7 +12,7 @@ pub use self::caches::{ }; mod config; -pub use self::config::{QueryConfig, QueryDescription, QueryVtable}; +pub use self::config::{QueryConfig, QueryDescription, QueryVTable}; use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index bbcd00be943..5e8ea07d00f 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -4,7 +4,7 @@ use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams}; use crate::query::caches::QueryCache; -use crate::query::config::{QueryDescription, QueryVtable}; +use crate::query::config::{QueryDescription, QueryVTable}; use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame}; use rustc_data_structures::fingerprint::Fingerprint; @@ -331,7 +331,7 @@ fn try_execute_query<CTX, C>( span: Span, key: C::Key, dep_node: Option<DepNode<CTX::DepKind>>, - query: &QueryVtable<CTX, C::Key, C::Value>, + query: &QueryVTable<CTX, C::Key, C::Value>, ) -> (C::Stored, Option<DepNodeIndex>) where C: QueryCache, @@ -368,7 +368,7 @@ fn execute_job<CTX, K, V>( tcx: CTX, key: K, mut dep_node_opt: Option<DepNode<CTX::DepKind>>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, job_id: QueryJobId, ) -> (V, DepNodeIndex) where @@ -437,7 +437,7 @@ fn try_load_from_disk_and_cache_in_memory<CTX, K, V>( tcx: CTX, key: &K, dep_node: &DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) -> Option<(V, DepNodeIndex)> where K: Clone, @@ -467,7 +467,7 @@ where if let Some(result) = result { if std::intrinsics::unlikely( - tcx.dep_context().sess().opts.debugging_opts.query_dep_graph, + tcx.dep_context().sess().opts.unstable_opts.query_dep_graph, ) { dep_graph.mark_debug_loaded_from_disk(*dep_node) } @@ -486,7 +486,7 @@ where // give us some coverage of potential bugs though. let try_verify = prev_fingerprint.as_value().1 % 32 == 0; if std::intrinsics::unlikely( - try_verify || tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich, + try_verify || tcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich, ) { incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query); } @@ -530,7 +530,7 @@ fn incremental_verify_ich<CTX, K, V: Debug>( tcx: CTX::DepContext, result: &V, dep_node: &DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) where CTX: QueryContext, { @@ -642,7 +642,7 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D fn ensure_must_run<CTX, K, V>( tcx: CTX, key: &K, - query: &QueryVtable<CTX, K, V>, + query: &QueryVTable<CTX, K, V>, ) -> (bool, Option<DepNode<CTX::DepKind>>) where K: crate::dep_graph::DepNodeParams<CTX::DepContext>, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 6631470f219..e955a1798b7 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -524,7 +524,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let crate_root = self.r.resolve_crate_root(source.ident); let crate_name = match crate_root.kind { ModuleKind::Def(.., name) => name, - ModuleKind::Block(..) => unreachable!(), + ModuleKind::Block => unreachable!(), }; // HACK(eddyb) unclear how good this is, but keeping `$crate` // in `source` breaks `src/test/ui/imports/import-crate-var.rs`, @@ -936,7 +936,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { if self.block_needs_anonymous_module(block) { let module = self.r.new_module( Some(parent), - ModuleKind::Block(block.id), + ModuleKind::Block, expansion.to_expn_id(), block.span, parent.no_implicit_prelude, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 52706fbb9e6..66641fb2cb2 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -259,7 +259,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_expr(&mut self, expr: &'a Expr) { let parent_def = match expr.kind { ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id), - ExprKind::Closure(_, asyncness, ..) => { + ExprKind::Closure(_, _, asyncness, ..) => { // Async closures desugar to closures inside of closures, so // we must create two defs. let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2851b08cd93..22a307a15ed 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -163,7 +163,7 @@ impl<'a> Resolver<'a> { let container = match parent.kind { ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), - ModuleKind::Block(..) => "block", + ModuleKind::Block => "block", }; let old_noun = match old_binding.is_import() { @@ -508,7 +508,7 @@ impl<'a> Resolver<'a> { E0401, "can't use generic parameters from outer function", ); - err.span_label(span, "use of generic parameter from outer function".to_string()); + err.span_label(span, "use of generic parameter from outer function"); let sm = self.session.source_map(); match outer_res { @@ -565,8 +565,7 @@ impl<'a> Resolver<'a> { } else if let Some(sp) = sm.generate_fn_name_span(span) { err.span_label( sp, - "try adding a local generic parameter in this method instead" - .to_string(), + "try adding a local generic parameter in this method instead", ); } else { err.help("try using a local generic parameter instead"); @@ -990,7 +989,7 @@ impl<'a> Resolver<'a> { E0735, "generic parameters cannot use `Self` in their defaults" ); - err.span_label(span, "`Self` in generic parameter default".to_string()); + err.span_label(span, "`Self` in generic parameter default"); err } ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { @@ -1627,7 +1626,7 @@ impl<'a> Resolver<'a> { "{}{} `{}` defined here", prefix, suggestion.res.descr(), - suggestion.candidate.as_str(), + suggestion.candidate, ), ); } @@ -2024,7 +2023,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { debug!("make_path_suggestion: span={:?} path={:?}", span, path); match (path.get(0), path.get(1)) { @@ -2059,12 +2058,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); - if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } + if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggests a missing `crate::` if that resolves to an correct module. @@ -2078,7 +2077,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; let result = self.r.maybe_resolve_path(&path, None, parent_scope); @@ -2086,12 +2085,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let PathResult::Module(..) = result { Some(( path, - vec![ + Some( "`use` statements changed in Rust 2018; read more at \ <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\ clarity.html>" .to_string(), - ], + ), )) } else { None @@ -2109,12 +2108,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; let result = self.r.maybe_resolve_path(&path, None, parent_scope); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); - if let PathResult::Module(..) = result { Some((path, Vec::new())) } else { None } + if let PathResult::Module(..) = result { Some((path, None)) } else { None } } /// Suggests a missing external crate name if that resolves to an correct module. @@ -2131,7 +2130,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { &mut self, mut path: Vec<Segment>, parent_scope: &ParentScope<'b>, - ) -> Option<(Vec<Segment>, Vec<String>)> { + ) -> Option<(Vec<Segment>, Option<String>)> { if path[1].ident.span.rust_2015() { return None; } @@ -2152,7 +2151,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { name, path, result ); if let PathResult::Module(..) = result { - return Some((path, Vec::new())); + return Some((path, None)); } } @@ -2176,7 +2175,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import: &'b Import<'b>, module: ModuleOrUniformRoot<'b>, ident: Ident, - ) -> Option<(Option<Suggestion>, Vec<String>)> { + ) -> Option<(Option<Suggestion>, Option<String>)> { let ModuleOrUniformRoot::Module(mut crate_module) = module else { return None; }; @@ -2288,12 +2287,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { String::from("a macro with this name exists at the root of the crate"), Applicability::MaybeIncorrect, )); - let note = vec![ - "this could be because a macro annotated with `#[macro_export]` will be exported \ - at the root of the crate instead of the module where it is defined" - .to_string(), - ]; - Some((suggestion, note)) + Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \ + at the root of the crate instead of the module where it is defined" + .to_string()))) } else { None } @@ -2603,12 +2599,12 @@ fn show_candidates( .skip(1) .all(|(_, descr, _, _)| descr == descr_first) { - descr_first.to_string() + descr_first } else { - "item".to_string() + "item" }; let plural_descr = - if descr.ends_with("s") { format!("{}es", descr) } else { format!("{}s", descr) }; + if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) }; let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr); let mut has_colon = false; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0cc6d05d1d0..6e678288142 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -218,7 +218,7 @@ impl<'a> Resolver<'a> { return Some((self.expn_def_scope(ctxt.remove_mark()), None)); } - if let ModuleKind::Block(..) = module.kind { + if let ModuleKind::Block = module.kind { return Some((module.parent.unwrap().nearest_item_scope(), None)); } @@ -333,7 +333,7 @@ impl<'a> Resolver<'a> { }; match module.kind { - ModuleKind::Block(..) => {} // We can see through blocks + ModuleKind::Block => {} // We can see through blocks _ => break, } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index e6060ad4665..b89273990d8 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -336,7 +336,7 @@ impl<'a> Resolver<'a> { struct UnresolvedImportError { span: Span, label: Option<String>, - note: Vec<String>, + note: Option<String>, suggestion: Option<Suggestion>, } @@ -427,7 +427,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let err = UnresolvedImportError { span: import.span, label: None, - note: Vec::new(), + note: None, suggestion: None, }; if path.contains("::") { @@ -463,10 +463,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg); - if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() { - for message in note { - diag.note(message); - } + if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() { + diag.note(note); } for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) { @@ -644,7 +642,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { None => UnresolvedImportError { span, label: Some(label), - note: Vec::new(), + note: None, suggestion, }, }; @@ -686,7 +684,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { return Some(UnresolvedImportError { span: import.span, label: Some(String::from("cannot glob-import a module into itself")), - note: Vec::new(), + note: None, suggestion: None, }); } @@ -830,7 +828,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let (suggestion, note) = match self.check_for_module_export_macro(import, module, ident) { Some((suggestion, note)) => (suggestion.or(lev_suggestion), note), - _ => (lev_suggestion, Vec::new()), + _ => (lev_suggestion, None), }; let label = match module { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 098b5a0c92e..ed65100ae77 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_middle::middle::resolve_lifetime::Set1; use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -43,6 +44,10 @@ type IdentMap<T> = FxHashMap<Ident, T>; /// Map from the name in a pattern to its binding mode. type BindingMap = IdentMap<BindingInfo>; +use diagnostics::{ + ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind, +}; + #[derive(Copy, Clone, Debug)] struct BindingInfo { span: Span, @@ -257,8 +262,11 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box<dyn Foo>`). AnonymousReportError, - /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId, /* in_fn_return */ bool), + /// Replace all anonymous lifetimes by provided lifetime. + Elided(LifetimeRes), + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, } #[derive(Copy, Clone, Debug)] @@ -268,6 +276,7 @@ enum LifetimeBinderKind { WhereBound, Item, Function, + Closure, ImplBlock, } @@ -281,6 +290,7 @@ impl LifetimeBinderKind { Item => "item", ImplBlock => "impl block", Function => "function", + Closure => "closure", } } } @@ -520,6 +530,10 @@ struct DiagnosticMetadata<'ast> { /// When processing impl trait currently_processing_impl_trait: Option<(TraitRef, Ty)>, + + /// Accumulate the errors due to missed lifetime elision, + /// and report them all at once for each function. + current_elision_failures: Vec<MissingLifetime>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -538,11 +552,18 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// The current set of local scopes for lifetimes. lifetime_ribs: Vec<LifetimeRib>, + /// We are looking for lifetimes in an elision context. + /// The set contains all the resolutions that we encountered so far. + /// They will be used to determine the correct lifetime for the fn return type. + /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named + /// lifetimes. + lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>, + /// The trait that the current context can refer to. current_trait_ref: Option<(Module<'a>, TraitRef)>, /// Fields used to add information to diagnostic errors. - diagnostic_metadata: DiagnosticMetadata<'ast>, + diagnostic_metadata: Box<DiagnosticMetadata<'ast>>, /// State used to know whether to ignore resolution errors for function bodies. /// @@ -578,7 +599,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_anon_const(&mut self, constant: &'ast AnonConst) { // We deal with repeat expressions explicitly in `resolve_expr`. self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(constant, IsRepeatExpr::No); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(constant, IsRepeatExpr::No); + }) }) } fn visit_expr(&mut self, expr: &'ast Expr) { @@ -605,8 +628,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { TyKind::Rptr(None, _) => { // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with // NodeId `ty.id`. + // This span will be used in case of elision failure. let span = self.r.session.source_map().next_point(ty.span.shrink_to_lo()); self.resolve_elided_lifetime(ty.id, span); + visit::walk_ty(self, ty); } TyKind::Path(ref qself, ref path) => { self.diagnostic_metadata.current_type_path = Some(ty); @@ -632,8 +657,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| this.visit_path(&path, ty.id), ); - self.diagnostic_metadata.current_type_path = prev_ty; - return; + } else { + visit::walk_ty(self, ty) } } TyKind::ImplicitSelf => { @@ -647,9 +672,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { ) .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); + visit::walk_ty(self, ty) + } + TyKind::ImplTrait(..) => { + let candidates = self.lifetime_elision_candidates.take(); + visit::walk_ty(self, ty); + self.lifetime_elision_candidates = candidates; } TyKind::TraitObject(ref bounds, ..) => { self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); + visit::walk_ty(self, ty) } TyKind::BareFn(ref bare_fn) => { let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo()); @@ -668,20 +700,26 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { binder: ty.id, report_in_path: false, }, - |this| walk_list!(this, visit_param, &bare_fn.decl.inputs), - ); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(ty.id, true), - |this| this.visit_fn_ret_ty(&bare_fn.decl.output), + |this| { + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + bare_fn + .decl + .inputs + .iter() + .map(|Param { ty, .. }| (None, &**ty)), + &bare_fn.decl.output, + ) + }, ); }, - ); - self.diagnostic_metadata.current_trait_object = prev; - return; + ) } - _ => (), + _ => visit::walk_ty(self, ty), } - visit::walk_ty(self, ty); self.diagnostic_metadata.current_trait_object = prev; self.diagnostic_metadata.current_type_path = prev_ty; } @@ -755,15 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { | FnKind::Fn(_, _, sig, _, generics, None) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), - |this| walk_list!(this, visit_param, &sig.decl.inputs), - ); self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, true), - |this| this.visit_fn_ret_ty(&sig.decl.output), + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + ) + }, ); return; } @@ -788,24 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let declaration = &sig.decl; let async_node_id = sig.header.asyncness.opt_return_id(); - // Argument-position elided lifetimes must be transformed into fresh - // generic parameters. This is especially useful for `async fn`, where - // these fresh generic parameters can be applied to the opaque `impl Trait` - // return type. - let rib = if async_node_id.is_some() { - // Only emit a hard error for `async fn`, since this kind of - // elision has always been allowed in regular `fn`s. + this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: fn_id, - report_in_path: true, - } - } else { - LifetimeRibKind::AnonymousPassThrough(fn_id, false) - }; - this.with_lifetime_rib( - rib, - // Add each argument to the rib. - |this| this.resolve_params(&declaration.inputs), + report_in_path: async_node_id.is_some(), + }, + |this| { + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + ) + }, ); // Construct the list of in-scope lifetime parameters for async lowering. @@ -844,23 +884,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .insert(async_node_id, extra_lifetime_params); } - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough( - // For async fn, the return type appears inside a custom - // `impl Future` RPIT, so we override the binder's id. - async_node_id.unwrap_or(fn_id), - true, - ), - |this| visit::walk_fn_ret_ty(this, &declaration.output), - ); - if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_block(body), ); @@ -868,19 +898,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.in_func_body = previous_state; } } - FnKind::Closure(declaration, body) => { - // We do not have any explicit generic lifetime parameter. - // FIXME(rfc3216): Change when implementing `for<>` bounds on closures. + FnKind::Closure(binder, declaration, body) => { + this.visit_closure_binder(binder); + this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: false, + match binder { + // We do not have any explicit generic lifetime parameter. + ClosureBinder::NotPresent => { + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + } + } + ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, // Add each argument to the rib. |this| this.resolve_params(&declaration.inputs), ); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, true), + match binder { + ClosureBinder::NotPresent => { + LifetimeRibKind::Elided(LifetimeRes::Infer) + } + ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, + }, |this| visit::walk_fn_ret_ty(this, &declaration.output), ); @@ -889,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id, false), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_expr(body), ); @@ -915,6 +956,18 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } } + fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) { + match b { + ClosureBinder::NotPresent => {} + ClosureBinder::For { generic_params, .. } => { + self.visit_generic_params( + &generic_params, + self.diagnostic_metadata.current_self_item.is_some(), + ); + } + } + } + fn visit_generic_arg(&mut self, arg: &'ast GenericArg) { debug!("visit_generic_arg({:?})", arg); let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true); @@ -1012,11 +1065,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { binder, report_in_path: false, }, - |this| walk_list!(this, visit_ty, &p_args.inputs), - ); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(binder, true), - |this| visit::walk_fn_ret_ty(this, &p_args.output), + |this| { + this.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, ); break; } @@ -1026,9 +1082,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_generic_args(self, path_span, args); break; } - LifetimeRibKind::AnonymousPassThrough(..) - | LifetimeRibKind::AnonymousCreateParameter { .. } + LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure | LifetimeRibKind::AnonConst | LifetimeRibKind::ConstGeneric => {} } @@ -1133,8 +1190,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, label_ribs: Vec::new(), lifetime_ribs: Vec::new(), + lifetime_elision_candidates: None, current_trait_ref: None, - diagnostic_metadata: DiagnosticMetadata::default(), + diagnostic_metadata: Box::new(DiagnosticMetadata::default()), // errors at module scope should always be reported in_func_body: false, lifetime_uses: Default::default(), @@ -1339,7 +1397,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { work: impl FnOnce(&mut Self) -> T, ) -> T { self.lifetime_ribs.push(LifetimeRib::new(kind)); + let outer_elision_candidates = self.lifetime_elision_candidates.take(); let ret = work(self); + self.lifetime_elision_candidates = outer_elision_candidates; self.lifetime_ribs.pop(); ret } @@ -1349,7 +1409,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let ident = lifetime.ident; if ident.name == kw::StaticLifetime { - self.record_lifetime_res(lifetime.id, LifetimeRes::Static); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Static, + LifetimeElisionCandidate::Named, + ); return; } @@ -1362,7 +1426,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let rib = &self.lifetime_ribs[i]; let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); if let LifetimeRes::Param { param, .. } = res { match self.lifetime_uses.entry(param) { @@ -1376,15 +1440,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Do not suggest eliding a lifetime where an anonymous // lifetime would be illegal. LifetimeRibKind::Item - | LifetimeRibKind::AnonymousPassThrough(_, true) - | LifetimeRibKind::AnonymousReportError => { - Some(LifetimeUseSet::Many) - } + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousPassThrough(_, false) - | LifetimeRibKind::AnonymousCreateParameter { .. } => { + LifetimeRibKind::AnonymousCreateParameter { .. } => { Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) } + // Only report if eliding the lifetime would have the same + // semantics. + LifetimeRibKind::Elided(r) => Some(if res == r { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }), LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => None, @@ -1406,12 +1474,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Item => break, LifetimeRibKind::ConstGeneric => { self.emit_non_static_lt_in_const_generic_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } LifetimeRibKind::AnonConst => { self.maybe_emit_forbidden_non_static_lifetime_error(lifetime); - self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); return; } _ => {} @@ -1429,19 +1505,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } self.emit_undeclared_lifetime_error(lifetime, outer_res); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); + let missing_lifetime = MissingLifetime { + id: lifetime.id, + span: lifetime.ident.span, + kind: if elided { + MissingLifetimeKind::Ampersand + } else { + MissingLifetimeKind::Underscore + }, + count: 1, + }; + let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for i in (0..self.lifetime_ribs.len()).rev() { let rib = &mut self.lifetime_ribs[i]; + debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder); - self.record_lifetime_res(lifetime.id, res); + self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } LifetimeRibKind::AnonymousReportError => { @@ -1463,14 +1551,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(lifetime.ident.span, note) .emit(); - self.record_lifetime_res(lifetime.id, LifetimeRes::Error); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } - LifetimeRibKind::AnonymousPassThrough(node_id, _) => { - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: node_id, elided }, - ); + LifetimeRibKind::Elided(res) => { + self.record_lifetime_res(lifetime.id, res, elision_candidate); + return; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } LifetimeRibKind::Item => break, @@ -1479,23 +1569,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | LifetimeRibKind::AnonConst => {} } } - // This resolution is wrong, it passes the work to HIR lifetime resolution. - // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic. - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided }, - ); + self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } #[tracing::instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); + let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; + self.record_lifetime_res( anchor_id, LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) }, + LifetimeElisionCandidate::Ignore, ); - - let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; self.resolve_anonymous_lifetime(<, true); } @@ -1564,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | PathSource::Struct | PathSource::TupleStruct(..) => false, }; + if !missing && !segment.has_generic_args { + continue; + } let elided_lifetime_span = if segment.has_generic_args { // If there are brackets, but not generic arguments, then use the opening bracket @@ -1580,43 +1670,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res( segment_id, LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end }, + LifetimeElisionCandidate::Ignore, ); if !missing { // Do not create a parameter for patterns and expressions. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - LifetimeRibKind::AnonymousPassThrough(binder, _) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res); - } - break; - } - // `LifetimeRes::Error`, which would usually be used in the case of - // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead, - // we simply resolve to an implicit lifetime, which will be checked later, at - // which point a suitable error will be emitted. - LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res); - } - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} - } + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Infer, + LifetimeElisionCandidate::Named, + ); } continue; } + let missing_lifetime = MissingLifetime { + id: node_ids.start, + span: elided_lifetime_span, + kind: if segment.has_generic_args { + MissingLifetimeKind::Comma + } else { + MissingLifetimeKind::Brackets + }, + count: expected_lifetimes, + }; let mut should_lint = true; for rib in self.lifetime_ribs.iter().rev() { match rib.kind { @@ -1647,23 +1725,47 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { should_lint = false; for id in node_ids { - self.record_lifetime_res(id, LifetimeRes::Error); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Named, + ); } break; } // Do not create a parameter for patterns and expressions. LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { + // Group all suggestions into the first record. + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { let res = self.create_fresh_lifetime(id, ident, binder); - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Named), + ); } break; } - // `PassThrough` is the normal case. - LifetimeRibKind::AnonymousPassThrough(binder, _) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; + LifetimeRibKind::Elided(res) => { + let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + res, + replace(&mut candidate, LifetimeElisionCandidate::Ignore), + ); + } + break; + } + LifetimeRibKind::ElisionFailure => { + self.diagnostic_metadata.current_elision_failures.push(missing_lifetime); + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } break; } @@ -1672,13 +1774,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // we simply resolve to an implicit lifetime, which will be checked later, at // which point a suitable error will be emitted. LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; for id in node_ids { - self.record_lifetime_res(id, res); + self.record_lifetime_res( + id, + LifetimeRes::Error, + LifetimeElisionCandidate::Ignore, + ); } + self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); break; } LifetimeRibKind::Generics { .. } @@ -1705,13 +1808,214 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[tracing::instrument(level = "debug", skip(self))] - fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) { + fn record_lifetime_res( + &mut self, + id: NodeId, + res: LifetimeRes, + candidate: LifetimeElisionCandidate, + ) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!( "lifetime {:?} resolved multiple times ({:?} before, {:?} now)", id, prev_res, res ) } + match res { + LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + candidates.insert(res, candidate); + } + } + LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + } + } + + #[tracing::instrument(level = "debug", skip(self))] + fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) { + if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { + panic!( + "lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)", + id, prev_res, res + ) + } + } + + /// Perform resolution of a function signature, accounting for lifetime elision. + #[tracing::instrument(level = "debug", skip(self, inputs))] + fn resolve_fn_signature( + &mut self, + fn_id: NodeId, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone, + output_ty: &'ast FnRetTy, + ) { + // Add each argument to the rib. + let elision_lifetime = self.resolve_fn_params(has_self, inputs); + debug!(?elision_lifetime); + + let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); + let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + LifetimeRibKind::Elided(*res) + } else { + LifetimeRibKind::ElisionFailure + }; + self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, &output_ty)); + let elision_failures = + replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures); + if !elision_failures.is_empty() { + let Err(failure_info) = elision_lifetime else { bug!() }; + self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + } + } + + /// Resolve inside function parameters and parameter types. + /// Returns the lifetime for elision in fn return type, + /// or diagnostic information in case of elision failure. + fn resolve_fn_params( + &mut self, + has_self: bool, + inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>, + ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> { + let outer_candidates = + replace(&mut self.lifetime_elision_candidates, Some(Default::default())); + + let mut elision_lifetime = None; + let mut lifetime_count = 0; + let mut parameter_info = Vec::new(); + + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + for (index, (pat, ty)) in inputs.enumerate() { + debug!(?pat, ?ty); + if let Some(pat) = pat { + self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + } + self.visit_ty(ty); + + if let Some(ref candidates) = self.lifetime_elision_candidates { + let new_count = candidates.len(); + let local_count = new_count - lifetime_count; + if local_count != 0 { + parameter_info.push(ElisionFnParameter { + index, + ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind { + Some(ident) + } else { + None + }, + lifetime_count: local_count, + span: ty.span, + }); + } + lifetime_count = new_count; + } + + // Handle `self` specially. + if index == 0 && has_self { + let self_lifetime = self.find_lifetime_for_self(ty); + if let Set1::One(lifetime) = self_lifetime { + elision_lifetime = Some(lifetime); + self.lifetime_elision_candidates = None; + } else { + self.lifetime_elision_candidates = Some(Default::default()); + lifetime_count = 0; + } + } + debug!("(resolving function / closure) recorded parameter"); + } + + let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates); + debug!(?all_candidates); + + if let Some(res) = elision_lifetime { + return Ok(res); + } + + // We do not have a `self` candidate, look at the full list. + let all_candidates = all_candidates.unwrap(); + if all_candidates.len() == 1 { + Ok(*all_candidates.first().unwrap().0) + } else { + let all_candidates = all_candidates + .into_iter() + .filter_map(|(_, candidate)| match candidate { + LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None, + LifetimeElisionCandidate::Missing(missing) => Some(missing), + }) + .collect(); + Err((all_candidates, parameter_info)) + } + } + + /// List all the lifetimes that appear in the provided type. + fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> { + struct SelfVisitor<'r, 'a> { + r: &'r Resolver<'a>, + impl_self: Option<Res>, + lifetime: Set1<LifetimeRes>, + } + + impl SelfVisitor<'_, '_> { + // Look for `self: &'a Self` - also desugared from `&'a self`, + // and if that matches, use it for elision and return early. + fn is_self_ty(&self, ty: &Ty) -> bool { + match ty.kind { + TyKind::ImplicitSelf => true, + TyKind::Path(None, _) => { + let path_res = self.r.partial_res_map[&ty.id].base_res(); + if let Res::SelfTy { .. } = path_res { + return true; + } + Some(path_res) == self.impl_self + } + _ => false, + } + } + } + + impl<'a> Visitor<'a> for SelfVisitor<'_, '_> { + fn visit_ty(&mut self, ty: &'a Ty) { + trace!("SelfVisitor considering ty={:?}", ty); + if let TyKind::Rptr(lt, ref mt) = ty.kind && self.is_self_ty(&mt.ty) { + let lt_id = if let Some(lt) = lt { + lt.id + } else { + let res = self.r.lifetimes_res_map[&ty.id]; + let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() }; + start + }; + let lt_res = self.r.lifetimes_res_map[<_id]; + trace!("SelfVisitor inserting res={:?}", lt_res); + self.lifetime.insert(lt_res); + } + visit::walk_ty(self, ty) + } + } + + let impl_self = self + .diagnostic_metadata + .current_self_type + .as_ref() + .and_then(|ty| { + if let TyKind::Path(None, _) = ty.kind { + self.r.partial_res_map.get(&ty.id) + } else { + None + } + }) + .map(|res| res.base_res()) + .filter(|res| { + // Permit the types that unambiguously always + // result in the same type constructor being used + // (it can't differ between `Self` and `self`). + matches!( + res, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_) + ) + }); + let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty }; + visitor.visit_ty(ty); + trace!("SelfVisitor found={:?}", visitor.lifetime); + visitor.lifetime } /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved @@ -1936,22 +2240,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { self.with_item_rib(|this| { - this.visit_ty(ty); - if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), - ); - } + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.visit_ty(ty); + }); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); + } + }); }); } @@ -2022,7 +2330,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { { diagnostics::signal_lifetime_shadowing(self.r.session, original, param.ident); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2033,7 +2341,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.report_error(param.ident.span, err); if let GenericParamKind::Lifetime = param.kind { // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } } @@ -2052,7 +2360,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "`'_` is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2067,7 +2375,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .span_label(param.ident.span, "'static is a reserved lifetime name") .emit(); // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_res(param.id, LifetimeRes::Error); + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } @@ -2079,7 +2387,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), GenericParamKind::Lifetime => { let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_res(param.id, res); + self.record_lifetime_param(param.id, res); function_lifetime_rib.bindings.insert(ident, (param.id, res)); continue; } @@ -2088,7 +2396,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let res = match kind { ItemRibKind(..) | AssocItemRibKind => Res::Def(def_kind, def_id.to_def_id()), NormalRibKind => Res::Err, - _ => bug!("Unexpected rib kind {:?}", kind), + _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), }; self.r.record_partial_res(param.id, PartialRes::new(res)); rib.bindings.insert(ident, res); @@ -2102,7 +2410,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[TypeNS].pop(); self.ribs[ValueNS].pop(); - self.lifetime_ribs.pop(); + let function_lifetime_rib = self.lifetime_ribs.pop().unwrap(); + + // Do not account for the parameters we just bound for function lifetime elision. + if let Some(ref mut candidates) = self.lifetime_elision_candidates { + for (_, res) in function_lifetime_rib.bindings.values() { + candidates.remove(res); + } + } if let LifetimeBinderKind::BareFnType | LifetimeBinderKind::WhereBound @@ -2200,20 +2515,26 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - None, - |this| this.visit_expr(expr), + self.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Infer), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }, ); } } AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } - AssocItemKind::TyAlias(box TyAlias { generics, .. }) => { - walk_assoc_item(self, generics, LifetimeBinderKind::Item, item); - } + AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self + .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + walk_assoc_item(this, generics, LifetimeBinderKind::Item, item) + }), AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") } @@ -2284,7 +2605,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { LifetimeRibKind::Generics { span: generics.span, binder: item_id, - kind: LifetimeBinderKind::ImplBlock + kind: LifetimeBinderKind::ImplBlock, }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. @@ -2304,7 +2625,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Register the trait definitions from here. if let Some(trait_id) = trait_id { - this.r.trait_impls.entry(trait_id).or_default().push(item_def_id); + this.r + .trait_impls + .entry(trait_id) + .or_default() + .push(item_def_id); } let item_def_id = item_def_id.to_def_id(); @@ -2323,21 +2648,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_generics(generics); // Resolve the items within the impl. - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false), - |this| { - this.with_current_self_type(self_type, |this| { - this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { - debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); - for item in impl_items { - this.resolve_impl_item(&**item); - } - }); - }); - }, - ); + this.with_current_self_type(self_type, |this| { + this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { + debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); + for item in impl_items { + this.resolve_impl_item(&**item); + } + }); + }); }); }, - ); + ) }, ); }); @@ -2368,8 +2689,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| { - this.visit_expr(expr) + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) }); } } @@ -2412,18 +2738,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { kind: LifetimeBinderKind::Item, }, |this| { - // If this is a trait impl, ensure the type - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - TypeNS, - item.span, - |i, s, c| TypeNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + // If this is a trait impl, ensure the type + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + TypeNS, + item.span, + |i, s, c| TypeNotMemberOfTrait(i, s, c), + ); - visit::walk_assoc_item(this, item, AssocCtxt::Impl) + visit::walk_assoc_item(this, item, AssocCtxt::Impl) + }); }, ); } @@ -3519,7 +3847,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. - ExprKind::Closure(_, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => { + ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => { self.with_rib(ValueNS, NormalRibKind, |this| { this.with_label_rib(ClosureOrAsyncRibKind, |this| { // Resolve arguments: @@ -3539,6 +3867,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } // For closures, ClosureOrAsyncRibKind is added in visit_fn + ExprKind::Closure(ClosureBinder::For { ref generic_params, span }, ..) => { + self.with_generic_param_rib( + &generic_params, + NormalRibKind, + LifetimeRibKind::Generics { + binder: expr.id, + kind: LifetimeBinderKind::Closure, + span, + }, + |this| visit::walk_expr(this, expr), + ); + } ExprKind::Closure(..) => visit::walk_expr(self, expr), ExprKind::Async(..) => { self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr)); @@ -3546,7 +3886,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Repeat(ref elem, ref ct) => { self.visit_expr(elem); self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| { - this.resolve_anon_const(ct, IsRepeatExpr::Yes) + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { + this.resolve_anon_const(ct, IsRepeatExpr::Yes) + }) }); } ExprKind::ConstBlock(ref ct) => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 677d7036b2f..6b49c6b1ac6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,7 +1,6 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; -use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; +use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; @@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment}; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, - NodeId, Path, Ty, TyKind, + NodeId, Path, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust::path_segment_to_string; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -20,7 +19,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -29,7 +28,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span}; use std::iter; use std::ops::Deref; @@ -59,41 +58,6 @@ impl AssocSuggestion { } } -pub(crate) enum MissingLifetimeSpot<'tcx> { - Generics(&'tcx hir::Generics<'tcx>), - HigherRanked { span: Span, span_type: ForLifetimeSpanType }, - Static, -} - -pub(crate) enum ForLifetimeSpanType { - BoundEmpty, - BoundTail, - TypeEmpty, - TypeTail, -} - -impl ForLifetimeSpanType { - pub(crate) fn descr(&self) -> &'static str { - match self { - Self::BoundEmpty | Self::BoundTail => "bound", - Self::TypeEmpty | Self::TypeTail => "type", - } - } - - pub(crate) fn suggestion(&self, sugg: &str) -> String { - match self { - Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), - Self::BoundTail | Self::TypeTail => format!(", {}", sugg), - } - } -} - -impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> { - fn into(self) -> MissingLifetimeSpot<'tcx> { - MissingLifetimeSpot::Generics(self) - } -} - fn is_self_type(path: &[Segment], namespace: Namespace) -> bool { namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper } @@ -118,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str (variant_path_string, enum_path_string) } +/// Description of an elided lifetime. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) struct MissingLifetime { + /// Used to overwrite the resolution with the suggestion, to avoid cascasing errors. + pub id: NodeId, + /// Where to suggest adding the lifetime. + pub span: Span, + /// How the lifetime was introduced, to have the correct space and comma. + pub kind: MissingLifetimeKind, + /// Number of elided lifetimes, used for elision in path. + pub count: usize, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub(super) enum MissingLifetimeKind { + /// An explicit `'_`. + Underscore, + /// An elided lifetime `&' ty`. + Ampersand, + /// An elided lifetime in brackets with written brackets. + Comma, + /// An elided lifetime with elided brackets. + Brackets, +} + +/// Description of the lifetimes appearing in a function parameter. +/// This is used to provide a literal explanation to the elision failure. +#[derive(Clone, Debug)] +pub(super) struct ElisionFnParameter { + /// The index of the argument in the original definition. + pub index: usize, + /// The name of the argument if it's a simple ident. + pub ident: Option<Ident>, + /// The number of lifetimes in the parameter. + pub lifetime_count: usize, + /// The span of the parameter. + pub span: Span, +} + +/// Description of lifetimes that appear as candidates for elision. +/// This is used to suggest introducing an explicit lifetime. +#[derive(Debug)] +pub(super) enum LifetimeElisionCandidate { + /// This is not a real lifetime. + Ignore, + /// There is a named lifetime, we won't suggest anything. + Named, + Missing(MissingLifetime), +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { @@ -1430,7 +1444,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Items from this module self.r.add_module_candidates(module, &mut names, &filter_fn); - if let ModuleKind::Block(..) = module.kind { + if let ModuleKind::Block = module.kind { // We can see through blocks } else { // Items from the prelude @@ -1999,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(lifetime_ref.ident.span, "undeclared lifetime"); err }; - let mut suggest_note = true; + self.suggest_introducing_lifetime( + &mut err, + Some(lifetime_ref.ident.name.as_str()), + |err, _, span, message, suggestion| { + err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + true + }, + ); + err.emit(); + } + fn suggest_introducing_lifetime( + &self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + name: Option<&str>, + suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool, + ) { + let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { + let mut should_continue = true; match rib.kind { LifetimeRibKind::Generics { binder: _, span, kind } => { - if !span.can_be_used_for_suggestions() && suggest_note { + if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( span, &format!( "lifetime `{}` is missing in item created through this procedural macro", - lifetime_ref.ident, + name, ), ); continue; @@ -2026,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - lifetime_ref.ident, + name.unwrap_or("'a"), if higher_ranked { " " } else { "" }, ); (span, sugg) } else { let span = self.r.session.source_map().span_through_char(span, '<').shrink_to_hi(); - let sugg = format!("{}, ", lifetime_ref.ident); + let sugg = format!("{}, ", name.unwrap_or("'a")); (span, sugg) }; if higher_ranked { - err.span_suggestion( - span, - &format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - kind.descr(), - lifetime_ref - ), - sugg, - Applicability::MaybeIncorrect, + let message = format!( + "consider making the {} lifetime-generic with a new `{}` lifetime", + kind.descr(), + name.unwrap_or("'a"), ); + should_continue = suggest(err, true, span, &message, sugg); err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", ); + } else if let Some(name) = name { + let message = format!("consider introducing lifetime `{}` here", name); + should_continue = suggest(err, false, span, &message, sugg); } else { - err.span_suggestion( - span, - &format!("consider introducing lifetime `{}` here", lifetime_ref.ident), - sugg, - Applicability::MaybeIncorrect, - ); + let message = format!("consider introducing a named lifetime parameter"); + should_continue = suggest(err, false, span, &message, sugg); } } LifetimeRibKind::Item => break, _ => {} } + if !should_continue { + break; + } } - - err.emit(); } pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { @@ -2101,547 +2128,209 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .emit(); } } -} -/// Report lifetime/lifetime shadowing as an error. -pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { - let mut err = struct_span_err!( - sess, - shadower.span, - E0496, - "lifetime name `{}` shadows a lifetime name that is already in scope", - orig.name, - ); - err.span_label(orig.span, "first declared here"); - err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); - err.emit(); -} - -/// Shadowing involving a label is only a warning for historical reasons. -//FIXME: make this a proper lint. -pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { - let name = shadower.name; - let shadower = shadower.span; - let mut err = sess.struct_span_warn( - shadower, - &format!("label name `{}` shadows a label name that is already in scope", name), - ); - err.span_label(orig, "first declared here"); - err.span_label(shadower, format!("label `{}` already in scope", name)); - err.emit(); -} - -impl<'tcx> LifetimeContext<'_, 'tcx> { pub(crate) fn report_missing_lifetime_specifiers( - &self, - spans: Vec<Span>, - count: usize, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - self.tcx.sess, + &mut self, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) -> ErrorGuaranteed { + let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum(); + let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + + let mut err = struct_span_err!( + self.r.session, spans, E0106, "missing lifetime specifier{}", - pluralize!(count) - ) + pluralize!(num_lifetimes) + ); + self.add_missing_lifetime_specifiers_label( + &mut err, + lifetime_refs, + function_param_lifetimes, + ); + err.emit() } - /// Returns whether to add `'static` lifetime to the suggested lifetime list. - pub(crate) fn report_elision_failure( - &self, - diag: &mut Diagnostic, - params: &[ElisionFailureInfo], - ) -> bool { - let mut m = String::new(); - let len = params.len(); + pub(crate) fn add_missing_lifetime_specifiers_label( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + lifetime_refs: Vec<MissingLifetime>, + function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>, + ) { + for < in &lifetime_refs { + err.span_label( + lt.span, + format!( + "expected {} lifetime parameter{}", + if lt.count == 1 { "named".to_string() } else { lt.count.to_string() }, + pluralize!(lt.count), + ), + ); + } - let elided_params: Vec<_> = - params.iter().cloned().filter(|info| info.lifetime_count > 0).collect(); + let mut in_scope_lifetimes: Vec<_> = self + .lifetime_ribs + .iter() + .rev() + .take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item)) + .flat_map(|rib| rib.bindings.iter()) + .map(|(&ident, &res)| (ident, res)) + .filter(|(ident, _)| ident.name != kw::UnderscoreLifetime) + .collect(); + debug!(?in_scope_lifetimes); - let elided_len = elided_params.len(); + debug!(?function_param_lifetimes); + if let Some((param_lifetimes, params)) = &function_param_lifetimes { + let elided_len = param_lifetimes.len(); + let num_params = params.len(); - for (i, info) in elided_params.into_iter().enumerate() { - let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } = - info; + let mut m = String::new(); - diag.span_label(span, ""); - let help_name = if let Some(ident) = - parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident()) - { - format!("`{}`", ident) - } else { - format!("argument {}", index + 1) - }; + for (i, info) in params.iter().enumerate() { + let ElisionFnParameter { ident, index, lifetime_count, span } = *info; + debug_assert_ne!(lifetime_count, 0); - m.push_str( - &(if n == 1 { - help_name + err.span_label(span, ""); + + if i != 0 { + if i + 1 < num_params { + m.push_str(", "); + } else if num_params == 2 { + m.push_str(" or "); + } else { + m.push_str(", or "); + } + } + + let help_name = if let Some(ident) = ident { + format!("`{}`", ident) } else { - format!( - "one of {}'s {} {}lifetimes", - help_name, - n, - if have_bound_regions { "free " } else { "" } - ) - })[..], - ); + format!("argument {}", index + 1) + }; - if elided_len == 2 && i == 0 { - m.push_str(" or "); - } else if i + 2 == elided_len { - m.push_str(", or "); - } else if i != elided_len - 1 { - m.push_str(", "); + if lifetime_count == 1 { + m.push_str(&help_name[..]) + } else { + m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..]) + } } - } - if len == 0 { - diag.help( - "this function's return type contains a borrowed value, \ + if num_params == 0 { + err.help( + "this function's return type contains a borrowed value, \ but there is no value for it to be borrowed from", - ); - true - } else if elided_len == 0 { - diag.help( - "this function's return type contains a borrowed value with \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if elided_len == 0 { + err.help( + "this function's return type contains a borrowed value with \ an elided lifetime, but the lifetime cannot be derived from \ the arguments", - ); - true - } else if elided_len == 1 { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + ); + if in_scope_lifetimes.is_empty() { + in_scope_lifetimes = vec![( + Ident::with_dummy_span(kw::StaticLifetime), + (DUMMY_NODE_ID, LifetimeRes::Static), + )]; + } + } else if num_params == 1 { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say which {} it is borrowed from", - m - )); - false - } else { - diag.help(&format!( - "this function's return type contains a borrowed value, \ + m + )); + } else { + err.help(&format!( + "this function's return type contains a borrowed value, \ but the signature does not say whether it is borrowed from {}", - m - )); - false + m + )); + } } - } - pub(crate) fn is_trait_ref_fn_scope( - &mut self, - trait_ref: &'tcx hir::PolyTraitRef<'tcx>, - ) -> bool { - if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res { - if [ - self.tcx.lang_items().fn_once_trait(), - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - ] - .contains(&Some(did)) - { - let (span, span_type) = if let Some(bound) = - trait_ref.bound_generic_params.iter().rfind(|param| { - matches!( - param.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) { - (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail) - } else { - (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - return true; - } + let existing_name = match &in_scope_lifetimes[..] { + [] => Symbol::intern("'a"), + [(existing, _)] => existing.name, + _ => Symbol::intern("'lifetime"), }; - false - } - - pub(crate) fn add_missing_lifetime_specifiers_label( - &self, - err: &mut Diagnostic, - mut spans_with_counts: Vec<(Span, usize)>, - in_scope_lifetimes: FxIndexSet<LocalDefId>, - params: Option<&[ElisionFailureInfo]>, - ) { - let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes - .iter() - .filter_map(|def_id| { - let name = self.tcx.item_name(def_id.to_def_id()); - let span = self.tcx.def_ident_span(def_id.to_def_id())?; - Some((name, span)) - }) - .filter(|&(n, _)| n != kw::UnderscoreLifetime) - .unzip(); - if let Some(params) = params { - // If there's no lifetime available, suggest `'static`. - if self.report_elision_failure(err, params) && lifetime_names.is_empty() { - lifetime_names.insert(kw::StaticLifetime); + let mut spans_suggs: Vec<_> = Vec::new(); + let build_sugg = |lt: MissingLifetime| match lt.kind { + MissingLifetimeKind::Underscore => { + debug_assert_eq!(lt.count, 1); + (lt.span, existing_name.to_string()) } - } - let params = params.unwrap_or(&[]); - - let snippets: Vec<Option<String>> = spans_with_counts - .iter() - .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok()) - .collect(); - - // Empty generics are marked with a span of "<", but since from now on - // that information is in the snippets it can be removed from the spans. - for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) { - if snippet.as_deref() == Some("<") { - *span = span.shrink_to_hi(); + MissingLifetimeKind::Ampersand => { + debug_assert_eq!(lt.count, 1); + (lt.span.shrink_to_hi(), format!("{} ", existing_name)) } + MissingLifetimeKind::Comma => { + let sugg: String = std::iter::repeat([existing_name.as_str(), ", "]) + .take(lt.count) + .flatten() + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + MissingLifetimeKind::Brackets => { + let sugg: String = std::iter::once("<") + .chain( + std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "), + ) + .chain([">"]) + .collect(); + (lt.span.shrink_to_hi(), sugg) + } + }; + for < in &lifetime_refs { + spans_suggs.push(build_sugg(lt)); } - - for &(span, count) in &spans_with_counts { - err.span_label( - span, - format!( - "expected {} lifetime parameter{}", - if count == 1 { "named".to_string() } else { count.to_string() }, - pluralize!(count), - ), - ); - } - - let suggest_existing = - |err: &mut Diagnostic, - name: &str, - formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| { - if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = - self.missing_named_lifetime_spots.iter().rev().next() - { - // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest - // using `'a`, but also introduce the concept of HRLTs by suggesting - // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) - let mut introduce_suggestion = vec![]; - - let a_to_z_repeat_n = |n| { - (b'a'..=b'z').map(move |c| { - let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); - s - }) - }; - - // If all single char lifetime names are present, we wrap around and double the chars. - let lt_name = (1..) - .flat_map(a_to_z_repeat_n) - .find(|lt| !lifetime_names.contains(&Symbol::intern(<))) - .unwrap(); - let msg = format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - span_type.descr(), - lt_name, - ); - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - let for_sugg = span_type.suggestion(<_name); - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) - { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); - } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, stripped))); - } - } + debug!(?spans_suggs); + match in_scope_lifetimes.len() { + 0 => { + if let Some((param_lifetimes, _)) = function_param_lifetimes { + for lt in param_lifetimes { + spans_suggs.push(build_sugg(lt)) } - introduce_suggestion.push((*for_span, for_sugg)); - for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) { - if let Some(formatter) = formatter { - introduce_suggestion.push((*span, formatter(<_name))); - } - } - err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, - Applicability::MaybeIncorrect, - ); } - - let spans_suggs: Vec<_> = formatters - .into_iter() - .zip(spans_with_counts.iter()) - .filter_map(|(formatter, (span, _))| { - if let Some(formatter) = formatter { - Some((*span, formatter(name))) - } else { - None - } - }) - .collect(); - if spans_suggs.is_empty() { - // If all the spans come from macros, we cannot extract snippets and then - // `formatters` only contains None and `spans_suggs` is empty. - return; - } - err.multipart_suggestion_verbose( - &format!( - "consider using the `{}` lifetime", - lifetime_names.iter().next().unwrap() - ), - spans_suggs, - Applicability::MaybeIncorrect, - ); - }; - let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| { - for missing in self.missing_named_lifetime_spots.iter().rev() { - let mut introduce_suggestion = vec![]; - let msg; - let should_break; - introduce_suggestion.push(match missing { - MissingLifetimeSpot::Generics(generics) => { - if generics.span == DUMMY_SP { - // Account for malformed generics in the HIR. This shouldn't happen, - // but if we make a mistake elsewhere, mainly by keeping something in - // `missing_named_lifetime_spots` that we shouldn't, like associated - // `const`s or making a mistake in the AST lowering we would provide - // nonsensical suggestions. Guard against that by skipping these. - // (#74264) - continue; - } - msg = "consider introducing a named lifetime parameter".to_string(); - should_break = true; - if let Some(param) = generics.params.iter().find(|p| { - !matches!( - p.kind, - hir::GenericParamKind::Type { synthetic: true, .. } - | hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided - } - ) - }) { - (param.span.shrink_to_lo(), "'a, ".to_string()) - } else { - (generics.span, "<'a>".to_string()) - } - } - MissingLifetimeSpot::HigherRanked { span, span_type } => { - msg = format!( - "consider making the {} lifetime-generic with a new `'a` lifetime", - span_type.descr(), - ); - should_break = false; - err.note( - "for more information on higher-ranked polymorphism, visit \ - https://doc.rust-lang.org/nomicon/hrtb.html", - ); - (*span, span_type.suggestion("'a")) - } - MissingLifetimeSpot::Static => { - let mut spans_suggs = Vec::new(); - for ((span, count), snippet) in - spans_with_counts.iter().copied().zip(snippets.iter()) - { - let (span, sugg) = match snippet.as_deref() { - Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), - Some("'_") => (span, "'static".to_owned()), - Some(snippet) if !snippet.ends_with('>') => { - if snippet == "" { - ( - span, - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else if snippet == "<" || snippet == "(" { - ( - span.shrink_to_hi(), - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ) - } else { - ( - span.shrink_to_hi(), - format!( - "<{}>", - std::iter::repeat("'static") - .take(count) - .collect::<Vec<_>>() - .join(", "), - ), - ) - } - } - _ => continue, - }; - spans_suggs.push((span, sugg.to_string())); - } + self.suggest_introducing_lifetime( + err, + None, + |err, higher_ranked, span, message, intro_sugg| { err.multipart_suggestion_verbose( - "consider using the `'static` lifetime", - spans_suggs, + message, + std::iter::once((span, intro_sugg)) + .chain(spans_suggs.clone()) + .collect(), Applicability::MaybeIncorrect, ); - continue; - } - }); - - struct Lifetime(Span, String); - impl Lifetime { - fn is_unnamed(&self) -> bool { - self.1.starts_with('&') && !self.1.starts_with("&'") - } - fn is_underscore(&self) -> bool { - self.1.starts_with("&'_ ") - } - fn is_named(&self) -> bool { - self.1.starts_with("&'") - } - fn suggestion(&self, sugg: String) -> Option<(Span, String)> { - Some( - match ( - self.is_unnamed(), - self.is_underscore(), - self.is_named(), - sugg.starts_with('&'), - ) { - (true, _, _, false) => (self.span_unnamed_borrow(), sugg), - (true, _, _, true) => { - (self.span_unnamed_borrow(), sugg[1..].to_string()) - } - (_, true, _, false) => { - (self.span_underscore_borrow(), sugg.trim().to_string()) - } - (_, true, _, true) => { - (self.span_underscore_borrow(), sugg[1..].trim().to_string()) - } - (_, _, true, false) => { - (self.span_named_borrow(), sugg.trim().to_string()) - } - (_, _, true, true) => { - (self.span_named_borrow(), sugg[1..].trim().to_string()) - } - _ => return None, - }, - ) - } - fn span_unnamed_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo).with_hi(lo) - } - fn span_named_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - self.0.with_lo(lo) - } - fn span_underscore_borrow(&self) -> Span { - let lo = self.0.lo() + BytePos(1); - let hi = lo + BytePos(2); - self.0.with_lo(lo).with_hi(hi) - } - } - - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { - if let Some((span, sugg)) = - Lifetime(param.span, snippet).suggestion("'a ".to_string()) - { - introduce_suggestion.push((span, sugg)); - } - } - } - for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map( - |((span, _), sugg)| match &sugg { - Some(sugg) => Some((span, sugg.to_string())), - _ => None, + higher_ranked }, - ) { - let (span, sugg) = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .ok() - .and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone())) - .unwrap_or((span, sugg)); - introduce_suggestion.push((span, sugg.to_string())); - } + ); + } + 1 => { err.multipart_suggestion_verbose( - &msg, - introduce_suggestion, + &format!("consider using the `{}` lifetime", existing_name), + spans_suggs, Applicability::MaybeIncorrect, ); - if should_break { - break; - } - } - }; - let lifetime_names: Vec<_> = lifetime_names.iter().collect(); - match &lifetime_names[..] { - [name] => { - let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new(); - for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some(Box::new(|name| format!("&{} ", name))), - Some("'_") => Some(Box::new(|n| n.to_string())), - Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))), - Some("<") => Some(Box::new(move |n| { - std::iter::repeat(n).take(count).collect::<Vec<_>>().join(", ") - })), - Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::<Vec<_>>() - .join(", ") - ) - })), - _ => None, - }); + // Record as using the suggested resolution. + let (_, (_, res)) = in_scope_lifetimes[0]; + for < in &lifetime_refs { + self.r.lifetimes_res_map.insert(lt.id, res); } - suggest_existing(err, name.as_str(), suggs); } - [] => { - let mut suggs = Vec::new(); - for (snippet, (_, count)) in - snippets.iter().cloned().zip(spans_with_counts.iter().copied()) - { - suggs.push(match snippet.as_deref() { - Some("&") => Some("&'a ".to_string()), - Some("'_") => Some("'a".to_string()), - Some("") => { - Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join("")) - } - Some("<") => { - Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")) - } - Some(snippet) => Some(format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "), - )), - None => None, - }); - } - suggest_new(err, suggs); - } - lts if lts.len() > 1 => { + _ => { + let lifetime_spans: Vec<_> = + in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect(); err.span_note(lifetime_spans, "these named lifetimes are available to use"); - let mut spans_suggs: Vec<_> = Vec::new(); - for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) { - match snippet.as_deref() { - Some("") => spans_suggs.push((span, "'lifetime, ".to_string())), - Some("&") => spans_suggs - .push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())), - _ => {} - } - } - if spans_suggs.len() > 0 { // This happens when we have `Foo<T>` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. @@ -2652,7 +2341,34 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { ); } } - _ => unreachable!(), } } } + +/// Report lifetime/lifetime shadowing as an error. +pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) { + let mut err = struct_span_err!( + sess, + shadower.span, + E0496, + "lifetime name `{}` shadows a lifetime name that is already in scope", + orig.name, + ); + err.span_label(orig.span, "first declared here"); + err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name)); + err.emit(); +} + +/// Shadowing involving a label is only a warning for historical reasons. +//FIXME: make this a proper lint. +pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { + let name = shadower.name; + let shadower = shadower.span; + let mut err = sess.struct_span_warn( + shadower, + &format!("label name `{}` shadows a label name that is already in scope", name), + ); + err.span_label(orig, "first declared here"); + err.span_label(shadower, format!("label `{}` already in scope", name)); + err.emit(); +} diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 557dbecfabe..94460e33d8b 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,12 +1,11 @@ -// ignore-tidy-filelength -//! Name resolution for lifetimes. +//! Resolution of early vs late bound lifetimes. //! -//! Name resolution for lifetimes follows *much* simpler rules than the -//! full resolve. For example, lifetime names are never exported or -//! used between functions, and they operate in a purely top-down -//! way. Therefore, we break lifetime name resolution into a separate pass. +//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this +//! information, typechecking needs to transform the lifetime parameters into bound lifetimes. +//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit +//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file +//! is also responsible for assigning their semantics to implicit lifetimes in trait objects. -use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::struct_span_err; @@ -14,18 +13,16 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node}; -use rustc_hir::{GenericParamKind, HirIdMap}; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; +use rustc_middle::bug; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::Cow; -use std::cell::Cell; use std::fmt; use std::mem::take; @@ -34,8 +31,6 @@ trait RegionExt { fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); - fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region; - fn id(&self) -> Option<DefId>; fn shifted(self, amount: u32) -> Region; @@ -66,16 +61,9 @@ impl RegionExt for Region { (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) } - fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region { - let i = index.get(); - index.set(i + 1); - let depth = ty::INNERMOST; - Region::LateBoundAnon(depth, named_late_bound_vars + i, i) - } - fn id(&self) -> Option<DefId> { match *self { - Region::Static | Region::LateBoundAnon(..) => None, + Region::Static => None, Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => { Some(id) @@ -88,9 +76,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, idx, id) => { Region::LateBound(debruijn.shifted_in(amount), idx, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index) - } _ => self, } } @@ -100,9 +85,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, index, id) => { Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index) - } _ => self, } } @@ -152,10 +134,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>, - - /// When encountering an undefined named lifetime, we will suggest introducing it in these - /// places. - pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, } #[derive(Debug)] @@ -198,10 +176,6 @@ enum Scope<'a> { s: ScopeRef<'a>, - /// In some cases not allowing late bounds allows us to avoid ICEs. - /// This is almost ways set to true. - allow_late_bound: bool, - /// If this binder comes from a where clause, specify how it was created. /// This is used to diagnose inaccessible lifetimes in APIT: /// ```ignore (illustrative) @@ -220,9 +194,8 @@ enum Scope<'a> { }, /// A scope which either determines unspecified lifetimes or errors - /// on them (e.g., due to ambiguity). For more details, see `Elide`. + /// on them (e.g., due to ambiguity). Elision { - elide: Elide, s: ScopeRef<'a>, }, @@ -278,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { opaque_type_parent, scope_type, hir_id, - allow_late_bound, where_bound_origin, s: _, } => f @@ -288,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) - .field("allow_late_bound", allow_late_bound) .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), Scope::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::Elision { elide, s: _ } => { - f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish() - } + Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), Scope::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) @@ -314,32 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { } } -#[derive(Clone, Debug)] -enum Elide { - /// Use a fresh anonymous late-bound lifetime each time, by - /// incrementing the counter to generate sequential indices. All - /// anonymous lifetimes must start *after* named bound vars. - FreshLateAnon(u32, Cell<u32>), - /// Always use this one lifetime. - Exact(Region), - /// Less or more than one lifetime were found, error on unspecified. - Error(Vec<ElisionFailureInfo>), - /// Forbid lifetime elision inside of a larger scope where it would be - /// permitted. For example, in let position impl trait. - Forbid, -} - -#[derive(Clone, Debug)] -pub(crate) struct ElisionFailureInfo { - /// Where we can find the argument pattern. - pub(crate) parent: Option<hir::BodyId>, - /// The index of the argument in the original definition. - pub(crate) index: usize, - pub(crate) lifetime_count: usize, - pub(crate) have_bound_regions: bool, - pub(crate) span: Span, -} - type ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -421,7 +364,6 @@ fn do_resolve( scope: ROOT_SCOPE, trait_definition_only, xcrate_object_lifetime_defaults: Default::default(), - missing_named_lifetime_spots: vec![], }; visitor.visit_item(item); @@ -503,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) } - Region::LateBoundAnon(_, _, anon_idx) => { - ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx)) - } _ => bug!("{:?} is not a late region", region), } } @@ -571,7 +510,54 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure { bound_generic_params, .. } = e.kind { + if let hir::ExprKind::Closure(hir::Closure { + binder, bound_generic_params, fn_decl, .. + }) = e.kind + { + if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { + fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> { + struct V(Option<Span>); + + impl<'v> Visitor<'v> for V { + fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + match t.kind { + _ if self.0.is_some() => (), + hir::TyKind::Infer => { + self.0 = Some(t.span); + } + _ => intravisit::walk_ty(self, t), + } + } + } + + let mut v = V(None); + v.visit_ty(ty); + v.0 + } + + let infer_in_rt_sp = match fn_decl.output { + hir::FnRetTy::DefaultReturn(sp) => Some(sp), + hir::FnRetTy::Return(ty) => span_of_infer(ty), + }; + + let infer_spans = fn_decl + .inputs + .into_iter() + .filter_map(span_of_infer) + .chain(infer_in_rt_sp) + .collect::<Vec<_>>(); + + if !infer_spans.is_empty() { + self.tcx.sess + .struct_span_err( + infer_spans, + "implicit types in closure signatures are forbidden when `for<...>` is present", + ) + .span_label(for_sp, "`for<...>` is here") + .emit(); + } + } + let next_early_index = self.next_early_index(); let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = bound_generic_params @@ -584,6 +570,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { (pair, r) }) .unzip(); + self.map.late_bound_vars.insert(e.hir_id, binders); let scope = Scope::Binder { hir_id: e.hir_id, @@ -592,9 +579,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; + self.with(scope, |this| { // a closure has no bounds, so everything // contained within is scoped within its binder. @@ -616,11 +603,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } match item.kind { hir::ItemKind::Fn(_, ref generics, _) => { - self.missing_named_lifetime_spots.push(generics.into()); self.visit_early_late(None, item.hir_id(), generics, |this| { intravisit::walk_item(this, item); }); - self.missing_named_lifetime_spots.pop(); } hir::ItemKind::ExternCrate(_) @@ -634,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { // No lifetime parameters, but implied 'static. - let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE }; - self.with(scope, |this| intravisit::walk_item(this, item)); + self.with(Scope::Elision { s: self.scope }, |this| { + intravisit::walk_item(this, item) + }); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { // Opaque types are visited when we visit the @@ -683,8 +669,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::TraitAlias(ref generics, ..) | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { - self.missing_named_lifetime_spots.push(generics.into()); - // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes @@ -713,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -722,7 +705,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_item(this, item); }); }); - self.missing_named_lifetime_spots.pop(); } } } @@ -748,20 +730,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match ty.kind { hir::TyKind::BareFn(ref c) => { let next_early_index = self.next_early_index(); - let lifetime_span: Option<Span> = - c.generic_params.iter().rev().find_map(|param| match param.kind { - GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => { - Some(param.span) - } - _ => None, - }); - let (span, span_type) = if let Some(span) = lifetime_span { - (span.shrink_to_hi(), ForLifetimeSpanType::TypeTail) - } else { - (ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty) - }; - self.missing_named_lifetime_spots - .push(MissingLifetimeSpot::HigherRanked { span, span_type }); let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c .generic_params .iter() @@ -781,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -789,7 +756,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // contained within is scoped within its binder. intravisit::walk_ty(this, ty); }); - self.missing_named_lifetime_spots.pop(); } hir::TyKind::TraitObject(bounds, ref lifetime, _) => { debug!(?bounds, ?lifetime, "TraitObject"); @@ -800,11 +766,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }); match lifetime.name { - LifetimeName::Implicit => { - // For types like `dyn Foo`, we should - // generate a special form of elided. - span_bug!(ty.span, "object-lifetime-default expected, not implicit",); - } LifetimeName::ImplicitObjectLifetimeDefault => { // If the user does not write *anything*, we // use the object lifetime defaulting @@ -812,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box<dyn Debug + 'static>`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Underscore => { + LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(&[lifetime]) } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -853,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // position impl Trait let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + let scope = Scope::Elision { s: this.scope }; this.with(scope, |this| { intravisit::walk_item(this, opaque_ty); }) @@ -929,24 +889,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index_for_opaque_type(); debug!(?index); - let mut elision = None; let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; + debug!(?generics.params); for param in generics.params { match param.kind { GenericParamKind::Lifetime { .. } => { let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); - if let hir::ParamName::Plain(Ident { - name: kw::UnderscoreLifetime, - .. - }) = param.name - { - // Pick the elided lifetime "definition" if one exists - // and use it to make an elision scope. - elision = Some(reg); - } else { - lifetimes.insert(def_id, reg); - } + lifetimes.insert(def_id, reg); } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -956,51 +906,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let next_early_index = index + non_lifetime_count; self.map.late_bound_vars.insert(ty.hir_id, vec![]); - if let Some(elision_region) = elision { - let scope = - Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope }; - self.with(scope, |this| { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: this.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - this.with(scope, |this| { - this.visit_generics(generics); - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - }); - } else { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: self.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - } + let scope = Scope::Binder { + hir_id: ty.hir_id, + lifetimes, + next_early_index, + s: self.scope, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + }) + }); } _ => intravisit::walk_ty(self, ty), } @@ -1010,7 +933,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::TraitItemKind::*; match trait_item.kind { Fn(_, _) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(trait_item.hir_id())), @@ -1018,10 +940,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &trait_item.generics, |this| intravisit::walk_trait_item(this, trait_item), ); - self.missing_named_lifetime_spots.pop(); } Type(bounds, ref ty) => { - self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let generics = &trait_item.generics; let mut index = self.next_early_index(); debug!("visit_ty: index = {}", index); @@ -1047,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -1062,14 +981,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(trait_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_trait_item(self, trait_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1078,7 +994,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { use self::hir::ImplItemKind::*; match impl_item.kind { Fn(..) => { - self.missing_named_lifetime_spots.push((&impl_item.generics).into()); let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(impl_item.hir_id())), @@ -1086,11 +1001,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { &impl_item.generics, |this| intravisit::walk_impl_item(this, impl_item), ); - self.missing_named_lifetime_spots.pop(); } TyAlias(ref ty) => { let generics = &impl_item.generics; - self.missing_named_lifetime_spots.push(generics.into()); let mut index = self.next_early_index(); let mut non_lifetime_count = 0; debug!("visit_ty: index = {}", index); @@ -1115,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1125,14 +1037,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.visit_ty(ty); }) }); - self.missing_named_lifetime_spots.pop(); } Const(_, _) => { // Only methods and types support generics. assert!(impl_item.generics.params.is_empty()); - self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static); intravisit::walk_impl_item(self, impl_item); - self.missing_named_lifetime_spots.pop(); } } } @@ -1140,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]), hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Param(param_def_id, _) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. hir::LifetimeName::Error => {} + // Those will be resolved by typechecking. + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} } } @@ -1161,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _: Span, + _: hir::HirId, + ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::Return(ref ty) => Some(&**ty), }; - self.visit_fn_like_elision(&fd.inputs, output); + self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); + intravisit::walk_fn_kind(self, fk); + self.visit_nested_body(body_id) } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { @@ -1224,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: Some(origin), }; this.with(scope, |this| { @@ -1297,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: self.next_early_index(), opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1315,8 +1230,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ) { debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); - let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); - let next_early_index = self.next_early_index(); let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); @@ -1350,17 +1263,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); this.visit_trait_ref(&trait_ref.trait_ref); }); - - if should_pop_missing_lt { - self.missing_named_lifetime_spots.pop(); - } } } @@ -1506,14 +1414,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { { let LifetimeContext { tcx, map, .. } = self; let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); - let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots); let mut this = LifetimeContext { tcx: *tcx, map, scope: &wrap_scope, trait_definition_only: self.trait_definition_only, xcrate_object_lifetime_defaults, - missing_named_lifetime_spots, }; let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { @@ -1521,7 +1427,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { f(&mut this); } self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; - self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; } /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. @@ -1611,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, walk); @@ -1677,7 +1581,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { break None; } - Scope::Binder { ref lifetimes, scope_type, s, .. } => { + Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => { if let Some(&def) = lifetimes.get(®ion_def_id) { break Some(def.shifted(late_depth)); } @@ -1685,6 +1589,21 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { BinderScopeType::Normal => late_depth += 1, BinderScopeType::Concatenating => {} } + // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in + // regular fns. + if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin + && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name + && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner) + && !self.tcx.features().anonymous_lifetime_in_impl_trait + { + rustc_session::parse::feature_err( + &self.tcx.sess.parse_sess, + sym::anonymous_lifetime_in_impl_trait, + lifetime_ref.span, + "anonymous lifetimes in `impl Trait` are unstable", + ).emit(); + return; + } scope = s; } @@ -1772,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); if generic_args.parenthesized { - self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty())); + self.visit_fn_like_elision( + generic_args.inputs(), + Some(generic_args.bindings[0].ty()), + false, + ); return; } - let mut elide_lifetimes = true; - let lifetimes: Vec<_> = generic_args - .args - .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if !lt.is_elided() { - elide_lifetimes = false; - } - Some(lt) - } - _ => None, - }) - .collect(); - // We short-circuit here if all are elided in order to pluralize - // possible errors - if elide_lifetimes { - self.resolve_elided_lifetimes(&lifetimes); - } else { - lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); + for arg in generic_args.args { + if let hir::GenericArg::Lifetime(lt) = arg { + self.visit_lifetime(lt); + } } // Figure out if this is a type/trait segment, @@ -2051,436 +1958,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &mut self, inputs: &'tcx [hir::Ty<'tcx>], output: Option<&'tcx hir::Ty<'tcx>>, + in_closure: bool, ) { - debug!("visit_fn_like_elision: enter"); - let mut scope = &*self.scope; - let hir_id = loop { - match scope { - Scope::Binder { hir_id, allow_late_bound: true, .. } => { - break *hir_id; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::Supertrait { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = *s; - } - Scope::Root - | Scope::Body { .. } - | Scope::Binder { allow_late_bound: false, .. } => { - // See issues #83907 and #83693. Just bail out from looking inside. - // See the issue #95023 for not allowing late bound - self.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - "In fn_like_elision without appropriate scope above", - ); - return; - } - } - }; - // While not strictly necessary, we gather anon lifetimes *before* actually - // visiting the argument types. - let mut gather = GatherAnonLifetimes { anon_count: 0 }; - for input in inputs { - gather.visit_ty(input); - } - trace!(?gather.anon_count); - let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default(); - let named_late_bound_vars = late_bound_vars.len() as u32; - late_bound_vars.extend( - (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))), - ); - let arg_scope = Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)), - s: self.scope, - }; - self.with(arg_scope, |this| { + self.with(Scope::Elision { s: self.scope }, |this| { for input in inputs { this.visit_ty(input); } - }); - - let Some(output) = output else { return }; - - debug!("determine output"); - - // Figure out if there's a body we can get argument names from, - // and whether there's a `self` argument (treated specially). - let mut assoc_item_kind = None; - let mut impl_self = None; - let parent = self.tcx.hir().get_parent_node(output.hir_id); - let body = match self.tcx.hir().get(parent) { - // `fn` definitions and methods. - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body), - - Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => { - if let hir::ItemKind::Trait(.., ref trait_items) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - assoc_item_kind = - trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); - } - match *m { - hir::TraitFn::Required(_) => None, - hir::TraitFn::Provided(body) => Some(body), - } - } - - Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => { - if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - impl_self = Some(self_ty); - assoc_item_kind = - items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind); - } - Some(body) - } - - // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). - Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None, - - Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None, - - // Everything else (only closures?) doesn't - // actually enjoy elision in return types. - _ => { - self.visit_ty(output); - return; - } - }; - - let has_self = match assoc_item_kind { - Some(hir::AssocItemKind::Fn { has_self }) => has_self, - _ => false, - }; - - // In accordance with the rules for lifetime elision, we can determine - // what region to use for elision in the output type in two ways. - // First (determined here), if `self` is by-reference, then the - // implied output region is the region of the self parameter. - if has_self { - struct SelfVisitor<'a> { - map: &'a NamedRegionMap, - impl_self: Option<&'a hir::TyKind<'a>>, - lifetime: Set1<Region>, - } - - impl SelfVisitor<'_> { - // Look for `self: &'a Self` - also desugared from `&'a self`, - // and if that matches, use it for elision and return early. - fn is_self_ty(&self, res: Res) -> bool { - if let Res::SelfTy { .. } = res { - return true; - } - - // Can't always rely on literal (or implied) `Self` due - // to the way elision rules were originally specified. - if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = - self.impl_self - { - match path.res { - // Permit the types that unambiguously always - // result in the same type constructor being used - // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _) - | Res::PrimTy(_) => return res == path.res, - _ => {} - } - } - - false - } - } - - impl<'a> Visitor<'a> for SelfVisitor<'a> { - fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) { - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind - { - if self.is_self_ty(path.res) { - if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - self.lifetime.insert(*lifetime); - } - } - } - } - intravisit::walk_ty(self, ty) - } - } - - let mut visitor = SelfVisitor { - map: self.map, - impl_self: impl_self.map(|ty| &ty.kind), - lifetime: Set1::Empty, - }; - visitor.visit_ty(&inputs[0]); - if let Set1::One(lifetime) = visitor.lifetime { - let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - return; - } - } - - // Second, if there was exactly one lifetime (either a substitution or a - // reference) in the arguments, then any anonymous regions in the output - // have that lifetime. - let mut possible_implied_output_region = None; - let mut lifetime_count = 0; - let arg_lifetimes = inputs - .iter() - .enumerate() - .skip(has_self as usize) - .map(|(i, input)| { - let mut gather = GatherLifetimes { - map: self.map, - outer_index: ty::INNERMOST, - have_bound_regions: false, - lifetimes: Default::default(), - }; - gather.visit_ty(input); - - lifetime_count += gather.lifetimes.len(); - - if lifetime_count == 1 && gather.lifetimes.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = gather.lifetimes.iter().cloned().next(); - } - - ElisionFailureInfo { - parent: body, - index: i, - lifetime_count: gather.lifetimes.len(), - have_bound_regions: gather.have_bound_regions, - span: input.span, - } - }) - .collect(); - - let elide = if lifetime_count == 1 { - Elide::Exact(possible_implied_output_region.unwrap()) - } else { - Elide::Error(arg_lifetimes) - }; - - debug!(?elide); - - let scope = Scope::Elision { elide, s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - - struct GatherLifetimes<'a> { - map: &'a NamedRegionMap, - outer_index: ty::DebruijnIndex, - have_bound_regions: bool, - lifetimes: FxHashSet<Region>, - } - - impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_in(1); - } - match ty.kind { - hir::TyKind::TraitObject(bounds, ref lifetime, _) => { - for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - } - - // Stay on the safe side and don't include the object - // lifetime default (which may not end up being used). - if !lifetime.is_elided() { - self.visit_lifetime(lifetime); - } - } - _ => { - intravisit::walk_ty(self, ty); - } - } - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_out(1); - } - } - - fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) { - if let hir::GenericParamKind::Lifetime { .. } = param.kind { - // FIXME(eddyb) Do we want this? It only makes a difference - // if this `for<'a>` lifetime parameter is never used. - self.have_bound_regions = true; - } - - intravisit::walk_generic_param(self, param); - } - - fn visit_poly_trait_ref( - &mut self, - trait_ref: &hir::PolyTraitRef<'_>, - modifier: hir::TraitBoundModifier, - ) { - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, trait_ref, modifier); - self.outer_index.shift_out(1); - } - - fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) { - if let hir::GenericBound::LangItemTrait { .. } = bound { - self.outer_index.shift_in(1); - intravisit::walk_param_bound(self, bound); - self.outer_index.shift_out(1); - } else { - intravisit::walk_param_bound(self, bound); - } + if !in_closure && let Some(output) = output { + this.visit_ty(output); } - - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - match lifetime { - Region::LateBound(debruijn, _, _) - | Region::LateBoundAnon(debruijn, _, _) - if debruijn < self.outer_index => - { - self.have_bound_regions = true; - } - _ => { - // FIXME(jackh726): nested trait refs? - self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index)); - } - } - } - } - } - - struct GatherAnonLifetimes { - anon_count: u32, - } - impl<'v> Visitor<'v> for GatherAnonLifetimes { - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // If we enter a `BareFn`, then we enter a *new* binding scope - if let hir::TyKind::BareFn(_) = ty.kind { - return; - } - intravisit::walk_ty(self, ty); - } - - fn visit_generic_args( - &mut self, - path_span: Span, - generic_args: &'v hir::GenericArgs<'v>, - ) { - // parenthesized args enter a new elision scope - if generic_args.parenthesized { - return; - } - intravisit::walk_generic_args(self, path_span, generic_args) - } - - #[instrument(skip(self), level = "trace")] - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if lifetime_ref.is_elided() { - self.anon_count += 1; - } - } - } - } - - fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) { - debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs); - - if lifetime_refs.is_empty() { - return; + }); + if in_closure && let Some(output) = output { + self.visit_ty(output); } - - let mut late_depth = 0; - let mut scope = self.scope; - let mut in_scope_lifetimes = FxIndexSet::default(); - let error = loop { - match *scope { - // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => return, - - Scope::Root => break None, - - Scope::Binder { s, ref lifetimes, scope_type, .. } => { - // collect named lifetimes for suggestions - in_scope_lifetimes.extend(lifetimes.keys().copied()); - match scope_type { - BinderScopeType::Normal => late_depth += 1, - BinderScopeType::Concatenating => {} - } - scope = s; - } - - Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter), - .. - } => { - for lifetime_ref in lifetime_refs { - let lifetime = - Region::late_anon(named_late_bound_vars, counter).shifted(late_depth); - - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Exact(l), .. } => { - let lifetime = l.shifted(late_depth); - for lifetime_ref in lifetime_refs { - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => { - let mut scope = s; - loop { - match scope { - Scope::Binder { ref lifetimes, s, .. } => { - // Collect named lifetimes for suggestions. - in_scope_lifetimes.extend(lifetimes.keys().copied()); - scope = s; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = s; - } - _ => break, - } - } - break Some(&e[..]); - } - - Scope::Elision { elide: Elide::Forbid, .. } => break None, - - Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { - scope = s; - } - } - }; - - let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); - spans.sort(); - let mut spans_dedup = spans.clone(); - spans_dedup.dedup(); - let spans_with_counts: Vec<_> = spans_dedup - .into_iter() - .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count())) - .collect(); - - let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len()); - - self.add_missing_lifetime_specifiers_label( - &mut err, - spans_with_counts, - in_scope_lifetimes, - error, - ); - err.emit(); } fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8968179c92e..31d10008efb 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -10,7 +10,8 @@ #![feature(box_patterns)] #![feature(drain_filter)] #![feature(if_let_guard)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] +#![feature(iter_intersperse)] #![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] @@ -436,7 +437,7 @@ enum ModuleKind { /// f(); // Resolves to (1) /// } /// ``` - Block(NodeId), + Block, /// Any module with a name. /// /// This could be: @@ -453,7 +454,7 @@ impl ModuleKind { /// Get name of the module. pub fn name(&self) -> Option<Symbol> { match self { - ModuleKind::Block(..) => None, + ModuleKind::Block => None, ModuleKind::Def(.., name) => Some(*name), } } @@ -529,7 +530,7 @@ impl<'a> ModuleData<'a> { ) -> Self { let is_foreign = match kind { ModuleKind::Def(_, def_id, _) => !def_id.is_local(), - ModuleKind::Block(_) => false, + ModuleKind::Block => false, }; ModuleData { parent, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4210560f531..070fb9c721b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -325,7 +325,7 @@ impl<'a> ResolverExpand for Resolver<'a> { UNUSED_MACROS, node_id, ident.span, - &format!("unused macro definition: `{}`", ident.as_str()), + &format!("unused macro definition: `{}`", ident.name), ); } for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { @@ -341,7 +341,7 @@ impl<'a> ResolverExpand for Resolver<'a> { &format!( "{} rule of macro `{}` is never used", crate::diagnostics::ordinalize(arm_i + 1), - ident.as_str() + ident.name ), ); } @@ -796,16 +796,23 @@ impl<'a> Resolver<'a> { ) { let span = path.span; if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level { + if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level + { let feature = stability.feature; - if !self.active_features.contains(&feature) && !span.allows_unstable(feature) { + + let is_allowed = |feature| { + self.active_features.contains(&feature) || span.allows_unstable(feature) + }; + let allowed_by_implication = + implied_by.map(|feature| is_allowed(feature)).unwrap_or(false); + if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; let soft_handler = |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg); stability::report_unstable( self.session, feature, - reason, + reason.to_opt_reason(), issue, None, is_soft, diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 6eb2f2d929d..e2e0e1f5b30 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> { pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> { let span_utils = SpanUtils::new(&save_ctxt.tcx.sess); let dumper = Dumper::new(save_ctxt.config.clone()); - DumpVisitor { - tcx: save_ctxt.tcx, - save_ctxt, - dumper, - span: span_utils, - // mac_defs: FxHashSet::default(), - // macro_calls: FxHashSet::default(), - } + DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils } } pub fn analysis(&self) -> &rls_data::Analysis { @@ -1360,7 +1353,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { } } } - hir::ExprKind::Closure { ref fn_decl, body, .. } => { + hir::ExprKind::Closure(&hir::Closure { ref fn_decl, body, .. }) => { let id = format!("${}", ex.hir_id); // walk arg and return types @@ -1425,9 +1418,10 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.process_macro_use(l.span); self.process_var_decl(&l.pat); - // Just walk the initializer and type (don't want to walk the pattern again). + // Just walk the initializer, the else branch and type (don't want to walk the pattern again). walk_list!(self, visit_ty, &l.ty); walk_list!(self, visit_expr, &l.init); + walk_list!(self, visit_block, l.els); } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index 5056163b7fe..dbc5c15195c 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -indexmap = "1.8.0" +indexmap = "1.9.1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } [dev-dependencies] diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 366efe9cfa5..5c17ef6ace2 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -297,6 +297,10 @@ impl FileEncoder { } } + pub fn file(&self) -> &File { + &self.file + } + #[inline] fn capacity(&self) -> usize { self.buf.len() diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index cbb03ffd7a0..eede4d16ea3 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -1,11 +1,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lock; +use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use std::cmp::{self, Ordering}; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct VariantInfo { - pub name: Option<String>, + pub name: Option<Symbol>, pub kind: SizeKind, pub size: u64, pub align: u64, @@ -20,7 +21,7 @@ pub enum SizeKind { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct FieldInfo { - pub name: String, + pub name: Symbol, pub offset: u64, pub size: u64, pub align: u64, @@ -119,7 +120,7 @@ impl CodeStats { let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; let indent = if !struct_like { let name = match name.as_ref() { - Some(name) => name.to_owned(), + Some(name) => name.to_string(), None => i.to_string(), }; println!( diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b7da0f22942..fe9ef604541 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -722,7 +722,7 @@ impl Default for Options { target_triple: TargetTriple::from_triple(host_triple()), test: false, incremental: None, - debugging_opts: Default::default(), + unstable_opts: Default::default(), prints: Vec::new(), cg: Default::default(), error_format: ErrorOutputType::default(), @@ -752,8 +752,8 @@ impl Options { /// Returns `true` if there is a reason to build the dep graph. pub fn build_dep_graph(&self) -> bool { self.incremental.is_some() - || self.debugging_opts.dump_dep_graph - || self.debugging_opts.query_dep_graph + || self.unstable_opts.dump_dep_graph + || self.unstable_opts.query_dep_graph } pub fn file_path_mapping(&self) -> FilePathMapping { @@ -762,13 +762,13 @@ impl Options { /// Returns `true` if there will be an output file generated. pub fn will_create_output_file(&self) -> bool { - !self.debugging_opts.parse_only && // The file is just being parsed - !self.debugging_opts.ls // The file is just being queried + !self.unstable_opts.parse_only && // The file is just being parsed + !self.unstable_opts.ls // The file is just being queried } #[inline] pub fn share_generics(&self) -> bool { - match self.debugging_opts.share_generics { + match self.unstable_opts.share_generics { Some(setting) => setting, None => match self.optimize { OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, @@ -782,7 +782,7 @@ impl Options { } } -impl DebuggingOptions { +impl UnstableOptions { pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { HandlerFlags { can_emit_warnings, @@ -940,7 +940,7 @@ fn default_configuration(sess: &Session) -> CrateConfig { let panic_strategy = sess.panic_strategy(); ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); - for s in sess.opts.debugging_opts.sanitizer { + for s in sess.opts.unstable_opts.sanitizer { let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); } @@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig { if sess.opts.debug_assertions { ret.insert((sym::debug_assertions, None)); } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if sess.opts.crate_types.contains(&CrateType::ProcMacro) { ret.insert((sym::proc_macro, None)); } @@ -1403,6 +1405,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { /// long-term interface for rustc. pub fn rustc_optgroups() -> Vec<RustcOptGroup> { let mut opts = rustc_short_optgroups(); + // FIXME: none of these descriptions are actually used opts.extend(vec![ opt::multi_s( "", @@ -1411,7 +1414,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "NAME[=PATH]", ), opt::opt_s("", "sysroot", "Override the system root", "PATH"), - opt::multi("Z", "", "Set internal debugging options", "FLAG"), + opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"), opt::opt_s( "", "error-format", @@ -1659,12 +1662,12 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { edition } -fn check_debug_option_stability( - debugging_opts: &DebuggingOptions, +fn check_error_format_stability( + unstable_opts: &UnstableOptions, error_format: ErrorOutputType, json_rendered: HumanReadableErrorType, ) { - if !debugging_opts.unstable_options { + if !unstable_opts.unstable_options { if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { early_error( ErrorOutputType::Json { pretty: false, json_rendered }, @@ -1683,12 +1686,12 @@ fn check_debug_option_stability( } fn parse_output_types( - debugging_opts: &DebuggingOptions, + unstable_opts: &UnstableOptions, matches: &getopts::Matches, error_format: ErrorOutputType, ) -> OutputTypes { let mut output_types = BTreeMap::new(); - if !debugging_opts.parse_only { + if !unstable_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { let (shorthand, path) = match output_type.split_once('=') { @@ -1762,19 +1765,19 @@ fn should_override_cgus_and_disable_thinlto( (disable_thinlto, codegen_units) } -fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { - if debugging_opts.threads == 0 { +fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) { + if unstable_opts.threads == 0 { early_error(error_format, "value for threads must be a positive non-zero integer"); } - if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { + if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() { early_error(error_format, "optimization fuel is incompatible with multiple threads"); } } fn collect_print_requests( cg: &mut CodegenOptions, - dopts: &mut DebuggingOptions, + unstable_opts: &mut UnstableOptions, matches: &getopts::Matches, error_format: ErrorOutputType, ) -> Vec<PrintRequest> { @@ -1803,7 +1806,7 @@ fn collect_print_requests( "native-static-libs" => PrintRequest::NativeStaticLibs, "stack-protector-strategies" => PrintRequest::StackProtectorStrategies, "target-spec-json" => { - if dopts.unstable_options { + if unstable_opts.unstable_options { PrintRequest::TargetSpec } else { early_error( @@ -2069,10 +2072,10 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec< pub fn parse_externs( matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, + unstable_opts: &UnstableOptions, error_format: ErrorOutputType, ) -> Externs { - let is_unstable_enabled = debugging_opts.unstable_options; + let is_unstable_enabled = unstable_opts.unstable_options; let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); for arg in matches.opt_strs("extern") { let (name, path) = match arg.split_once('=') { @@ -2171,7 +2174,7 @@ pub fn parse_externs( fn parse_remap_path_prefix( matches: &getopts::Matches, - debugging_opts: &DebuggingOptions, + unstable_opts: &UnstableOptions, error_format: ErrorOutputType, ) -> Vec<(PathBuf, PathBuf)> { let mut mapping: Vec<(PathBuf, PathBuf)> = matches @@ -2185,7 +2188,7 @@ fn parse_remap_path_prefix( Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), }) .collect(); - match &debugging_opts.remap_cwd_prefix { + match &unstable_opts.remap_cwd_prefix { Some(to) => match std::env::current_dir() { Ok(cwd) => mapping.push((cwd, to.clone())), Err(_) => (), @@ -2195,6 +2198,8 @@ fn parse_remap_path_prefix( mapping } +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session_options(matches: &getopts::Matches) -> Options { let color = parse_color(matches); @@ -2217,12 +2222,12 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let crate_types = parse_crate_types_from_list(unparsed_crate_types) .unwrap_or_else(|e| early_error(error_format, &e)); - let mut debugging_opts = DebuggingOptions::build(matches, error_format); + let mut unstable_opts = UnstableOptions::build(matches, error_format); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); - check_debug_option_stability(&debugging_opts, error_format, json_rendered); + check_error_format_stability(&unstable_opts, error_format, json_rendered); - if !debugging_opts.unstable_options && json_unused_externs.is_enabled() { + if !unstable_opts.unstable_options && json_unused_externs.is_enabled() { early_error( error_format, "the `-Z unstable-options` flag must also be passed to enable \ @@ -2230,7 +2235,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - let output_types = parse_output_types(&debugging_opts, matches, error_format); + let output_types = parse_output_types(&unstable_opts, matches, error_format); let mut cg = CodegenOptions::build(matches, error_format); let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( @@ -2240,20 +2245,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { cg.codegen_units, ); - check_thread_count(&debugging_opts, error_format); + check_thread_count(&unstable_opts, error_format); let incremental = cg.incremental.as_ref().map(PathBuf::from); - let assert_incr_state = - parse_assert_incr_state(&debugging_opts.assert_incr_state, error_format); + let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format); - if debugging_opts.profile && incremental.is_some() { + if unstable_opts.profile && incremental.is_some() { early_error( error_format, "can't instrument with gcov profiling when compiling incrementally", ); } - if debugging_opts.profile { + if unstable_opts.profile { match codegen_units { Some(1) => {} None => codegen_units = Some(1), @@ -2271,7 +2275,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - if debugging_opts.profile_sample_use.is_some() + if unstable_opts.profile_sample_use.is_some() && (cg.profile_generate.enabled() || cg.profile_use.is_some()) { early_error( @@ -2282,7 +2286,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { // Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes // precedence. - match (cg.symbol_mangling_version, debugging_opts.symbol_mangling_version) { + match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) { (Some(smv_c), Some(smv_z)) if smv_c != smv_z => { early_error( error_format, @@ -2291,7 +2295,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } (Some(SymbolManglingVersion::V0), _) => {} - (Some(_), _) if !debugging_opts.unstable_options => { + (Some(_), _) if !unstable_opts.unstable_options => { early_error( error_format, "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`", @@ -2310,7 +2314,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes // precedence. - match (cg.instrument_coverage, debugging_opts.instrument_coverage) { + match (cg.instrument_coverage, unstable_opts.instrument_coverage) { (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { early_error( error_format, @@ -2319,7 +2323,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} - (Some(_), _) if !debugging_opts.unstable_options => { + (Some(_), _) if !unstable_opts.unstable_options => { early_error( error_format, "`-C instrument-coverage=except-*` requires `-Z unstable-options`", @@ -2363,7 +2367,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { - debugging_opts.graphviz_font = graphviz_font; + unstable_opts.graphviz_font = graphviz_font; } if !cg.embed_bitcode { @@ -2386,7 +2390,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); + let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format); let cg = cg; @@ -2412,15 +2416,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); } - let externs = parse_externs(matches, &debugging_opts, error_format); + let externs = parse_externs(matches, &unstable_opts, error_format); let crate_name = matches.opt_str("crate-name"); - let remap_path_prefix = parse_remap_path_prefix(matches, &debugging_opts, error_format); + let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format); - let pretty = parse_pretty(&debugging_opts, error_format); + let pretty = parse_pretty(&unstable_opts, error_format); - if !debugging_opts.unstable_options + if !unstable_opts.unstable_options && !target_triple.triple().contains("apple") && cg.split_debuginfo.is_some() { @@ -2481,7 +2485,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { target_triple, test, incremental, - debugging_opts, + unstable_opts, prints, cg, error_format, @@ -2506,10 +2510,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } } -fn parse_pretty(debugging_opts: &DebuggingOptions, efmt: ErrorOutputType) -> Option<PpMode> { +fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> { use PpMode::*; - let first = match debugging_opts.unpretty.as_deref()? { + let first = match unstable_opts.unpretty.as_deref()? { "normal" => Source(PpSourceMode::Normal), "identified" => Source(PpSourceMode::Identified), "expanded" => Source(PpSourceMode::Expanded), @@ -2707,6 +2711,14 @@ impl PpMode { | MirCFG => true, } } + pub fn needs_hir(&self) -> bool { + use PpMode::*; + match *self { + Source(_) | AstTree(_) => false, + + Hir(_) | HirTree | ThirTree | Mir | MirCFG => true, + } + } pub fn needs_analysis(&self) -> bool { use PpMode::*; diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 7353c1ca0e2..44cf5048642 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,5 +1,5 @@ #![feature(if_let_guard)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8f1057b793f..ef314115043 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -102,28 +102,6 @@ macro_rules! top_level_options { ); } -impl Options { - pub fn mir_opt_level(&self) -> usize { - self.debugging_opts - .mir_opt_level - .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) - } - - pub fn instrument_coverage(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedGenerics - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedFunctions - } -} - top_level_options!( /// The top-level command-line options struct. /// @@ -149,9 +127,11 @@ top_level_options!( /// `CodegenOptions`, think about how it influences incremental compilation. If in /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to /// unnecessary re-compilation. + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))] crate_types: Vec<CrateType> [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it @@ -177,7 +157,7 @@ top_level_options!( incremental: Option<PathBuf> [UNTRACKED], assert_incr_state: Option<IncrementalStateAssertion> [UNTRACKED], - debugging_opts: DebuggingOptions [SUBSTRUCT], + unstable_opts: UnstableOptions [SUBSTRUCT], prints: Vec<PrintRequest> [UNTRACKED], cg: CodegenOptions [SUBSTRUCT], externs: Externs [UNTRACKED], @@ -198,7 +178,9 @@ top_level_options!( /// what rustc was invoked with, but massaged a bit to agree with /// commands like `--emit llvm-ir` which they're often incompatible with /// if we otherwise use the defaults of rustc. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))] cli_forced_codegen_units: Option<usize> [UNTRACKED], + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] cli_forced_thinlto_off: bool [UNTRACKED], /// Remap source path prefixes in all output (messages, object files, debug, etc.). @@ -249,11 +231,12 @@ macro_rules! options { ),* ,) => ( #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] + pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } impl Default for $struct_name { fn default() -> $struct_name { - $struct_name { $( $( #[$attr] )* $opt: $init),* } + $struct_name { $($opt: $init),* } } } @@ -297,6 +280,22 @@ macro_rules! options { ) } +impl Options { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn time_passes(&self) -> bool { + self.unstable_opts.time_passes || self.unstable_opts.time + } +} + +impl CodegenOptions { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn instrument_coverage(&self) -> InstrumentCoverage { + self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + } +} + // Sometimes different options need to build a common structure. // That structure can be kept in one of the options' fields, the others become dummy. macro_rules! redirect_field { @@ -377,7 +376,7 @@ mod desc { pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_oom_strategy: &str = "either `panic` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -535,7 +534,7 @@ mod parse { ) -> bool { match v { Some(s) => { - for s in s.split(",") { + for s in s.split(',') { let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false }; slot.push((pass_name.to_string(), &s[..1] == "+")); } @@ -683,6 +682,7 @@ mod parse { "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "memtag" => SanitizerSet::MEMTAG, + "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, @@ -1075,6 +1075,7 @@ options! { ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))] code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED], @@ -1094,12 +1095,14 @@ options! { "extra data to put in each output filename"), force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED], "force use of the frame pointers"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))] force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), incremental: Option<String> = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1112,6 +1115,7 @@ options! { "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))] link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED], @@ -1126,6 +1130,7 @@ options! { "generate build artifacts that are compatible with linker-based LTO"), llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to LLVM (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], "perform LLVM link-time optimizations"), metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1142,8 +1147,10 @@ options! { "disable LLVM's SLP vectorization pass"), opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))] overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], "use overflow checks for integer arithmetic"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))] panic: Option<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED], "panic strategy to compile crate with"), passes: Vec<String> = (Vec::new(), parse_list, [TRACKED], @@ -1155,6 +1162,7 @@ options! { "compile the program with profiling instrumentation"), profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))] relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), @@ -1166,6 +1174,7 @@ options! { "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))] split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], @@ -1187,12 +1196,13 @@ options! { } options! { - DebuggingOptions, DB_OPTIONS, dbopts, "Z", "debugging", + UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", // This list is in alphabetical order. // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs + // - src/doc/unstable-book/src/compiler-flags allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED], "only allow the listed language features to be enabled in code (space separated)"), @@ -1200,14 +1210,18 @@ options! { "encode MIR of all functions into the crate metadata (default: no)"), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), + box_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], + "emit noalias metadata for box (default: yes)"), branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED], "set options for branch target identification and pointer authentication on AArch64"), cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], @@ -1276,6 +1290,11 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), + emit_thin_lto: bool = (true, parse_bool, [TRACKED], + "emit the bc module with thin LTO info (default: yes)"), + export_executable_symbols: bool = (false, parse_bool, [TRACKED], + "export symbols from executables, as if they were dynamic libraries"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))] fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -1318,6 +1337,7 @@ options! { "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1326,6 +1346,7 @@ options! { `=except-unused-generics` `=except-unused-functions` `=off` (default)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))] instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -1348,6 +1369,7 @@ options! { merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED], "control the operation of the MergeFunctions LLVM pass, taking \ the same values as the target option of the same name"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))] meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], @@ -1357,6 +1379,9 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), + mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], + "use line numbers relative to the function in mir pretty printing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))] mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], @@ -1423,6 +1448,7 @@ options! { See #77382 and #74551."), print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))] print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -1497,6 +1523,7 @@ options! { "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], @@ -1517,6 +1544,7 @@ options! { symbol_mangling_version: Option<SymbolManglingVersion> = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))] teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -1532,6 +1560,7 @@ options! { "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), thir_unsafeck: bool = (false, parse_bool, [TRACKED], @@ -1540,14 +1569,19 @@ options! { /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))] threads: usize = (1, parse_threads, [UNTRACKED], "use a thread pool with N threads"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time: bool = (false, parse_bool, [UNTRACKED], "measure time of rustc processes (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))] time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))] tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], @@ -1580,14 +1614,19 @@ options! { `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), unsound_mir_opts: bool = (false, parse_bool, [TRACKED], "enable unsound and buggy MIR optimizations (default: no)"), + /// This name is kind of confusing: Most unstable options enable something themselves, while + /// this just allows "normal" options to be feature-gated. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))] unstable_options: bool = (false, parse_bool, [UNTRACKED], "adds unstable command line options to rustc interface (default: no)"), use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED], "use legacy .ctors section for initializers rather than .init_array"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))] verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))] verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), virtual_function_elimination: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1cccef2f64f..9669287b3f3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; +use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -194,6 +194,9 @@ pub struct Session { /// Set of enabled features for the current target. pub target_features: FxHashSet<Symbol>, + + /// Set of enabled features for the current target, including unstable ones. + pub unstable_target_features: FxHashSet<Symbol>, } pub struct PerfStats { @@ -541,10 +544,10 @@ impl Session { /// warnings or errors are emitted. If no messages are emitted ("good path"), then /// it's likely a bug. pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { - if self.opts.debugging_opts.print_type_sizes - || self.opts.debugging_opts.query_dep_graph - || self.opts.debugging_opts.dump_mir.is_some() - || self.opts.debugging_opts.unpretty.is_some() + if self.opts.unstable_opts.print_type_sizes + || self.opts.unstable_opts.query_dep_graph + || self.opts.unstable_opts.dump_mir.is_some() + || self.opts.unstable_opts.unpretty.is_some() || self.opts.output_types.contains_key(&OutputType::Mir) || std::env::var_os("RUSTC_LOG").is_some() { @@ -580,35 +583,28 @@ impl Session { pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } - pub fn verbose(&self) -> bool { - self.opts.debugging_opts.verbose - } + pub fn time_passes(&self) -> bool { - self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time - } - pub fn instrument_mcount(&self) -> bool { - self.opts.debugging_opts.instrument_mcount - } - pub fn time_llvm_passes(&self) -> bool { - self.opts.debugging_opts.time_llvm_passes + self.opts.time_passes() } - pub fn meta_stats(&self) -> bool { - self.opts.debugging_opts.meta_stats - } - pub fn asm_comments(&self) -> bool { - self.opts.debugging_opts.asm_comments - } - pub fn verify_llvm_ir(&self) -> bool { - self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() + + /// Returns `true` if internal lints should be added to the lint store - i.e. if + /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors + /// to be emitted under rustdoc). + pub fn enable_internal_lints(&self) -> bool { + self.unstable_options() && !self.opts.actually_rustdoc } - pub fn print_llvm_passes(&self) -> bool { - self.opts.debugging_opts.print_llvm_passes + + pub fn instrument_coverage(&self) -> bool { + self.opts.cg.instrument_coverage() != InstrumentCoverage::Off } - pub fn binary_dep_depinfo(&self) -> bool { - self.opts.debugging_opts.binary_dep_depinfo + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics } - pub fn mir_opt_level(&self) -> usize { - self.opts.mir_opt_level() + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions } /// Gets the features enabled for the current compilation session. @@ -626,102 +622,8 @@ impl Session { } } - /// Calculates the flavor of LTO to use for this compilation. - pub fn lto(&self) -> config::Lto { - // If our target has codegen requirements ignore the command line - if self.target.requires_lto { - return config::Lto::Fat; - } - - // If the user specified something, return that. If they only said `-C - // lto` and we've for whatever reason forced off ThinLTO via the CLI, - // then ensure we can't use a ThinLTO. - match self.opts.cg.lto { - config::LtoCli::Unspecified => { - // The compiler was invoked without the `-Clto` flag. Fall - // through to the default handling - } - config::LtoCli::No => { - // The user explicitly opted out of any kind of LTO - return config::Lto::No; - } - config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => { - // All of these mean fat LTO - return config::Lto::Fat; - } - config::LtoCli::Thin => { - return if self.opts.cli_forced_thinlto_off { - config::Lto::Fat - } else { - config::Lto::Thin - }; - } - } - - // Ok at this point the target doesn't require anything and the user - // hasn't asked for anything. Our next decision is whether or not - // we enable "auto" ThinLTO where we use multiple codegen units and - // then do ThinLTO over those codegen units. The logic below will - // either return `No` or `ThinLocal`. - - // If processing command line options determined that we're incompatible - // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. - if self.opts.cli_forced_thinlto_off { - return config::Lto::No; - } - - // If `-Z thinlto` specified process that, but note that this is mostly - // a deprecated option now that `-C lto=thin` exists. - if let Some(enabled) = self.opts.debugging_opts.thinlto { - if enabled { - return config::Lto::ThinLocal; - } else { - return config::Lto::No; - } - } - - // If there's only one codegen unit and LTO isn't enabled then there's - // no need for ThinLTO so just return false. - if self.codegen_units() == 1 { - return config::Lto::No; - } - - // Now we're in "defaults" territory. By default we enable ThinLTO for - // optimized compiles (anything greater than O0). - match self.opts.optimize { - config::OptLevel::No => config::Lto::No, - _ => config::Lto::ThinLocal, - } - } - - /// Returns the panic strategy for this compile session. If the user explicitly selected one - /// using '-C panic', use that, otherwise use the panic strategy defined by the target. - pub fn panic_strategy(&self) -> PanicStrategy { - self.opts.cg.panic.unwrap_or(self.target.panic_strategy) - } - pub fn fewer_names(&self) -> bool { - if let Some(fewer_names) = self.opts.debugging_opts.fewer_names { - fewer_names - } else { - let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) - || self.opts.output_types.contains_key(&OutputType::Bitcode) - // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. - || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); - !more_names - } - } - - pub fn unstable_options(&self) -> bool { - self.opts.debugging_opts.unstable_options - } - pub fn is_nightly_build(&self) -> bool { - self.opts.unstable_features.is_nightly_build() - } pub fn is_sanitizer_cfi_enabled(&self) -> bool { - self.opts.debugging_opts.sanitizer.contains(SanitizerSet::CFI) - } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) + self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } /// Check whether this compile session and crate type use static crt. @@ -735,6 +637,8 @@ impl Session { let found_negative = requested_features.clone().any(|r| r == "-crt-static"); let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + // JUSTIFICATION: necessary use of crate_types directly (see FIXME below) + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if found_positive || found_negative { found_positive } else if crate_type == Some(CrateType::ProcMacro) @@ -749,69 +653,18 @@ impl Session { } } - pub fn relocation_model(&self) -> RelocModel { - self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) - } - - pub fn code_model(&self) -> Option<CodeModel> { - self.opts.cg.code_model.or(self.target.code_model) - } - - pub fn tls_model(&self) -> TlsModel { - self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model) - } - pub fn is_wasi_reactor(&self) -> bool { self.target.options.os == "wasi" && matches!( - self.opts.debugging_opts.wasi_exec_model, + self.opts.unstable_opts.wasi_exec_model, Some(config::WasiExecModel::Reactor) ) } - pub fn split_debuginfo(&self) -> SplitDebuginfo { - self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) - } - - pub fn stack_protector(&self) -> StackProtector { - if self.target.options.supports_stack_protector { - self.opts.debugging_opts.stack_protector - } else { - StackProtector::None - } - } - pub fn target_can_use_split_dwarf(&self) -> bool { !self.target.is_like_windows && !self.target.is_like_osx } - pub fn must_emit_unwind_tables(&self) -> bool { - // This is used to control the emission of the `uwtable` attribute on - // LLVM functions. - // - // Unwind tables are needed when compiling with `-C panic=unwind`, but - // LLVM won't omit unwind tables unless the function is also marked as - // `nounwind`, so users are allowed to disable `uwtable` emission. - // Historically rustc always emits `uwtable` attributes by default, so - // even they can be disabled, they're still emitted by default. - // - // On some targets (including windows), however, exceptions include - // other events such as illegal instructions, segfaults, etc. This means - // that on Windows we end up still needing unwind tables even if the `-C - // panic=abort` flag is passed. - // - // You can also find more info on why Windows needs unwind tables in: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 - // - // If a target requires unwind tables, then they must be emitted. - // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` - // value, if it is provided, or disable them, if not. - self.target.requires_uwtable - || self.opts.cg.force_unwind_tables.unwrap_or( - self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, - ) - } - pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) } @@ -930,7 +783,7 @@ impl Session { /// This expends fuel if applicable, and records fuel if applicable. pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool { let mut ret = true; - if let Some((ref c, _)) = self.opts.debugging_opts.fuel { + if let Some((ref c, _)) = self.opts.unstable_opts.fuel { if c == crate_name { assert_eq!(self.threads(), 1); let mut fuel = self.optimization_fuel.lock(); @@ -948,7 +801,7 @@ impl Session { } } } - if let Some(ref c) = self.opts.debugging_opts.print_fuel { + if let Some(ref c) = self.opts.unstable_opts.print_fuel { if c == crate_name { assert_eq!(self.threads(), 1); self.print_fuel.fetch_add(1, SeqCst); @@ -957,10 +810,284 @@ impl Session { ret } + pub fn rust_2015(&self) -> bool { + self.edition() == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.edition() >= Edition::Edition2018 + } + + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.edition() >= Edition::Edition2021 + } + + /// Are we allowed to use features from the Rust 2024 edition? + pub fn rust_2024(&self) -> bool { + self.edition() >= Edition::Edition2024 + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.needs_plt; + + let dbg_opts = &self.opts.unstable_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| attr.has_name(*kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| item.has_name(name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| attr.has_name(name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator<Item = &'a Attribute> { + attrs.iter().filter(move |attr| attr.has_name(name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option<Symbol> { + attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) + } +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +impl Session { + pub fn verbose(&self) -> bool { + self.opts.unstable_opts.verbose + } + + pub fn instrument_mcount(&self) -> bool { + self.opts.unstable_opts.instrument_mcount + } + + pub fn time_llvm_passes(&self) -> bool { + self.opts.unstable_opts.time_llvm_passes + } + + pub fn meta_stats(&self) -> bool { + self.opts.unstable_opts.meta_stats + } + + pub fn asm_comments(&self) -> bool { + self.opts.unstable_opts.asm_comments + } + + pub fn verify_llvm_ir(&self) -> bool { + self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() + } + + pub fn print_llvm_passes(&self) -> bool { + self.opts.unstable_opts.print_llvm_passes + } + + pub fn binary_dep_depinfo(&self) -> bool { + self.opts.unstable_opts.binary_dep_depinfo + } + + pub fn mir_opt_level(&self) -> usize { + self.opts + .unstable_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) + } + + /// Calculates the flavor of LTO to use for this compilation. + pub fn lto(&self) -> config::Lto { + // If our target has codegen requirements ignore the command line + if self.target.requires_lto { + return config::Lto::Fat; + } + + // If the user specified something, return that. If they only said `-C + // lto` and we've for whatever reason forced off ThinLTO via the CLI, + // then ensure we can't use a ThinLTO. + match self.opts.cg.lto { + config::LtoCli::Unspecified => { + // The compiler was invoked without the `-Clto` flag. Fall + // through to the default handling + } + config::LtoCli::No => { + // The user explicitly opted out of any kind of LTO + return config::Lto::No; + } + config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => { + // All of these mean fat LTO + return config::Lto::Fat; + } + config::LtoCli::Thin => { + return if self.opts.cli_forced_thinlto_off { + config::Lto::Fat + } else { + config::Lto::Thin + }; + } + } + + // Ok at this point the target doesn't require anything and the user + // hasn't asked for anything. Our next decision is whether or not + // we enable "auto" ThinLTO where we use multiple codegen units and + // then do ThinLTO over those codegen units. The logic below will + // either return `No` or `ThinLocal`. + + // If processing command line options determined that we're incompatible + // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. + if self.opts.cli_forced_thinlto_off { + return config::Lto::No; + } + + // If `-Z thinlto` specified process that, but note that this is mostly + // a deprecated option now that `-C lto=thin` exists. + if let Some(enabled) = self.opts.unstable_opts.thinlto { + if enabled { + return config::Lto::ThinLocal; + } else { + return config::Lto::No; + } + } + + // If there's only one codegen unit and LTO isn't enabled then there's + // no need for ThinLTO so just return false. + if self.codegen_units() == 1 { + return config::Lto::No; + } + + // Now we're in "defaults" territory. By default we enable ThinLTO for + // optimized compiles (anything greater than O0). + match self.opts.optimize { + config::OptLevel::No => config::Lto::No, + _ => config::Lto::ThinLocal, + } + } + + /// Returns the panic strategy for this compile session. If the user explicitly selected one + /// using '-C panic', use that, otherwise use the panic strategy defined by the target. + pub fn panic_strategy(&self) -> PanicStrategy { + self.opts.cg.panic.unwrap_or(self.target.panic_strategy) + } + + pub fn fewer_names(&self) -> bool { + if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { + fewer_names + } else { + let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) + || self.opts.output_types.contains_key(&OutputType::Bitcode) + // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); + !more_names + } + } + + pub fn unstable_options(&self) -> bool { + self.opts.unstable_opts.unstable_options + } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } + + pub fn overflow_checks(&self) -> bool { + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) + } + + pub fn relocation_model(&self) -> RelocModel { + self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) + } + + pub fn code_model(&self) -> Option<CodeModel> { + self.opts.cg.code_model.or(self.target.code_model) + } + + pub fn tls_model(&self) -> TlsModel { + self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) + } + + pub fn split_debuginfo(&self) -> SplitDebuginfo { + self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) + } + + pub fn stack_protector(&self) -> StackProtector { + if self.target.options.supports_stack_protector { + self.opts.unstable_opts.stack_protector + } else { + StackProtector::None + } + } + + pub fn must_emit_unwind_tables(&self) -> bool { + // This is used to control the emission of the `uwtable` attribute on + // LLVM functions. + // + // Unwind tables are needed when compiling with `-C panic=unwind`, but + // LLVM won't omit unwind tables unless the function is also marked as + // `nounwind`, so users are allowed to disable `uwtable` emission. + // Historically rustc always emits `uwtable` attributes by default, so + // even they can be disabled, they're still emitted by default. + // + // On some targets (including windows), however, exceptions include + // other events such as illegal instructions, segfaults, etc. This means + // that on Windows we end up still needing unwind tables even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows needs unwind tables in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + // + // If a target requires unwind tables, then they must be emitted. + // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` + // value, if it is provided, or disable them, if not. + self.target.requires_uwtable + || self.opts.cg.force_unwind_tables.unwrap_or( + self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, + ) + } + /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { - self.opts.debugging_opts.threads + self.opts.unstable_opts.threads } /// Returns the number of codegen units that should be used for this @@ -1034,112 +1161,20 @@ impl Session { } pub fn teach(&self, code: &DiagnosticId) -> bool { - self.opts.debugging_opts.teach && self.diagnostic().must_teach(code) - } - - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.opts.edition >= Edition::Edition2021 - } - - /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.opts.edition >= Edition::Edition2024 + self.opts.unstable_opts.teach && self.diagnostic().must_teach(code) } pub fn edition(&self) -> Edition { self.opts.edition } - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.needs_plt; - - let dbg_opts = &self.opts.debugging_opts; - - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } - - /// Checks if LLVM lifetime markers should be emitted. - pub fn emit_lifetime_markers(&self) -> bool { - self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) - } - pub fn link_dead_code(&self) -> bool { self.opts.cg.link_dead_code.unwrap_or(false) } - - pub fn instrument_coverage(&self) -> bool { - self.opts.instrument_coverage() - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.instrument_coverage_except_unused_generics() - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.instrument_coverage_except_unused_functions() - } - - pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.has_name(*kind)) - } - - pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.has_name(name)) - } - - pub fn find_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> Option<&'a Attribute> { - attrs.iter().find(|attr| attr.has_name(name)) - } - - pub fn filter_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> impl Iterator<Item = &'a Attribute> { - attrs.iter().filter(move |attr| attr.has_name(name)) - } - - pub fn first_attr_value_str_by_name( - &self, - attrs: &[Attribute], - name: Symbol, - ) -> Option<Symbol> { - attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) - } } +// JUSTIFICATION: part of session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, @@ -1148,7 +1183,7 @@ fn default_emitter( fallback_bundle: LazyFallbackBundle, emitter_dest: Option<Box<dyn Write + Send>>, ) -> Box<dyn Emitter + sync::Send> { - let macro_backtrace = sopts.debugging_opts.macro_backtrace; + let macro_backtrace = sopts.unstable_opts.macro_backtrace; match (sopts.error_format, emitter_dest) { (config::ErrorOutputType::HumanReadable(kind), dst) => { let (short, color_config) = kind.unzip(); @@ -1161,7 +1196,7 @@ fn default_emitter( short, macro_backtrace, ); - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( @@ -1170,7 +1205,7 @@ fn default_emitter( bundle, fallback_bundle, short, - sopts.debugging_opts.teach, + sopts.unstable_opts.teach, sopts.diagnostic_width, macro_backtrace, ), @@ -1186,7 +1221,7 @@ fn default_emitter( macro_backtrace, ), }; - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( @@ -1200,7 +1235,7 @@ fn default_emitter( sopts.diagnostic_width, macro_backtrace, ) - .ui_testing(sopts.debugging_opts.ui_testing), + .ui_testing(sopts.unstable_opts.ui_testing), ), (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( JsonEmitter::new( @@ -1214,7 +1249,7 @@ fn default_emitter( sopts.diagnostic_width, macro_backtrace, ) - .ui_testing(sopts.debugging_opts.ui_testing), + .ui_testing(sopts.unstable_opts.ui_testing), ), } } @@ -1224,6 +1259,8 @@ pub enum DiagnosticOutput { Raw(Box<dyn Write + Send>), } +// JUSTIFICATION: literally session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session( sopts: config::Options, local_crate_source_file: Option<PathBuf>, @@ -1267,7 +1304,7 @@ pub fn build_session( } let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); - let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| { + let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { if target_cfg.is_like_msvc { SourceFileHashAlgorithm::Sha1 } else { @@ -1282,17 +1319,17 @@ pub fn build_session( let fallback_bundle = fallback_fluent_bundle( rustc_errors::DEFAULT_LOCALE_RESOURCES, - sopts.debugging_opts.translate_directionality_markers, + sopts.unstable_opts.translate_directionality_markers, ); let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest); let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, - sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings), + sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings), ); - let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile + let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile { let directory = if let Some(ref directory) = d { directory } else { std::path::Path::new(".") }; @@ -1300,8 +1337,8 @@ pub fn build_session( let profiler = SelfProfiler::new( directory, sopts.crate_name.as_deref(), - sopts.debugging_opts.self_profile_events.as_ref().map(|xs| &xs[..]), - &sopts.debugging_opts.self_profile_counter, + sopts.unstable_opts.self_profile_events.as_ref().map(|xs| &xs[..]), + &sopts.unstable_opts.self_profile_counter, ); match profiler { Ok(profiler) => Some(Arc::new(profiler)), @@ -1315,7 +1352,7 @@ pub fn build_session( }; let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); - parse_sess.assume_incomplete_release = sopts.debugging_opts.assume_incomplete_release; + parse_sess.assume_incomplete_release = sopts.unstable_opts.assume_incomplete_release; let host_triple = config::host_triple(); let target_triple = sopts.target_triple.triple(); @@ -1334,22 +1371,19 @@ pub fn build_session( local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0); let optimization_fuel = Lock::new(OptimizationFuel { - remaining: sopts.debugging_opts.fuel.as_ref().map_or(0, |i| i.1), + remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |i| i.1), out_of_fuel: false, }); let print_fuel = AtomicU64::new(0); - let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { + let cgu_reuse_tracker = if sopts.unstable_opts.query_dep_graph { CguReuseTracker::new() } else { CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new( - self_profiler, - sopts.debugging_opts.time_passes || sopts.debugging_opts.time, - sopts.debugging_opts.time_passes, - ); + let prof = + SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, @@ -1390,6 +1424,7 @@ pub fn build_session( miri_unleashed_features: Lock::new(Default::default()), asm_arch, target_features: FxHashSet::default(), + unstable_target_features: FxHashSet::default(), }; validate_commandline_args_with_session_available(&sess); @@ -1397,8 +1432,12 @@ pub fn build_session( sess } -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. +/// Validate command line arguments with a `Session`. +/// +/// If it is useful to have a Session available already for validating a commandline argument, you +/// can do so here. +// JUSTIFICATION: needs to access args to validate them +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or // dynamically downstream, rustc generates `__imp_` symbols that help linkers @@ -1429,7 +1468,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // Do the same for sample profile data. - if let Some(ref path) = sess.opts.debugging_opts.profile_sample_use { + if let Some(ref path) = sess.opts.unstable_opts.profile_sample_use { if !path.exists() { sess.err(&format!( "File `{}` passed to `-C profile-sample-use` does not exist.", @@ -1450,7 +1489,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // Sanitizers can only be used on platforms that we know have working sanitizer codegen. let supported_sanitizers = sess.target.options.supported_sanitizers; - let unsupported_sanitizers = sess.opts.debugging_opts.sanitizer - supported_sanitizers; + let unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers; match unsupported_sanitizers.into_iter().count() { 0 => {} 1 => { @@ -1467,13 +1506,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } // Cannot mix and match sanitizers. - let mut sanitizer_iter = sess.opts.debugging_opts.sanitizer.into_iter(); + let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter(); if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) { sess.err(&format!("`-Zsanitizer={first}` is incompatible with `-Zsanitizer={second}`")); } // Cannot enable crt-static with sanitizers on Linux - if sess.crt_static(None) && !sess.opts.debugging_opts.sanitizer.is_empty() { + if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() { sess.err( "sanitizer is incompatible with statically linked libc, \ disable it using `-C target-feature=-crt-static`", @@ -1485,21 +1524,21 @@ fn validate_commandline_args_with_session_available(sess: &Session) { if sess.is_sanitizer_cfi_enabled() { sess.err("`-Zsanitizer=cfi` requires `-Clto`"); } - if sess.opts.debugging_opts.virtual_function_elimination { + if sess.opts.unstable_opts.virtual_function_elimination { sess.err("`-Zvirtual-function-elimination` requires `-Clto`"); } } - if sess.opts.debugging_opts.stack_protector != StackProtector::None { + if sess.opts.unstable_opts.stack_protector != StackProtector::None { if !sess.target.options.supports_stack_protector { sess.warn(&format!( "`-Z stack-protector={}` is not supported for target {} and will be ignored", - sess.opts.debugging_opts.stack_protector, sess.opts.target_triple + sess.opts.unstable_opts.stack_protector, sess.opts.target_triple )) } } - if let Some(dwarf_version) = sess.opts.debugging_opts.dwarf_version { + if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version { if dwarf_version > 5 { sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version)); } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3df4dfb74b3..e169d3c7cfb 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -108,7 +108,7 @@ fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) // which will cause us to require that this method always be called with `Span` hashing // enabled. HashingControls { hash_spans } - if hash_spans == !ctx.debug_opts_incremental_ignore_spans() => {} + if hash_spans == !ctx.unstable_opts_incremental_ignore_spans() => {} other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other), } } @@ -644,7 +644,10 @@ pub fn debug_hygiene_data(verbose: bool) -> String { let expn_data = expn_data.as_ref().expect("no expansion data for an expansion ID"); debug_expn_data((&id.to_expn_id(), expn_data)) }); + // Sort the hash map for more reproducible output. + // Because of this, it is fine to rely on the unstable iteration order of the map. + #[allow(rustc::potential_query_instability)] let mut foreign_expn_data: Vec<_> = data.foreign_expn_data.iter().collect(); foreign_expn_data.sort_by_key(|(id, _)| (id.krate, id.local_id)); foreign_expn_data.into_iter().for_each(debug_expn_data); @@ -1141,7 +1144,6 @@ pub enum DesugaringKind { Async, Await, ForLoop, - LetElse, WhileLoop, } @@ -1157,7 +1159,6 @@ impl DesugaringKind { DesugaringKind::YeetExpr => "`do yeet` expression", DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop => "`for` loop", - DesugaringKind::LetElse => "`let...else`", DesugaringKind::WhileLoop => "`while` loop", } } @@ -1210,6 +1211,7 @@ impl HygieneEncodeContext { // It's fine to iterate over a HashMap, because the serialization // of the table that we insert data into doesn't depend on insertion // order + #[allow(rustc::potential_query_instability)] for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { if self.serialized_ctxts.lock().insert(ctxt) { encode_ctxt(encoder, index, data); @@ -1218,6 +1220,8 @@ impl HygieneEncodeContext { let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; + // Same as above, this is fine as we are inserting into a order-independent hashset + #[allow(rustc::potential_query_instability)] for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { if self.serialized_expns.lock().insert(expn) { encode_expn(encoder, expn, data, hash); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index a1f34287a5f..cf306928151 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -20,7 +20,6 @@ #![feature(negative_impls)] #![feature(min_specialization)] #![feature(rustc_attrs)] -#![allow(rustc::potential_query_instability)] #[macro_use] extern crate rustc_macros; @@ -2029,9 +2028,9 @@ impl InnerSpan { pub trait HashStableContext { fn def_path_hash(&self, def_id: DefId) -> DefPathHash; fn hash_spans(&self) -> bool; - /// Accesses `sess.opts.debugging_opts.incremental_ignore_spans` since + /// Accesses `sess.opts.unstable_opts.incremental_ignore_spans` since /// we don't have easy access to a `Session` - fn debug_opts_incremental_ignore_spans(&self) -> bool; + fn unstable_opts_incremental_ignore_spans(&self) -> bool; fn def_span(&self, def_id: LocalDefId) -> Span; fn span_data_to_lines_and_cols( &mut self, diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index afbb88e9233..28381157d50 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -463,6 +463,33 @@ impl SourceMap { self.span_to_string(sp, FileNameDisplayPreference::Remapped) } + /// Format the span location suitable for pretty printing anotations with relative line numbers + pub fn span_to_relative_line_string(&self, sp: Span, relative_to: Span) -> String { + if self.files.borrow().source_files.is_empty() || sp.is_dummy() || relative_to.is_dummy() { + return "no-location".to_string(); + } + + let lo = self.lookup_char_pos(sp.lo()); + let hi = self.lookup_char_pos(sp.hi()); + let offset = self.lookup_char_pos(relative_to.lo()); + + if lo.file.name != offset.file.name { + return self.span_to_embeddable_string(sp); + } + + let lo_line = lo.line.saturating_sub(offset.line); + let hi_line = hi.line.saturating_sub(offset.line); + + format!( + "{}:+{}:{}: +{}:{}", + lo.file.name.display(FileNameDisplayPreference::Remapped), + lo_line, + lo.col.to_usize() + 1, + hi_line, + hi.col.to_usize() + 1, + ) + } + /// Format the span location to be printed in diagnostics. Must not be emitted /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string /// for string suitable for embedding. @@ -586,17 +613,6 @@ impl SourceMap { } } - /// Returns whether or not this span points into a file - /// in the current crate. This may be `false` for spans - /// produced by a macro expansion, or for spans associated - /// with the definition of an item in a foreign crate - pub fn is_local_span(&self, sp: Span) -> bool { - let local_begin = self.lookup_byte_offset(sp.lo()); - let local_end = self.lookup_byte_offset(sp.hi()); - // This might be a weird span that covers multiple files - local_begin.sf.src.is_some() && local_end.sf.src.is_some() - } - pub fn is_span_accessible(&self, sp: Span) -> bool { self.span_to_source(sp, |src, start_index, end_index| { Ok(src.get(start_index..end_index).is_some()) @@ -718,6 +734,11 @@ impl SourceMap { sp } + /// Extends the given `Span` to contain the entire line it is on. + pub fn span_extend_to_line(&self, sp: Span) -> Span { + self.span_extend_to_prev_char(self.span_extend_to_next_char(sp, '\n', true), '\n', true) + } + /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char` /// `c`. pub fn span_until_char(&self, sp: Span, c: char) -> Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9b6967621f1..060e7a7b90a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -223,6 +223,16 @@ symbols! { LintPass, Mutex, N, + NonZeroI128, + NonZeroI16, + NonZeroI32, + NonZeroI64, + NonZeroI8, + NonZeroU128, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU8, None, Ok, Option, @@ -257,6 +267,7 @@ symbols! { Result, Return, Right, + Rust, RustcDecodable, RustcEncodable, Send, @@ -341,6 +352,7 @@ symbols! { always, and, and_then, + anonymous_lifetime_in_impl_trait, any, append_const_msg, arbitrary_enum_discriminant, @@ -459,6 +471,7 @@ symbols! { clone_closures, clone_from, closure, + closure_lifetime_binder, closure_to_fn_coercion, closure_track_caller, cmp, @@ -683,6 +696,7 @@ symbols! { fabsf32, fabsf64, fadd_fast, + fake_variadic, fdiv_fast, feature, fence, @@ -786,6 +800,7 @@ symbols! { impl_lint_pass, impl_macros, impl_trait_in_bindings, + implied_by, import, import_shadowing, imported_main, @@ -980,7 +995,6 @@ symbols! { no_link, no_main, no_mangle, - no_niche, no_sanitize, no_stack_check, no_start, @@ -1153,7 +1167,6 @@ symbols! { repr128, repr_align, repr_align_enum, - repr_no_niche, repr_packed, repr_simd, repr_transparent, @@ -1189,8 +1202,10 @@ symbols! { rustc, rustc_allocator, rustc_allocator_nounwind, + rustc_allocator_zeroed, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, + rustc_allowed_through_unstable_modules, rustc_attrs, rustc_box, rustc_builtin_macro, @@ -1200,6 +1215,7 @@ symbols! { rustc_const_stable, rustc_const_unstable, rustc_conversion_suggestion, + rustc_deallocator, rustc_def_path, rustc_diagnostic_item, rustc_diagnostic_macros, @@ -1222,6 +1238,8 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_lint_diagnostics, + rustc_lint_opt_deny_field_access, + rustc_lint_opt_ty, rustc_lint_query_instability, rustc_macro_transparency, rustc_main, @@ -1244,6 +1262,7 @@ symbols! { rustc_private, rustc_proc_macro_decls, rustc_promotable, + rustc_reallocator, rustc_regions, rustc_reservation_impl, rustc_serialize, @@ -1270,6 +1289,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + shadow_call_stack, shl, shl_assign, should_panic, @@ -1434,6 +1454,8 @@ symbols! { thumb2, thumb_mode: "thumb-mode", tmm_reg, + to_string, + to_vec, todo_macro, tool_attributes, tool_lints, @@ -1457,7 +1479,6 @@ symbols! { tuple, tuple_from_req, tuple_indexing, - tuple_variadic, two_phase, ty, type_alias_enum_variants, @@ -1512,6 +1533,9 @@ symbols! { unsized_locals, unsized_tuple_coercion, unstable, + unstable_location_reason_default: "this crate is being loaded from the sysroot, an \ + unstable location; did you mean to load this crate \ + from crates.io via `Cargo.toml` instead?", untagged_unions, unused_imports, unused_qualifications, @@ -1550,6 +1574,8 @@ symbols! { volatile_store, vreg, vreg_low16, + vtable_align, + vtable_size, warn, wasm_abi, wasm_import_module, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index d5befa10e23..b104a40c231 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" doctest = false [dependencies] +bitflags = "1.2.1" tracing = "0.1" punycode = "0.4.0" rustc-demangle = "0.1.21" diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index e3045c9321d..9241fd82c74 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -67,7 +67,7 @@ pub(super) fn mangle<'tcx>( ) .unwrap(); - if let ty::InstanceDef::VtableShim(..) = instance.def { + if let ty::InstanceDef::VTableShim(..) = instance.def { let _ = printer.write_str("{{vtable-shim}}"); } @@ -129,7 +129,7 @@ fn get_symbol_hash<'tcx>( } // We want to avoid accidental collision between different types of instances. - // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original // instances without this. discriminant(&instance.def).hash_stable(hcx, &mut hasher); }); diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index bed0e81e66e..5fc992023ca 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -102,9 +102,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; -use rustc_target::abi::call::FnAbi; use tracing::debug; @@ -112,6 +111,7 @@ mod legacy; mod v0; pub mod test; +pub mod typeid; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is @@ -150,11 +150,6 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty ty::SymbolName::new(tcx, &symbol_name) } -/// This function computes the typeid for the given function ABI. -pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { - v0::mangle_typeid_for_fnabi(tcx, fn_abi) -} - pub fn typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyExistentialTraitRef<'tcx>, diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs new file mode 100644 index 00000000000..9228bea43f9 --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -0,0 +1,18 @@ +// For more information about type metadata and type metadata identifiers for cross-language LLVM +// CFI support, see Type metadata in the design document in the tracking issue #89653. + +use rustc_middle::ty::{FnSig, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; + +mod typeid_itanium_cxx_abi; +use typeid_itanium_cxx_abi::TypeIdOptions; + +/// Returns a type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { + typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS) +} + +/// Returns a type metadata identifier for the specified FnSig. +pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String { + typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS) +} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs new file mode 100644 index 00000000000..a09b52fbfdf --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -0,0 +1,929 @@ +// For more information about type metadata and type metadata identifiers for cross-language LLVM +// CFI support, see Type metadata in the design document in the tracking issue #89653. + +// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective +// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C" +// calling convention to use this encoding for cross-language LLVM CFI. + +use bitflags::bitflags; +use core::fmt::Display; +use rustc_data_structures::base_n; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{ + self, Binder, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, + Term, Ty, TyCtxt, UintTy, +}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::sym; +use rustc_target::abi::call::{Conv, FnAbi}; +use rustc_target::spec::abi::Abi; +use std::fmt::Write as _; + +/// Type and extended type qualifiers. +#[derive(Eq, Hash, PartialEq)] +enum TyQ { + None, + Const, + Mut, +} + +/// Substitution dictionary key. +#[derive(Eq, Hash, PartialEq)] +enum DictKey<'tcx> { + Ty(Ty<'tcx>, TyQ), + Region(Region<'tcx>), + Const(Const<'tcx>), + Predicate(ExistentialPredicate<'tcx>), +} + +bitflags! { + /// Options for typeid_for_fnabi and typeid_for_fnsig. + pub struct TypeIdOptions: u32 { + const NO_OPTIONS = 0; + const GENERALIZE_POINTERS = 1; + const GENERALIZE_REPR_C = 2; + } +} + +/// Options for encode_ty. +type EncodeTyOptions = TypeIdOptions; + +/// Options for transform_ty. +type TransformTyOptions = TypeIdOptions; + +/// Converts a number to a disambiguator (see +/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>). +fn to_disambiguator(num: u64) -> String { + if let Some(num) = num.checked_sub(1) { + format!("s{}_", base_n::encode(num as u128, 62)) + } else { + "s_".to_string() + } +} + +/// Converts a number to a sequence number (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>). +fn to_seq_id(num: usize) -> String { + if let Some(num) = num.checked_sub(1) { + base_n::encode(num as u128, 36).to_uppercase() + } else { + "".to_string() + } +} + +/// Substitutes a component if found in the substitution dictionary (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>). +fn compress<'tcx>( + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + key: DictKey<'tcx>, + comp: &mut String, +) { + match dict.get(&key) { + Some(num) => { + comp.clear(); + let _ = write!(comp, "S{}_", to_seq_id(*num)); + } + None => { + dict.insert(key, dict.len()); + } + } +} + +// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly +// along with other is_c_type methods. +/// Returns whether a `ty::Ty` is `c_void`. +fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(adt_def, ..) => { + let def_id = adt_def.0.did; + let crate_name = tcx.crate_name(def_id.krate); + if tcx.item_name(def_id).as_str() == "c_void" + && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc) + { + true + } else { + false + } + } + _ => false, + } +} + +/// Encodes a const using the Itanium C++ ABI as a literal argument (see +/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>). +fn encode_const<'tcx>( + tcx: TyCtxt<'tcx>, + c: Const<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // L<element-type>[n]<element-value>E as literal argument + let mut s = String::from('L'); + + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + + // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16, + // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1. + fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) { + if value < zero { + s.push('n') + }; + let _ = write!(s, "{}", value); + } + + fn push_unsigned_value<T: Display>(s: &mut String, value: T) { + let _ = write!(s, "{}", value); + } + + if let Some(scalar_int) = c.kind().try_to_scalar_int() { + let signed = c.ty().is_signed(); + match scalar_int.size().bits() { + 8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0), + 16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0), + 32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0), + 64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0), + 128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0), + 8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()), + 16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()), + 32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()), + 64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()), + 128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()), + _ => { + bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits()); + } + }; + } else { + bug!("encode_const: unexpected type `{:?}`", c.ty()); + } + + // Close the "L..E" pair + s.push('E'); + + compress(dict, DictKey::Const(c), &mut s); + + s +} + +/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_fnsig<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: &FnSig<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: TypeIdOptions, +) -> String { + // Function types are delimited by an "F..E" pair + let mut s = String::from("F"); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + match fn_sig.abi { + Abi::C { .. } => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + + // Encode the parameter types + let tys = fn_sig.inputs(); + if !tys.is_empty() { + for ty in tys { + let ty = transform_ty(tcx, *ty, transform_ty_options); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + } + + if fn_sig.c_variadic { + s.push('z'); + } + } else { + if fn_sig.c_variadic { + s.push('z'); + } else { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + s.push('v') + } + } + + // Close the "F..E" pair + s.push('E'); + + s +} + +/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: Binder<'tcx, ExistentialPredicate<'tcx>>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor + // extended type. + let mut s = String::new(); + match predicate.as_ref().skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let name = encode_ty_name(tcx, trait_ref.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options)); + } + ty::ExistentialPredicate::Projection(projection) => { + let name = encode_ty_name(tcx, projection.item_def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, projection.substs, dict, options)); + match projection.term { + Term::Ty(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + Term::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + } + }; + compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); + s +} + +/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List<Binder<'tcx, ExistentialPredicate<'tcx>>>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // <predicate1[..predicateN]>E as part of vendor extended type + let mut s = String::new(); + let predicates: Vec<Binder<'tcx, ExistentialPredicate<'tcx>>> = + predicates.iter().map(|predicate| predicate).collect(); + for predicate in predicates { + s.push_str(&encode_predicate(tcx, predicate, dict, options)); + } + s +} + +/// Encodes a region using the Itanium C++ ABI as a vendor extended type. +fn encode_region<'tcx>( + _tcx: TyCtxt<'tcx>, + region: Region<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + _options: EncodeTyOptions, +) -> String { + // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type + let mut s = String::new(); + match region.kind() { + RegionKind::ReLateBound(debruijn, r) => { + s.push_str("u6regionI"); + // Debruijn index, which identifies the binder, as region disambiguator + let num = debruijn.index() as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + // Index within the binder + let _ = write!(s, "{}", r.var.index() as u64); + s.push('E'); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReErased => { + s.push_str("u6region"); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReEarlyBound(..) + | RegionKind::ReFree(..) + | RegionKind::ReStatic + | RegionKind::ReVar(..) + | RegionKind::RePlaceholder(..) + | RegionKind::ReEmpty(..) => { + bug!("encode_region: unexpected `{:?}`", region.kind()); + } + } + s +} + +/// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust +/// types that are not used at the FFI boundary. +fn encode_substs<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + // [I<subst1..substN>E] as part of vendor extended type + let mut s = String::new(); + let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect(); + if !substs.is_empty() { + s.push('I'); + for subst in substs { + match subst.unpack() { + GenericArgKind::Lifetime(region) => { + s.push_str(&encode_region(tcx, region, dict, options)); + } + GenericArgKind::Type(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + GenericArgKind::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + s.push('E'); + } + s +} + +/// Encodes a ty:Ty name, including its crate and path disambiguators and names. +fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String { + // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where + // <element-type> is <subst>, using v0's <path> without v0's extended form of paths: + // + // N<namespace-tagN>..N<namespace-tag1> + // C<crate-disambiguator><crate-name> + // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN> + // + // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: + // + // pub type Type1 = impl Send; + // let _: Type1 = <Struct1<i32>>::foo; + // fn foo1(_: Type1) { } + // + // pub type Type2 = impl Send; + // let _: Type2 = <Trait1<i32>>::foo; + // fn foo2(_: Type2) { } + // + // pub type Type3 = impl Send; + // let _: Type3 = <i32 as Trait1<i32>>::foo; + // fn foo3(_: Type3) { } + // + // pub type Type4 = impl Send; + // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo; + // fn foo3(_: Type4) { } + // + // Are encoded as: + // + // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE + // + // The reason for not using v0's extended form of paths is to use a consistent and simpler + // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e., + // keep symbol names close to how methods are represented in error messages). See + // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. + let mut s = String::new(); + + // Start and namespace tags + let mut def_path = tcx.def_path(def_id); + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + s.push('N'); + s.push_str(match disambiguated_data.data { + hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace> + hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> + hir::definitions::DefPathData::TypeNs(..) => "t", + hir::definitions::DefPathData::ValueNs(..) => "v", + hir::definitions::DefPathData::ClosureExpr => "C", + hir::definitions::DefPathData::Ctor => "c", + hir::definitions::DefPathData::AnonConst => "k", + hir::definitions::DefPathData::ImplTrait => "i", + hir::definitions::DefPathData::CrateRoot + | hir::definitions::DefPathData::Use + | hir::definitions::DefPathData::GlobalAsm + | hir::definitions::DefPathData::MacroNs(..) + | hir::definitions::DefPathData::LifetimeNs(..) => { + bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); + } + }); + } + + // Crate disambiguator and name + s.push('C'); + s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64())); + let crate_name = tcx.crate_name(def_path.krate).to_string(); + let _ = write!(s, "{}{}", crate_name.len(), &crate_name); + + // Disambiguators and names + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + let num = disambiguated_data.disambiguator as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + + let name = disambiguated_data.data.to_string(); + let _ = write!(s, "{}", name.len()); + + // Prepend a '_' if name starts with a digit or '_' + if let Some(first) = name.as_bytes().get(0) { + if first.is_ascii_digit() || *first == b'_' { + s.push('_'); + } + } else { + bug!("encode_ty_name: invalid name `{:?}`", name); + } + + s.push_str(&name); + } + + s +} + +/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + dict: &mut FxHashMap<DictKey<'tcx>, usize>, + options: EncodeTyOptions, +) -> String { + let mut typeid = String::new(); + + match ty.kind() { + // Primitive types + ty::Bool => { + typeid.push('b'); + } + + ty::Int(..) | ty::Uint(..) | ty::Float(..) => { + // u<length><type-name> as vendor extended type + let mut s = String::from(match ty.kind() { + ty::Int(IntTy::I8) => "u2i8", + ty::Int(IntTy::I16) => "u3i16", + ty::Int(IntTy::I32) => "u3i32", + ty::Int(IntTy::I64) => "u3i64", + ty::Int(IntTy::I128) => "u4i128", + ty::Int(IntTy::Isize) => "u5isize", + ty::Uint(UintTy::U8) => "u2u8", + ty::Uint(UintTy::U16) => "u3u16", + ty::Uint(UintTy::U32) => "u3u32", + ty::Uint(UintTy::U64) => "u3u64", + ty::Uint(UintTy::U128) => "u4u128", + ty::Uint(UintTy::Usize) => "u5usize", + ty::Float(FloatTy::F32) => "u3f32", + ty::Float(FloatTy::F64) => "u3f64", + _ => "", + }); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Char => { + // u4char as vendor extended type + let mut s = String::from("u4char"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Str => { + // u3str as vendor extended type + let mut s = String::from("u3str"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Never => { + // u5never as vendor extended type + let mut s = String::from("u5never"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Compound types + // () in Rust is equivalent to void return type in C + _ if ty.is_unit() => { + typeid.push('v'); + } + + // Sequence types + ty::Tuple(tys) => { + // u5tupleI<element-type1..element-typeN>E as vendor extended type + let mut s = String::from("u5tupleI"); + for ty in tys.iter() { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Array(ty0, len) => { + // A<array-length><element-type> + let mut s = String::from("A"); + let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap()); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Slice(ty0) => { + // u5sliceI<element-type>E as vendor extended type + let mut s = String::from("u5sliceI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // User-defined types + ty::Adt(adt_def, substs) => { + let mut s = String::new(); + let def_id = adt_def.0.did; + if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { + // For for cross-language CFI support, the encoding must be compatible at the FFI + // boundary. For instance: + // + // struct type1 {}; + // void foo(struct type1* bar) {} + // + // Is encoded as: + // + // _ZTSFvP5type1E + // + // So, encode any repr(C) user-defined type for extern function types with the "C" + // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where + // <name> is <unscoped-name>. + let name = tcx.item_name(def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } else { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is + // <subst>, as vendor extended type. + let name = encode_ty_name(tcx, def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, substs, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } + typeid.push_str(&s); + } + + ty::Foreign(def_id) => { + // <length><name>, where <name> is <unscoped-name> + let mut s = String::new(); + let name = tcx.item_name(*def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Function types + ty::FnDef(def_id, substs) + | ty::Closure(def_id, substs) + | ty::Generator(def_id, substs, ..) => { + // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_substs(tcx, substs, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Pointer types + ty::Ref(region, ty0, ..) => { + // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type + let mut s = String::new(); + s.push_str("u3refI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s); + if ty.is_mutable_ptr() { + s = format!("{}{}", "U3mut", &s); + compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); + } + typeid.push_str(&s); + } + + ty::RawPtr(tm) => { + // P[K]<element-type> + let mut s = String::new(); + s.push_str(&encode_ty(tcx, tm.ty, dict, options)); + if !ty.is_mutable_ptr() { + s = format!("{}{}", "K", &s); + compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s); + }; + s = format!("{}{}", "P", &s); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::FnPtr(fn_sig) => { + // PF<return-type><parameter-type1..parameter-typeN>E + let mut s = String::from("P"); + s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Trait types + ty::Dynamic(predicates, region) => { + // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as + // vendor extended type. + let mut s = String::from("u3dynI"); + s.push_str(&encode_predicates(tcx, predicates, dict, options)); + s.push_str(&encode_region(tcx, *region, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Unexpected types + ty::Bound(..) + | ty::Error(..) + | ty::GeneratorWitness(..) + | ty::Infer(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Projection(..) => { + bug!("encode_ty: unexpected `{:?}`", ty.kind()); + } + }; + + typeid +} + +// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all +// c_void types into unit types unconditionally, and generalizes all pointers if +// TransformTyOptions::GENERALIZE_POINTERS option is set. +fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { + let mut ty = ty; + + match ty.kind() { + ty::Bool + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Char + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::Dynamic(..) => {} + + _ if ty.is_unit() => {} + + ty::Tuple(tys) => { + ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options))); + } + + ty::Array(ty0, len) => { + let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap(); + ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len); + } + + ty::Slice(ty0) => { + ty = tcx.mk_slice(transform_ty(tcx, *ty0, options)); + } + + ty::Adt(adt_def, substs) => { + if is_c_void_ty(tcx, ty) { + ty = tcx.mk_unit(); + } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() + { + ty = tcx.mk_adt(*adt_def, ty::List::empty()); + } else if adt_def.repr().transparent() && adt_def.is_struct() { + let variant = adt_def.non_enum_variant(); + let param_env = tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = tcx.type_of(field.did); + let is_zst = + tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst()); + !is_zst + }); + if field.is_none() { + // Transform repr(transparent) types without non-ZST field into () + ty = tcx.mk_unit(); + } else { + let ty0 = tcx.type_of(field.unwrap().did); + // Generalize any repr(transparent) user-defined type that is either a pointer + // or reference, and either references itself or any other type that contains or + // references itself, to avoid a reference cycle. + if ty0.is_any_ptr() && ty0.contains(ty) { + ty = transform_ty( + tcx, + ty0, + options | TransformTyOptions::GENERALIZE_POINTERS, + ); + } else { + ty = transform_ty(tcx, ty0, options); + } + } + } else { + ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options)); + } + } + + ty::FnDef(def_id, substs) => { + ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options)); + } + + ty::Closure(def_id, substs) => { + ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options)); + } + + ty::Generator(def_id, substs, movability) => { + ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability); + } + + ty::Ref(region, ty0, ..) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit()); + } else { + ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit()); + } + } else { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options)); + } else { + ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options)); + } + } + } + + ty::RawPtr(tm) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ptr(tcx.mk_unit()); + } else { + ty = tcx.mk_imm_ptr(tcx.mk_unit()); + } + } else { + if ty.is_mutable_ptr() { + ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options)); + } else { + ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options)); + } + } + } + + ty::FnPtr(fn_sig) => { + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + ty = tcx.mk_imm_ptr(tcx.mk_unit()); + } else { + let parameters: Vec<Ty<'tcx>> = fn_sig + .skip_binder() + .inputs() + .iter() + .map(|ty| transform_ty(tcx, *ty, options)) + .collect(); + let output = transform_ty(tcx, fn_sig.skip_binder().output(), options); + ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars( + tcx.mk_fn_sig( + parameters.iter(), + &output, + fn_sig.c_variadic(), + fn_sig.unsafety(), + fn_sig.abi(), + ), + fn_sig.bound_vars(), + )); + } + } + + ty::Bound(..) + | ty::Error(..) + | ty::GeneratorWitness(..) + | ty::Infer(..) + | ty::Opaque(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Projection(..) => { + bug!("transform_ty: unexpected `{:?}`", ty.kind()); + } + } + + ty +} + +/// Transforms substs for being encoded and used in the substitution dictionary. +fn transform_substs<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + options: TransformTyOptions, +) -> SubstsRef<'tcx> { + let substs: Vec<GenericArg<'tcx>> = substs + .iter() + .map(|subst| { + if let GenericArgKind::Type(ty) = subst.unpack() { + if is_c_void_ty(tcx, ty) { + tcx.mk_unit().into() + } else { + transform_ty(tcx, ty, options).into() + } + } else { + subst + } + }) + .collect(); + tcx.mk_substs(substs.iter()) +} + +/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // Function types are delimited by an "F..E" pair + typeid.push('F'); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + match fn_abi.conv { + Conv::C => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + + // Encode the parameter types + if !fn_abi.c_variadic { + if !fn_abi.args.is_empty() { + for arg in fn_abi.args.iter() { + let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + } else { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + typeid.push('v'); + } + } else { + for n in 0..fn_abi.fixed_count { + let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + + typeid.push('z'); + } + + // Close the "F..E" pair + typeid.push('E'); + + typeid +} + +/// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_fnsig<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: &FnSig<'tcx>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); + + // Encode the function signature + typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options)); + + typeid +} diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 13229a3995c..71fa5a44887 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -12,7 +12,6 @@ use rustc_middle::ty::{ self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy, }; use rustc_span::symbol::kw; -use rustc_target::abi::call::FnAbi; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; @@ -42,7 +41,7 @@ pub(super) fn mangle<'tcx>( // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. let shim_kind = match instance.def { - ty::InstanceDef::VtableShim(_) => Some("vtable"), + ty::InstanceDef::VTableShim(_) => Some("vtable"), ty::InstanceDef::ReifyShim(_) => Some("reify"), _ => None, @@ -59,41 +58,6 @@ pub(super) fn mangle<'tcx>( std::mem::take(&mut cx.out) } -pub(super) fn mangle_typeid_for_fnabi<'tcx>( - _tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, -) -> String { - // LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This - // type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is - // associated with a type identifier (i.e., test type membership). - // - // Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as - // type metadata identifiers for function pointers. The typeinfo name encoding is a - // two-character code (i.e., “TS”) prefixed to the type encoding for the function. - // - // For cross-language LLVM CFI support, a compatible encoding must be used by either - // - // a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's - // type encodings[4]), or at least types used at the FFI boundary. - // b. Reducing the types to the least common denominator between types used by Clang (or at - // least types used at the FFI boundary) and Rust compilers (if even possible). - // c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and - // possibly other compilers). - // - // Option (b) may weaken the protection for Rust-compiled only code, so it should be provided - // as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled - // code. Option (c) would require changes to Clang to use the new ABI. - // - // [1] https://llvm.org/docs/TypeMetadata.html - // [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html - // [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables - // [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type - // - // FIXME(rcvalle): See comment above. - let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; - format!("typeid{}", arg_count) -} - pub(super) fn mangle_typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyExistentialTraitRef<'tcx>, diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index d1eafd6ac5f..b35502d9ee4 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1350,15 +1350,19 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum PointerKind { /// Most general case, we know no restrictions to tell LLVM. - Shared, + SharedMutable, - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + /// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`. Frozen, - /// `&mut T` which is `noalias` but not `readonly`. + /// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`. UniqueBorrowed, - /// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + /// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`. + UniqueBorrowedPinned, + + /// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly` + /// nor `dereferenceable`. UniqueOwned, } @@ -1372,7 +1376,7 @@ pub struct PointeeInfo { /// Used in `might_permit_raw_init` to indicate the kind of initialisation /// that is checked to be valid -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InitKind { Zero, Uninit, @@ -1487,14 +1491,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// /// `init_kind` indicates if the memory is zero-initialized or left uninitialized. /// - /// `strict` is an opt-in debugging flag added in #97323 that enables more checks. + /// This code is intentionally conservative, and will not detect + /// * zero init of an enum whose 0 variant does not allow zero initialization + /// * making uninitialized types who have a full valid range (ints, floats, raw pointers) + /// * Any form of invalid value being made inside an array (unless the value is uninhabited) /// - /// This is conservative: in doubt, it will answer `true`. + /// A strict form of these checks that uses const evaluation exists in + /// `rustc_const_eval::might_permit_raw_init`, and a tracking issue for making these checks + /// stricter is <https://github.com/rust-lang/rust/issues/66151>. /// - /// FIXME: Once we removed all the conservatism, we could alternatively - /// create an all-0/all-undef constant and run the const value validator to see if - /// this is a valid value for the given type. - pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool + /// FIXME: Once all the conservatism is removed from here, and the checks are ran by default, + /// we can use the const evaluation checks always instead. + pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool where Self: Copy, Ty: TyAbiInterface<'a, C>, @@ -1507,13 +1515,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { s.valid_range(cx).contains(0) } InitKind::Uninit => { - if strict { - // The type must be allowed to be uninit (which means "is a union"). - s.is_uninit_valid() - } else { - // The range must include all values. - s.is_always_valid(cx) - } + // The range must include all values. + s.is_always_valid(cx) } } }; @@ -1534,19 +1537,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { // If we have not found an error yet, we need to recursively descend into fields. match &self.fields { FieldsShape::Primitive | FieldsShape::Union { .. } => {} - FieldsShape::Array { count, .. } => { + FieldsShape::Array { .. } => { // FIXME(#66151): For now, we are conservative and do not check arrays by default. - if strict - && *count > 0 - && !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict) - { - // Found non empty array with a type that is unhappy about this kind of initialization - return false; - } } FieldsShape::Arbitrary { offsets, .. } => { for idx in 0..offsets.len() { - if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) { + if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) { // We found a field that is unhappy with this kind of initialization. return false; } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 25842049413..62a0f9fb034 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index aaa632333db..0db3eb6fcac 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index 987bf970529..e41bdc9a58c 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index e35035fd25a..238c365093f 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,6 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::{RelocModel, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index 5e31859aaef..c85f7f62a42 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS | SanitizerSet::MEMTAG + | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, ..super::android_base::opts() }, diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs new file mode 100644 index 00000000000..1b7161fbb85 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs @@ -0,0 +1,26 @@ +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelroLevel, Target, TargetOptions}; + +const LINKER_SCRIPT: &str = include_str!("./aarch64_nintendo_switch_freestanding_linker_script.ld"); + +/// A base target for Nintendo Switch devices using a pure LLVM toolchain. +pub fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-none".into(), + pointer_width: 64, + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), + arch: "aarch64".into(), + options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + linker: Some("rust-lld".into()), + link_script: Some(LINKER_SCRIPT.into()), + os: "horizon".into(), + max_atomic_width: Some(128), + panic_strategy: PanicStrategy::Abort, + position_independent_executables: true, + dynamic_linking: true, + executables: true, + relro_level: RelroLevel::Off, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld new file mode 100644 index 00000000000..f3441e65937 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding_linker_script.ld @@ -0,0 +1,78 @@ +OUTPUT_FORMAT(elf64-littleaarch64) +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + bss PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC; +} + +SECTIONS +{ + . = 0; + + .text : ALIGN(0x1000) { + HIDDEN(__text_start = .); + KEEP(*(.text.jmp)) + + . = 0x80; + + *(.text .text.*) + *(.plt .plt.*) + } + + /* Read-only sections */ + + . = ALIGN(0x1000); + + .module_name : { *(.module_name) } :rodata + + .rodata : { *(.rodata .rodata.*) } :rodata + .hash : { *(.hash) } + .dynsym : { *(.dynsym .dynsym.*) } + .dynstr : { *(.dynstr .dynstr.*) } + .rela.dyn : { *(.rela.dyn) } + + .eh_frame : { + HIDDEN(__eh_frame_start = .); + *(.eh_frame .eh_frame.*) + HIDDEN(__eh_frame_end = .); + } + + .eh_frame_hdr : { + HIDDEN(__eh_frame_hdr_start = .); + *(.eh_frame_hdr .eh_frame_hdr.*) + HIDDEN(__eh_frame_hdr_end = .); + } + + /* Read-write sections */ + + . = ALIGN(0x1000); + + .data : { + *(.data .data.*) + *(.got .got.*) + *(.got.plt .got.plt.*) + } :data + + .dynamic : { + HIDDEN(__dynamic_start = .); + *(.dynamic) + } + + /* BSS section */ + + . = ALIGN(0x1000); + + .bss : { + HIDDEN(__bss_start = .); + *(.bss .bss.*) + *(COMMON) + . = ALIGN(8); + HIDDEN(__bss_end = .); + } :bss +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index 2c7834c225b..d3fd7051a12 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+strict-align,+neon,+fp-armv8".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index 1b6525a7c69..6316abe1ba9 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+strict-align,-neon,-fp-armv8".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 713dc9a1f0e..9bfae46ef32 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -25,7 +25,6 @@ pub fn opts(os: &'static str) -> TargetOptions { function_sections: false, dynamic_linking: true, linker_is_gnu: false, - executables: true, families: cvs!["unix"], is_like_osx: true, default_dwarf_version: 2, diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index ecb6cbd9f8a..0328ea98c48 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -54,7 +54,6 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { abi: target_abi(arch).into(), cpu: target_cpu(arch).into(), dynamic_linking: false, - executables: true, link_env_remove: link_env_remove(arch), has_thread_local: false, ..super::apple_base::opts(os) diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 0cb18f17310..511693abe98 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { abi: "eabi".into(), endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index a5b7c12cc7b..5df4a0a1583 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { abi: "eabihf".into(), endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs index 8c2a9bcfde6..1bba3939397 100644 --- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs @@ -23,7 +23,6 @@ pub fn target() -> Target { abi: "eabihf".into(), linker_flavor: LinkerFlavor::Gcc, cpu: "mpcore".into(), - executables: true, families: cvs!["unix"], linker: Some("arm-none-eabi-gcc".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs index ff649434312..cb5cbe15836 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -22,7 +22,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index c0321d0bef4..fb5dd2e7574 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 2c3f79cc58b..5f1da09b317 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { options: TargetOptions { abi: "eabi".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 5c82e768483..0038ed0df8b 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { options: TargetOptions { abi: "eabihf".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 4fd6c06394d..1d441e558dd 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -16,7 +16,6 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { exe_suffix: ".elf".into(), linker: Some("avr-gcc".into()), - executables: true, eh_frame_header: false, pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]), late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]), diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs index 4fa6e12f5ba..3c4da6f883d 100644 --- a/compiler/rustc_target/src/spec/bpf_base.rs +++ b/compiler/rustc_target/src/spec/bpf_base.rs @@ -7,7 +7,6 @@ pub fn opts(endian: Endian) -> TargetOptions { endian, linker_flavor: LinkerFlavor::BpfLinker, atomic_cas: false, - executables: true, dynamic_linking: true, no_builtins: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index c1e469746cb..de2be781796 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "dragonfly".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index 36312d26e37..8c141aaaec3 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "freebsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index b02b70f76ee..df1e3275f73 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -23,7 +23,6 @@ pub fn opts() -> TargetOptions { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), dynamic_linking: true, - executables: true, families: cvs!["unix"], pre_link_args, pre_link_objects: crt_objects::new(&[ diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs index 61c05a2bdb6..8ab874410aa 100644 --- a/compiler/rustc_target/src/spec/haiku_base.rs +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "haiku".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], relro_level: RelroLevel::Full, ..Default::default() diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index e43153177f0..562ccef7eba 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -10,7 +10,6 @@ pub fn opts() -> TargetOptions { os: "hermit".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), - executables: true, has_thread_local: true, pre_link_args, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs index 80cf09517cc..cc2c78c69fe 100644 --- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { base.has_rpath = true; base.linker_is_gnu = false; base.dynamic_linking = true; - base.executables = true; base.c_enum_min_bits = 8; diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs index b0e1b109be1..77e000474b8 100644 --- a/compiler/rustc_target/src/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -27,7 +27,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "illumos".into(), dynamic_linking: true, - executables: true, has_rpath: true, families: cvs!["unix"], is_like_solaris: true, diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index 7a051532f82..a08756861e5 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions { os: "l4re".into(), env: "uclibc".into(), linker_flavor: LinkerFlavor::L4Bender, - executables: true, panic_strategy: PanicStrategy::Abort, linker: Some("l4-bender".into()), linker_is_gnu: false, diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 0f79ada0d93..f4fce3b4050 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "linux".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs index e3522de6de0..cfc8ec21c2a 100644 --- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -18,7 +18,6 @@ pub fn target() -> Target { vendor: "sony".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), cpu: "mips2".into(), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index 736af15cf44..fe2aa2de871 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -17,7 +17,6 @@ pub fn target() -> Target { cpu: "mips32r2".into(), features: "+mips32r2,+soft-float,+noabicalls".into(), max_atomic_width: Some(32), - executables: true, linker: Some("rust-lld".into()), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 48ccb10f214..f7abeafd38f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -618,6 +618,7 @@ bitflags::bitflags! { const HWADDRESS = 1 << 4; const CFI = 1 << 5; const MEMTAG = 1 << 6; + const SHADOWCALLSTACK = 1 << 7; } } @@ -632,6 +633,7 @@ impl SanitizerSet { SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::MEMTAG => "memtag", + SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", _ => return None, @@ -666,6 +668,7 @@ impl IntoIterator for SanitizerSet { SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::MEMTAG, + SanitizerSet::SHADOWCALLSTACK, SanitizerSet::THREAD, SanitizerSet::HWADDRESS, ] @@ -1035,6 +1038,8 @@ supported_targets! { ("armv6k-nintendo-3ds", armv6k_nintendo_3ds), + ("aarch64-nintendo-switch-freestanding", aarch64_nintendo_switch_freestanding), + ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi), ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf), @@ -1212,8 +1217,7 @@ pub struct TargetOptions { pub dynamic_linking: bool, /// If dynamic linking is available, whether only cdylibs are supported. pub only_cdylib: bool, - /// Whether executables are available on this target. iOS, for example, only allows static - /// libraries. Defaults to false. + /// Whether executables are available on this target. Defaults to true. pub executables: bool, /// Relocation model to use in object file. Corresponds to `llc /// -relocation-model=$relocation_model`. Defaults to `Pic`. @@ -1520,7 +1524,7 @@ impl Default for TargetOptions { features: "".into(), dynamic_linking: false, only_cdylib: false, - executables: false, + executables: true, relocation_model: RelocModel::Pic, code_model: None, tls_model: TlsModel::GeneralDynamic, @@ -1959,6 +1963,7 @@ impl Target { Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, Some("memtag") => SanitizerSet::MEMTAG, + Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, Some("thread") => SanitizerSet::THREAD, Some("hwaddress") => SanitizerSet::HWADDRESS, Some(s) => return Err(format!("unknown sanitizer {}", s)), diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs index cedacb60f31..6b09386ae3e 100644 --- a/compiler/rustc_target/src/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -9,7 +9,6 @@ pub fn target() -> Target { options: TargetOptions { c_int_width: "16".into(), - executables: true, // The LLVM backend currently can't generate object files. To // workaround this LLVM generates assembly files which then we feed diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index c4df4b546e3..edb30b72bf6 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions { TargetOptions { linker_flavor: LinkerFlavor::Msvc, - executables: true, is_like_windows: true, is_like_msvc: true, lld_flavor: LldFlavor::Link, diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index 40ef04ba043..be94ea23465 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "netbsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], no_default_libraries: false, has_rpath: true, diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 9d94ed8aa48..1c5b68001b9 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -26,7 +26,6 @@ pub fn target() -> Target { // Needed to use `dylib` and `bin` crate types and the linker. dynamic_linking: true, - executables: true, // Avoid using dylib because it contain metadata not supported // by LLVM NVPTX backend. diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index 51cecdd47ea..e7db14e05a4 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "openbsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, abi_return_struct_as_int: true, diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs index 1878cc3fc11..468fe478549 100644 --- a/compiler/rustc_target/src/spec/redox_base.rs +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions { os: "redox".into(), env: "relibc".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 7124e2df9b3..232139db6ca 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(0), atomic_cas: false, - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs index 508982eed68..3e5d2887f43 100644 --- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { max_atomic_width: Some(0), atomic_cas: false, features: "+m".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs index f2bd6249f0a..99317b9f118 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs index b46ca159370..a5de645c984 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, ..Default::default() diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs index 0200862c7e0..03baef65c0d 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs @@ -26,7 +26,6 @@ pub fn target() -> Target { atomic_cas: true, features: "+m,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index 4216968cb77..bf510d204a7 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { max_atomic_width: Some(0), atomic_cas: false, features: "+m,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 2a93459ef4f..03b3cfd1eb1 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+f,+d,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index 6a8d8a97de6..2a94c9dd233 100644 --- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs index d61e1b2ec10..b7e8e8cf7f5 100644 --- a/compiler/rustc_target/src/spec/solaris_base.rs +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "solaris".into(), dynamic_linking: true, - executables: true, has_rpath: true, families: cvs!["unix"], is_like_solaris: true, diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs index c5602a4513a..c585a6cd58e 100644 --- a/compiler/rustc_target/src/spec/solid_base.rs +++ b/compiler/rustc_target/src/spec/solid_base.rs @@ -5,6 +5,7 @@ pub fn opts(kernel: &str) -> TargetOptions { TargetOptions { os: format!("solid_{}", kernel).into(), vendor: "kmc".into(), + executables: false, frame_pointer: FramePointer::NonLeaf, has_thread_local: true, ..Default::default() diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index ef6038e6120..049142b89f1 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults TargetOptions { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, // In most cases, LLD is good enough linker: Some("rust-lld".into()), // Because these devices have very little resources having an unwinder is too onerous so we diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs index 2beb279e398..aa4784b63e7 100644 --- a/compiler/rustc_target/src/spec/vxworks_base.rs +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions { linker: Some("wr-c++".into()), exe_suffix: ".vxe".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, has_thread_local: true, diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 5736402ae14..9216d3e7b65 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -62,9 +62,6 @@ pub fn options() -> TargetOptions { dynamic_linking: true, only_cdylib: true, - // This means we'll just embed a `#[start]` function in the wasm module - executables: true, - // relatively self-explanatory! exe_suffix: ".wasm".into(), dll_prefix: "".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index a0480f386f7..90e0af3e38a 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -67,7 +67,6 @@ pub fn opts() -> TargetOptions { function_sections: false, linker: Some("gcc".into()), dynamic_linking: true, - executables: true, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index 30f995007a9..bae007dc9f3 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions { abi: "llvm".into(), linker: Some("clang".into()), dynamic_linking: true, - executables: true, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs index 334dec43ef7..fa69b919cec 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -24,7 +24,6 @@ pub fn opts() -> TargetOptions { TargetOptions { abi: "uwp".into(), vendor: "uwp".into(), - executables: false, limit_rdylib_exports: false, late_link_args, late_link_args_dynamic, diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs index 4348d924579..9d597ea2e62 100644 --- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -62,7 +62,6 @@ pub fn target() -> Target { vendor: "fortanix".into(), abi: "fortanix".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), max_atomic_width: Some(64), cpu: "x86-64".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs index 0c510dfaa12..809fd642d41 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs @@ -24,7 +24,6 @@ pub fn target() -> Target { features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" .into(), - executables: true, disable_redzone: true, panic_strategy: PanicStrategy::Abort, code_model: Some(CodeModel::Kernel), diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 282ee632ce5..c33629192cb 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -17,7 +17,7 @@ #![feature(drain_filter)] #![feature(hash_drain_filter)] #![feature(label_break_value)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(if_let_guard)] #![feature(never_type)] diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 65ff9ceb67e..5763e6d1b55 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -793,9 +793,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::RegionOutlives(binder) => { let binder = bound_predicate.rebind(binder); - if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { - return false; - } + select.infcx().region_outlives_predicate(&dummy_cause, binder) } ty::PredicateKind::TypeOutlives(binder) => { let binder = bound_predicate.rebind(binder); diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 6ca630b74cc..5fcaa52d417 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -3,7 +3,7 @@ // seems likely that they should eventually be merged into more // general routines. -use crate::infer::TyCtxtInferExt; +use crate::infer::{DefiningAnchor, TyCtxtInferExt}; use crate::traits::{ FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, Unimplemented, @@ -30,7 +30,11 @@ pub fn codegen_fulfill_obligation<'tcx>( // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt().enter(|infcx| { + let mut infcx_builder = + tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble); + infcx_builder.enter(|infcx| { + //~^ HACK `Bubble` is required for + // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs let mut selcx = SelectionContext::new(&infcx); let obligation_cause = ObligationCause::dummy(); @@ -69,7 +73,8 @@ pub fn codegen_fulfill_obligation<'tcx>( // Opaque types may have gotten their hidden types constrained, but we can ignore them safely // as they will get constrained elsewhere, too. - let _opaque_types = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); debug!("Cache miss: {trait_ref:?} => {impl_source:?}"); Ok(&*tcx.arena.alloc(impl_source)) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 52ca23c4b30..fa94aa19abd 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -22,11 +22,12 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; use std::iter; +use std::ops::ControlFlow; /// Whether we do the orphan check relative to this crate or /// to some remote crate. @@ -578,220 +579,175 @@ fn orphan_check_trait_ref<'tcx>( ); } - // Given impl<P1..=Pn> Trait<T1..=Tn> for T0, an impl is valid only - // if at least one of the following is true: - // - // - Trait is a local trait - // (already checked in orphan_check prior to calling this function) - // - All of - // - At least one of the types T0..=Tn must be a local type. - // Let Ti be the first such type. - // - No uncovered type parameters P1..=Pn may appear in T0..Ti (excluding Ti) - // - fn uncover_fundamental_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - in_crate: InCrate, - ) -> Vec<Ty<'tcx>> { - // FIXME: this is currently somewhat overly complicated, - // but fixing this requires a more complicated refactor. - if !contained_non_local_types(tcx, ty, in_crate).is_empty() { - if let Some(inner_tys) = fundamental_ty_inner_tys(tcx, ty) { - return inner_tys - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .collect(); + let mut checker = OrphanChecker::new(tcx, in_crate); + match trait_ref.visit_with(&mut checker) { + ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = + trait_ref.visit_with(&mut checker).break_value() + { + Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty))) + } else { + Err(OrphanCheckErr::UncoveredTy(ty, None)) } } - - vec![ty] - } - - let mut non_local_spans = vec![]; - for (i, input_ty) in trait_ref - .substs - .types() - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .enumerate() - { - debug!("orphan_check_trait_ref: check ty `{:?}`", input_ty); - let non_local_tys = contained_non_local_types(tcx, input_ty, in_crate); - if non_local_tys.is_empty() { - debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); - return Ok(()); - } else if let ty::Param(_) = input_ty.kind() { - debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); - let local_type = trait_ref - .substs - .types() - .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|&ty| ty_is_local_constructor(tcx, ty, in_crate)); - - debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); - - return Err(OrphanCheckErr::UncoveredTy(input_ty, local_type)); - } - - non_local_spans.extend(non_local_tys.into_iter().map(|input_ty| (input_ty, i == 0))); + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), } - // If we exit above loop, never found a local type. - debug!("orphan_check_trait_ref: no local type"); - Err(OrphanCheckErr::NonLocalInputType(non_local_spans)) } -/// Returns a list of relevant non-local types for `ty`. -/// -/// This is just `ty` itself unless `ty` is `#[fundamental]`, -/// in which case we recursively look into this type. -/// -/// If `ty` is local itself, this method returns an empty `Vec`. -/// -/// # Examples -/// -/// - `u32` is not local, so this returns `[u32]`. -/// - for `Foo<u32>`, where `Foo` is a local type, this returns `[]`. -/// - `&mut u32` returns `[u32]`, as `&mut` is a fundamental type, similar to `Box`. -/// - `Box<Foo<u32>>` returns `[]`, as `Box` is a fundamental type and `Foo` is local. -fn contained_non_local_types<'tcx>( +struct OrphanChecker<'tcx> { tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, in_crate: InCrate, -) -> Vec<Ty<'tcx>> { - if ty_is_local_constructor(tcx, ty, in_crate) { - Vec::new() - } else { - match fundamental_ty_inner_tys(tcx, ty) { - Some(inner_tys) => { - inner_tys.flat_map(|ty| contained_non_local_types(tcx, ty, in_crate)).collect() - } - None => vec![ty], + in_self_ty: bool, + /// Ignore orphan check failures and exclusively search for the first + /// local type. + search_first_local_ty: bool, + non_local_tys: Vec<(Ty<'tcx>, bool)>, +} + +impl<'tcx> OrphanChecker<'tcx> { + fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self { + OrphanChecker { + tcx, + in_crate, + in_self_ty: true, + search_first_local_ty: false, + non_local_tys: Vec::new(), } } -} -/// For `#[fundamental]` ADTs and `&T` / `&mut T`, returns `Some` with the -/// type parameters of the ADT, or `T`, respectively. For non-fundamental -/// types, returns `None`. -fn fundamental_ty_inner_tys<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option<impl Iterator<Item = Ty<'tcx>>> { - let (first_ty, rest_tys) = match *ty.kind() { - ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()), - ty::Adt(def, substs) if def.is_fundamental() => { - let mut types = substs.types(); - - // FIXME(eddyb) actually validate `#[fundamental]` up-front. - match types.next() { - None => { - tcx.sess.span_err( - tcx.def_span(def.did()), - "`#[fundamental]` requires at least one type parameter", - ); - - return None; - } + fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> { + self.non_local_tys.push((t, self.in_self_ty)); + ControlFlow::CONTINUE + } - Some(first_ty) => (first_ty, types), - } + fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> { + if self.search_first_local_ty { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) } - _ => return None, - }; + } - Some(iter::once(first_ty).chain(rest_tys)) + fn def_id_is_local(&mut self, def_id: DefId) -> bool { + match self.in_crate { + InCrate::Local => def_id.is_local(), + InCrate::Remote => false, + } + } } -fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { - match in_crate { - // The type is local to *this* crate - it will not be - // local in any other crate. - InCrate::Remote => false, - InCrate::Local => def_id.is_local(), - } +enum OrphanCheckEarlyExit<'tcx> { + ParamTy(Ty<'tcx>), + LocalTy(Ty<'tcx>), } -fn ty_is_local_constructor(tcx: TyCtxt<'_>, ty: Ty<'_>, in_crate: InCrate) -> bool { - debug!("ty_is_local_constructor({:?})", ty); - - match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) - | ty::Param(..) - | ty::Projection(..) => false, - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match in_crate { - InCrate::Local => false, - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => true, - }, - - ty::Adt(def, _) => def_id_is_local(def.did(), in_crate), - ty::Foreign(did) => def_id_is_local(did, in_crate), - ty::Opaque(..) => { - // This merits some explanation. - // Normally, opaque types are not involved when performing - // coherence checking, since it is illegal to directly - // implement a trait on an opaque type. However, we might - // end up looking at an opaque type during coherence checking - // if an opaque type gets used within another type (e.g. as - // a type parameter). This requires us to decide whether or - // not an opaque type should be considered 'local' or not. - // - // We choose to treat all opaque types as non-local, even - // those that appear within the same crate. This seems - // somewhat surprising at first, but makes sense when - // you consider that opaque types are supposed to hide - // the underlying type *within the same crate*. When an - // opaque type is used from outside the module - // where it is declared, it should be impossible to observe - // anything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type - // to determine whether or not the opaque type itself should - // be considered local. However, this could make it a breaking change - // to switch the underlying ('defining') type from a local type - // to a remote type. This would violate the rule that opaque - // types should be completely opaque apart from the traits - // that they implement, so we don't use this behavior. - false - } +impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { + type BreakTy = OrphanCheckEarlyExit<'tcx>; + fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::CONTINUE + } - ty::Dynamic(ref tt, ..) => { - if let Some(principal) = tt.principal() { - def_id_is_local(principal.def_id(), in_crate) - } else { - false + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + let result = match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) + | ty::Projection(..) => self.found_non_local_ty(ty), + + ty::Param(..) => self.found_param_ty(ty), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { + InCrate::Local => self.found_non_local_ty(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + }, + + // For fundamental types, we just look inside of them. + ty::Ref(_, ty, _) => ty.visit_with(self), + ty::Adt(def, substs) => { + if self.def_id_is_local(def.did()) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else if def.is_fundamental() { + substs.visit_with(self) + } else { + self.found_non_local_ty(ty) + } } - } + ty::Foreign(def_id) => { + if self.def_id_is_local(def_id) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Dynamic(tt, ..) => { + let principal = tt.principal().map(|p| p.def_id()); + if principal.map_or(false, |p| self.def_id_is_local(p)) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { + self.tcx.sess.delay_span_bug( + DUMMY_SP, + format!("ty_is_local invoked on closure or generator: {:?}", ty), + ); + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + ty::Opaque(..) => { + // This merits some explanation. + // Normally, opaque types are not involved when performing + // coherence checking, since it is illegal to directly + // implement a trait on an opaque type. However, we might + // end up looking at an opaque type during coherence checking + // if an opaque type gets used within another type (e.g. as + // the type of a field) when checking for auto trait or `Sized` + // impls. This requires us to decide whether or not an opaque + // type should be considered 'local' or not. + // + // We choose to treat all opaque types as non-local, even + // those that appear within the same crate. This seems + // somewhat surprising at first, but makes sense when + // you consider that opaque types are supposed to hide + // the underlying type *within the same crate*. When an + // opaque type is used from outside the module + // where it is declared, it should be impossible to observe + // anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type + // to determine whether or not the opaque type itself should + // be considered local. However, this could make it a breaking change + // to switch the underlying ('defining') type from a local type + // to a remote type. This would violate the rule that opaque + // types should be completely opaque apart from the traits + // that they implement, so we don't use this behavior. + self.found_non_local_ty(ty) + } + }; + // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so + // the first type we visit is always the self type. + self.in_self_ty = false; + result + } - ty::Error(_) => true, - - // These variants should never appear during coherence checking because they - // cannot be named directly. - // - // They could be indirectly used through an opaque type. While using opaque types - // in impls causes an error, this path can still be hit afterwards. - // - // See `test/ui/coherence/coherence-with-closure.rs` for an example where this - // could happens. - ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { - tcx.sess.delay_span_bug( - DUMMY_SP, - format!("ty_is_local invoked on closure or generator: {:?}", ty), - ); - true - } + // FIXME: Constants should participate in orphan checking. + fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 3a152eff485..254bc4ab663 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -10,22 +10,153 @@ //! generic constants mentioned in the `caller_bounds` of the current environment. use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; -use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; -use rustc_middle::mir; -use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::thir; -use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, DelaySpanBugEmitted, EarlyBinder, TyCtxt, TypeVisitable}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::abstract_const::{ + walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable, +}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_session::lint; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; -use std::cmp; use std::iter; use std::ops::ControlFlow; +pub struct ConstUnifyCtxt<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> ConstUnifyCtxt<'tcx> { + // Substitutes generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. + // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] + #[inline] + #[instrument(skip(self), level = "debug")] + fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option<AbstractConst<'tcx>> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } + } + + Some(abstr_const) + } + + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { + return true; + }; + + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; + + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); + + if a_ct.ty() != b_ct.ty() { + return false; + } + + match (a_ct.kind(), b_ct.kind()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, + } + } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, + } + } +} + +#[instrument(skip(tcx), level = "debug")] +pub fn try_unify_abstract_consts<'tcx>( + tcx: TyCtxt<'tcx>, + (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, +) -> bool { + (|| { + if let Some(a) = AbstractConst::new(tcx, a)? { + if let Some(b) = AbstractConst::new(tcx, b)? { + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + return Ok(const_unify_ctxt.try_unify(a, b)); + } + } + + Ok(false) + })() + .unwrap_or_else(|_: ErrorGuaranteed| true) + // FIXME(generic_const_exprs): We should instead have this + // method return the resulting `ty::Const` and return `ConstKind::Error` + // on `ErrorGuaranteed`. +} + /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'cx, 'tcx>( @@ -41,48 +172,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( if satisfied_from_param_env(tcx, ct, param_env)? { return Ok(()); } - - // We were unable to unify the abstract constant with - // a constant found in the caller bounds, there are - // now three possible cases here. - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] - enum FailureKind { - /// The abstract const still references an inference - /// variable, in this case we return `TooGeneric`. - MentionsInfer, - /// The abstract const references a generic parameter, - /// this means that we emit an error here. - MentionsParam, - /// The substs are concrete enough that we can simply - /// try and evaluate the given constant. - Concrete, - } - let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) { - Node::Leaf(leaf) => { - if leaf.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if leaf.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Cast(_, _, ty) => { - if ty.has_infer_types_or_consts() { - failure_kind = FailureKind::MentionsInfer; - } else if ty.has_param_types_or_consts() { - failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); - } - - ControlFlow::CONTINUE - } - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }); - - match failure_kind { + match ct.unify_failure_kind(tcx) { FailureKind::MentionsInfer => { return Err(NotConstEvaluatable::MentionsInfer); } @@ -95,15 +185,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); match concrete { - Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() { - infcx - .tcx - .sess - .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv)); - NotConstEvaluatable::MentionsParam - } else { - NotConstEvaluatable::MentionsInfer - }), + Err(ErrorHandled::TooGeneric) => { + Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug( + span, + format!("Missing value for constant, but no error reported?"), + ))) + } Err(ErrorHandled::Linted) => { let reported = infcx .tcx @@ -150,8 +237,11 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() { NotConstEvaluatable::MentionsInfer - } else { + } else if uv.has_param_types_or_consts() { NotConstEvaluatable::MentionsParam + } else { + let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + NotConstEvaluatable::Error(guar) }), Err(ErrorHandled::Linted) => { let reported = @@ -216,593 +306,3 @@ fn satisfied_from_param_env<'tcx>( Ok(false) } - -/// A tree representing an anonymous constant. -/// -/// This is only able to represent a subset of `MIR`, -/// and should not leak any information about desugarings. -#[derive(Debug, Clone, Copy)] -pub struct AbstractConst<'tcx> { - // FIXME: Consider adding something like `IndexSlice` - // and use this here. - inner: &'tcx [Node<'tcx>], - substs: SubstsRef<'tcx>, -} - -impl<'tcx> AbstractConst<'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - uv: ty::Unevaluated<'tcx, ()>, - ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; - debug!("AbstractConst::new({:?}) = {:?}", uv, inner); - Ok(inner.map(|inner| AbstractConst { inner, substs: tcx.erase_regions(uv.substs) })) - } - - pub fn from_const( - tcx: TyCtxt<'tcx>, - ct: ty::Const<'tcx>, - ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { - match ct.kind() { - ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), - ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported), - _ => Ok(None), - } - } - - #[inline] - pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { - AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } - } - - #[inline] - pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { - let node = self.inner.last().copied().unwrap(); - match node { - Node::Leaf(leaf) => Node::Leaf(EarlyBinder(leaf).subst(tcx, self.substs)), - Node::Cast(kind, operand, ty) => { - Node::Cast(kind, operand, EarlyBinder(ty).subst(tcx, self.substs)) - } - // Don't perform substitution on the following as they can't directly contain generic params - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, - } - } -} - -struct AbstractConstBuilder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body_id: thir::ExprId, - body: &'a thir::Thir<'tcx>, - /// The current WIP node tree. - nodes: IndexVec<NodeId, Node<'tcx>>, -} - -impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { - fn root_span(&self) -> Span { - self.body.exprs[self.body_id].span - } - - fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { - let reported = self - .tcx - .sess - .struct_span_err(self.root_span(), "overly complex generic constant") - .span_label(span, msg) - .help("consider moving this anonymous constant into a `const` function") - .emit(); - - Err(reported) - } - fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { - let reported = self - .tcx - .sess - .struct_span_err(self.root_span(), "overly complex generic constant") - .span_label(span, msg) - .help("consider moving this anonymous constant into a `const` function") - .note("this operation may be supported in the future") - .emit(); - - Err(reported) - } - - #[instrument(skip(tcx, body, body_id), level = "debug")] - fn new( - tcx: TyCtxt<'tcx>, - (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), - ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { - let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; - - struct IsThirPolymorphic<'a, 'tcx> { - is_poly: bool, - thir: &'a thir::Thir<'tcx>, - } - - use crate::rustc_middle::thir::visit::Visitor; - use thir::visit; - - impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { - fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { - if expr.ty.has_param_types_or_consts() { - return true; - } - - match expr.kind { - thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), - thir::ExprKind::ConstParam { .. } => true, - thir::ExprKind::Repeat { value, count } => { - self.visit_expr(&self.thir()[value]); - count.has_param_types_or_consts() - } - _ => false, - } - } - - fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { - if pat.ty.has_param_types_or_consts() { - return true; - } - - match pat.kind.as_ref() { - thir::PatKind::Constant { value } => value.has_param_types_or_consts(), - thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { - lo.has_param_types_or_consts() || hi.has_param_types_or_consts() - } - _ => false, - } - } - } - - impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { - fn thir(&self) -> &'a thir::Thir<'tcx> { - &self.thir - } - - #[instrument(skip(self), level = "debug")] - fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= self.expr_is_poly(expr); - if !self.is_poly { - visit::walk_expr(self, expr) - } - } - - #[instrument(skip(self), level = "debug")] - fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { - self.is_poly |= self.pat_is_poly(pat); - if !self.is_poly { - visit::walk_pat(self, pat); - } - } - } - - let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; - visit::walk_expr(&mut is_poly_vis, &body[body_id]); - debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); - if !is_poly_vis.is_poly { - return Ok(None); - } - - Ok(Some(builder)) - } - - /// We do not allow all binary operations in abstract consts, so filter disallowed ones. - fn check_binop(op: mir::BinOp) -> bool { - use mir::BinOp::*; - match op { - Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le - | Ne | Ge | Gt => true, - Offset => false, - } - } - - /// While we currently allow all unary operations, we still want to explicitly guard against - /// future changes here. - fn check_unop(op: mir::UnOp) -> bool { - use mir::UnOp::*; - match op { - Not | Neg => true, - } - } - - /// Builds the abstract const by walking the thir and bailing out when - /// encountering an unsupported operation. - fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { - debug!("Abstractconstbuilder::build: body={:?}", &*self.body); - self.recurse_build(self.body_id)?; - - for n in self.nodes.iter() { - if let Node::Leaf(ct) = n { - if let ty::ConstKind::Unevaluated(ct) = ct.kind() { - // `AbstractConst`s should not contain any promoteds as they require references which - // are not allowed. - assert_eq!(ct.promoted, None); - assert_eq!(ct, self.tcx.erase_regions(ct)); - } - } - } - - Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) - } - - fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { - use thir::ExprKind; - let node = &self.body.exprs[node]; - Ok(match &node.kind { - // I dont know if handling of these 3 is correct - &ExprKind::Scope { value, .. } => self.recurse_build(value)?, - &ExprKind::PlaceTypeAscription { source, .. } - | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, - &ExprKind::Literal { lit, neg} => { - let sp = node.span; - let constant = - match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { - Ok(c) => c, - Err(LitToConstError::Reported) => { - self.tcx.const_error(node.ty) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - }; - - self.nodes.push(Node::Leaf(constant)) - } - &ExprKind::NonHirLiteral { lit , user_ty: _} => { - let val = ty::ValTree::from_scalar_int(lit); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::ZstLiteral { user_ty: _ } => { - let val = ty::ValTree::zst(); - self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) - } - &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { - let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); - - let constant = self.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Unevaluated(uneval), - ty: node.ty, - }); - - self.nodes.push(Node::Leaf(constant)) - } - - ExprKind::ConstParam {param, ..} => { - let const_param = self.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Param(*param), - ty: node.ty, - }); - self.nodes.push(Node::Leaf(const_param)) - } - - ExprKind::Call { fun, args, .. } => { - let fun = self.recurse_build(*fun)?; - - let mut new_args = Vec::<NodeId>::with_capacity(args.len()); - for &id in args.iter() { - new_args.push(self.recurse_build(id)?); - } - let new_args = self.tcx.arena.alloc_slice(&new_args); - self.nodes.push(Node::FunctionCall(fun, new_args)) - } - &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { - let lhs = self.recurse_build(lhs)?; - let rhs = self.recurse_build(rhs)?; - self.nodes.push(Node::Binop(op, lhs, rhs)) - } - &ExprKind::Unary { op, arg } if Self::check_unop(op) => { - let arg = self.recurse_build(arg)?; - self.nodes.push(Node::UnaryOp(op, arg)) - } - // This is necessary so that the following compiles: - // - // ``` - // fn foo<const N: usize>(a: [(); N + 1]) { - // bar::<{ N + 1 }>(); - // } - // ``` - ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => { - self.recurse_build(*e)? - } - // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a - // "coercion cast" i.e. using a coercion or is a no-op. - // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) - &ExprKind::Use { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(abstract_const::CastKind::Use, arg, node.ty)) - } - &ExprKind::Cast { source } => { - let arg = self.recurse_build(source)?; - self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty)) - } - ExprKind::Borrow{ arg, ..} => { - let arg_node = &self.body.exprs[*arg]; - - // Skip reborrows for now until we allow Deref/Borrow/AddressOf - // expressions. - // FIXME(generic_const_exprs): Verify/explain why this is sound - if let ExprKind::Deref {arg} = arg_node.kind { - self.recurse_build(arg)? - } else { - self.maybe_supported_error( - node.span, - "borrowing is not supported in generic constants", - )? - } - } - // FIXME(generic_const_exprs): We may want to support these. - ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error( - node.span, - "dereferencing or taking the address is not supported in generic constants", - )?, - ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( - node.span, - "array construction is not supported in generic constants", - )?, - ExprKind::Block { .. } => self.maybe_supported_error( - node.span, - "blocks are not supported in generic constant", - )?, - ExprKind::NeverToAny { .. } => self.maybe_supported_error( - node.span, - "converting nevers to any is not supported in generic constant", - )?, - ExprKind::Tuple { .. } => self.maybe_supported_error( - node.span, - "tuple construction is not supported in generic constants", - )?, - ExprKind::Index { .. } => self.maybe_supported_error( - node.span, - "indexing is not supported in generic constant", - )?, - ExprKind::Field { .. } => self.maybe_supported_error( - node.span, - "field access is not supported in generic constant", - )?, - ExprKind::ConstBlock { .. } => self.maybe_supported_error( - node.span, - "const blocks are not supported in generic constant", - )?, - ExprKind::Adt(_) => self.maybe_supported_error( - node.span, - "struct/enum construction is not supported in generic constants", - )?, - // dont know if this is correct - ExprKind::Pointer { .. } => - self.error(node.span, "pointer casts are not allowed in generic constants")?, - ExprKind::Yield { .. } => - self.error(node.span, "generator control flow is not allowed in generic constants")?, - ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self - .error( - node.span, - "loops and loop control flow are not supported in generic constants", - )?, - ExprKind::Box { .. } => - self.error(node.span, "allocations are not allowed in generic constants")?, - - ExprKind::Unary { .. } => unreachable!(), - // we handle valid unary/binary ops above - ExprKind::Binary { .. } => - self.error(node.span, "unsupported binary operation in generic constants")?, - ExprKind::LogicalOp { .. } => - self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?, - ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { - self.error(node.span, "assignment is not supported in generic constants")? - } - ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error( - node.span, - "closures and function keywords are not supported in generic constants", - )?, - // let expressions imply control flow - ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => - self.error(node.span, "control flow is not supported in generic constants")?, - ExprKind::InlineAsm { .. } => { - self.error(node.span, "assembly is not supported in generic constants")? - } - - // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen - ExprKind::VarRef { .. } - | ExprKind::UpvarRef { .. } - | ExprKind::StaticRef { .. } - | ExprKind::ThreadLocalRef(_) => { - self.error(node.span, "unsupported operation in generic constant")? - } - }) - } -} - -/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. -pub(super) fn thir_abstract_const<'tcx>( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam<LocalDefId>, -) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { - if tcx.features().generic_const_exprs { - match tcx.def_kind(def.did) { - // FIXME(generic_const_exprs): We currently only do this for anonymous constants, - // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether - // we want to look into them or treat them as opaque projections. - // - // Right now we do neither of that and simply always fail to unify them. - DefKind::AnonConst | DefKind::InlineConst => (), - _ => return Ok(None), - } - - let body = tcx.thir_body(def)?; - - AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? - .map(AbstractConstBuilder::build) - .transpose() - } else { - Ok(None) - } -} - -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify_abstract_consts<'tcx>( - tcx: TyCtxt<'tcx>, - (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), - param_env: ty::ParamEnv<'tcx>, -) -> bool { - (|| { - if let Some(a) = AbstractConst::new(tcx, a)? { - if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; - return Ok(const_unify_ctxt.try_unify(a, b)); - } - } - - Ok(false) - })() - .unwrap_or_else(|_: ErrorGuaranteed| true) - // FIXME(generic_const_exprs): We should instead have this - // method return the resulting `ty::Const` and return `ConstKind::Error` - // on `ErrorGuaranteed`. -} - -#[instrument(skip(tcx, f), level = "debug")] -pub fn walk_abstract_const<'tcx, R, F>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - mut f: F, -) -> ControlFlow<R> -where - F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, -{ - #[instrument(skip(tcx, f), level = "debug")] - fn recurse<'tcx, R>( - tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, - f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, - ) -> ControlFlow<R> { - f(ct)?; - let root = ct.root(tcx); - debug!(?root); - match root { - Node::Leaf(_) => ControlFlow::CONTINUE, - Node::Binop(_, l, r) => { - recurse(tcx, ct.subtree(l), f)?; - recurse(tcx, ct.subtree(r), f) - } - Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), - Node::FunctionCall(func, args) => { - recurse(tcx, ct.subtree(func), f)?; - args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) - } - Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), - } - } - - recurse(tcx, ct, &mut f) -} - -struct ConstUnifyCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> ConstUnifyCtxt<'tcx> { - // Substitutes generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - #[inline] - #[instrument(skip(self), level = "debug")] - fn try_replace_substs_in_root( - &self, - mut abstr_const: AbstractConst<'tcx>, - ) -> Option<AbstractConst<'tcx>> { - while let Node::Leaf(ct) = abstr_const.root(self.tcx) { - match AbstractConst::from_const(self.tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) - } - - /// Tries to unify two abstract constants using structural equality. - #[instrument(skip(self), level = "debug")] - fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { - let a = if let Some(a) = self.try_replace_substs_in_root(a) { - a - } else { - return true; - }; - - let b = if let Some(b) = self.try_replace_substs_in_root(b) { - b - } else { - return true; - }; - - let a_root = a.root(self.tcx); - let b_root = b.root(self.tcx); - debug!(?a_root, ?b_root); - - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(self.tcx, self.param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(self.tcx, self.param_env); - debug!("b_ct evaluated: {:?}", b_ct); - - if a_ct.ty() != b_ct.ty() { - return false; - } - - match (a_ct.kind(), b_ct.kind()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param - } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv - } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, - } - } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify(a.subtree(al), b.subtree(bl)) - && self.try_unify(a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify(a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - self.try_unify(a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, - } - } -} diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 4d477886979..6c177f63887 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -1,7 +1,17 @@ -use rustc_middle::ty::TyCtxt; +use std::cell::RefCell; use super::TraitEngine; use super::{ChalkFulfillmentContext, FulfillmentContext}; +use crate::infer::InferCtxtExt; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::traits::{ + FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, +}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; pub trait TraitEngineExt<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box<Self>; @@ -9,10 +19,94 @@ pub trait TraitEngineExt<'tcx> { impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box<Self> { - if tcx.sess.opts.debugging_opts.chalk { + if tcx.sess.opts.unstable_opts.chalk { Box::new(ChalkFulfillmentContext::new()) } else { Box::new(FulfillmentContext::new()) } } } + +/// Used if you want to have pleasant experience when dealing +/// with obligations outside of hir or mir typeck. +pub struct ObligationCtxt<'a, 'tcx> { + pub infcx: &'a InferCtxt<'a, 'tcx>, + engine: RefCell<Box<dyn TraitEngine<'tcx>>>, +} + +impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) } + } + + pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { + self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation); + } + + pub fn register_obligations( + &self, + obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, + ) { + // Can't use `register_predicate_obligations` because the iterator + // may also use this `ObligationCtxt`. + for obligation in obligations { + self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation) + } + } + + pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations); + value + } + + /// Requires that `ty` must implement the trait with `def_id` in + /// the given environment. This trait must not have any type + /// parameters (except for `Self`). + pub fn register_bound( + &self, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, + ) { + let tcx = self.infcx.tcx; + let trait_ref = ty::TraitRef { def_id, substs: tcx.mk_substs_trait(ty, &[]) }; + self.register_obligation(Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), + }); + } + + pub fn normalize<T: TypeFoldable<'tcx>>( + &self, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: T, + ) -> T { + let infer_ok = self.infcx.partially_normalize_associated_types_in(cause, param_env, value); + self.register_infer_ok_obligations(infer_ok) + } + + pub fn equate_types( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ) -> Result<(), TypeError<'tcx>> { + match self.infcx.at(cause, param_env).eq(expected, actual) { + Ok(InferOk { obligations, value: () }) => { + self.register_obligations(obligations); + Ok(()) + } + Err(e) => Err(e), + } + } + + pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> { + self.engine.borrow_mut().select_all_or_error(self.infcx) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 8d7c6b26ba1..25ba520ace2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,10 +22,9 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; -use rustc_infer::infer::error_reporting::same_type_modulo_infer; -use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{ @@ -302,13 +301,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span = obligation.cause.span; } } - if let ObligationCauseCode::CompareImplMethodObligation { - impl_item_def_id, - trait_item_def_id, - } - | ObligationCauseCode::CompareImplTypeObligation { + if let ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, + kind: _, } = *obligation.cause.code() { self.report_extra_impl_obligation( @@ -474,7 +470,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(ref s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s.as_str()); + err.span_label(span, s); if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point @@ -531,7 +527,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let enclosing_scope_span = tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body)); - err.span_label(enclosing_scope_span, s.as_str()); + err.span_label(enclosing_scope_span, s); } self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); @@ -635,12 +631,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &format!( "expected a closure taking {} argument{}, but one taking {} argument{} was given", given.len(), - if given.len() == 1 { "" } else { "s" }, + pluralize!(given.len()), expected.len(), - if expected.len() == 1 { "" } else { "s" }, + pluralize!(expected.len()), ) ); - } else if !same_type_modulo_infer(given_ty, expected_ty) { + } else if !self.same_type_modulo_infer(given_ty, expected_ty) { // Print type mismatch let (expected_args, given_args) = self.cmp(given_ty, expected_ty); @@ -665,11 +661,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.suggest_restricting_param_bound( &mut err, trait_predicate, + None, obligation.cause.body_id, ); } else if !suggested { // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(trait_ref); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( impl_candidates, trait_ref, @@ -704,7 +701,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { let trait_ref = trait_pred.to_poly_trait_ref(); let impl_candidates = - self.find_similar_impl_candidates(trait_ref); + self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( impl_candidates, trait_ref, @@ -788,24 +785,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } - ty::PredicateKind::RegionOutlives(predicate) => { - let predicate = bound_predicate.rebind(predicate); - let predicate = self.resolve_vars_if_possible(predicate); - let err = self - .region_outlives_predicate(&obligation.cause, predicate) - .err() - .unwrap(); - struct_span_err!( - self.tcx.sess, - span, - E0279, - "the requirement `{}` is not satisfied (`{}`)", - predicate, - err, - ) - } - - ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => { + ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::TypeOutlives(..) => { let predicate = self.resolve_vars_if_possible(obligation.predicate); struct_span_err!( self.tcx.sess, @@ -823,10 +805,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { let found_kind = self.closure_kind(closure_substs).unwrap(); - let closure_span = - self.tcx.sess.source_map().guess_head_span( - self.tcx.hir().span_if_local(closure_def_id).unwrap(), - ); + let closure_span = self.tcx.def_span(closure_def_id); let mut err = struct_span_err!( self.tcx.sess, closure_span, @@ -884,7 +863,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } ty::PredicateKind::WellFormed(ty) => { - if !self.tcx.sess.opts.debugging_opts.chalk { + if !self.tcx.sess.opts.unstable_opts.chalk { // WF predicates cannot themselves make // errors. They can only block due to // ambiguity; otherwise, they always @@ -951,9 +930,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => None, }; - let found_span = found_did - .and_then(|did| self.tcx.hir().span_if_local(did)) - .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def + let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { // We check closures twice, with obligations flowing in different directions, @@ -1086,10 +1063,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let hir = self.tcx.hir(); Some(match node { Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure { body, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), .. }) => ( - sm.guess_head_span(fn_decl_span), + fn_decl_span, hir.body(body) .params .iter() @@ -1348,7 +1325,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn find_similar_impl_candidates( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>>; fn report_similar_impl_candidates( @@ -1407,7 +1384,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[AmbiguousSelection], + impls: &[DefId], predicate: ty::Predicate<'tcx>, ); @@ -1717,18 +1694,22 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn find_similar_impl_candidates( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>> { self.tcx - .all_impls(trait_ref.def_id()) + .all_impls(trait_pred.def_id()) .filter_map(|def_id| { - if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative { + if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative + || !trait_pred + .skip_binder() + .is_constness_satisfied_by(self.tcx.constness(def_id)) + { return None; } let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - self.fuzzy_match_tys(trait_ref.skip_binder().self_ty(), imp.self_ty(), false) + self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false) .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) }) .collect() @@ -2040,14 +2021,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); match selcx.select_from_obligation(&obligation) { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { - if self.is_tainted_by_errors() && subst.is_none() { - // If `subst.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return; - } self.annotate_source_of_ambiguity(&mut err, &impls, predicate); } _ => { @@ -2116,6 +2089,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); } } + + if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) = + (body_id, subst.map(|subst| subst.unpack())) + { + struct FindExprBySpan<'hir> { + span: Span, + result: Option<&'hir hir::Expr<'hir>>, + } + + impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.span == ex.span { + self.result = Some(ex); + } else { + hir::intravisit::walk_expr(self, ex); + } + } + } + + let mut expr_finder = FindExprBySpan { span, result: None }; + + expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); + + if let Some(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. } + ) = expr_finder.result + && let [ + .., + trait_path_segment @ hir::PathSegment { + res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)), + .. + }, + hir::PathSegment { + ident: assoc_item_name, + res: Some(rustc_hir::def::Res::Def(_, item_id)), + .. + } + ] = path.segments + && data.trait_ref.def_id == *trait_id + && self.tcx.trait_of_item(item_id) == Some(*trait_id) + && !self.is_tainted_by_errors() + { + let (verb, noun) = match self.tcx.associated_item(item_id).kind { + ty::AssocKind::Const => ("refer to the", "constant"), + ty::AssocKind::Fn => ("call", "function"), + ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here + }; + + // Replace the more general E0283 with a more specific error + err.cancel(); + err = self.tcx.sess.struct_span_err_with_code( + span, + &format!( + "cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type", + ), + rustc_errors::error_code!(E0790), + ); + + if let Some(local_def_id) = data.trait_ref.def_id.as_local() + && let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id) + && let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) { + err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name)); + } + + err.span_label(span, format!("cannot {verb} associated {noun} of trait")); + + let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); + + if trait_impls.blanket_impls().is_empty() + && let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next() + && let Some(impl_def_id) = impl_ty.def() { + let message = if trait_impls.non_blanket_impls().len() == 1 { + "use the fully-qualified path to the only available implementation".to_string() + } else { + format!( + "use a fully-qualified path to a specific available implementation ({} found)", + trait_impls.non_blanket_impls().len() + ) + }; + + err.multipart_suggestion( + message, + vec![ + (trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())), + (trait_path_segment.ident.span.shrink_to_hi(), format!(">")) + ], + Applicability::MaybeIncorrect + ); + } + } + }; + err } @@ -2179,6 +2244,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } + ty::PredicateKind::ConstEvaluatable(data) => { + if predicate.references_error() || self.is_tainted_by_errors() { + return; + } + let subst = data.substs.iter().find(|g| g.has_infer_types_or_consts()); + if let Some(subst) = subst { + let err = self.emit_inference_failure_err( + body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, &format!("cannot satisfy `{}`", predicate)); + err + } + } _ => { if self.tcx.sess.has_errors().is_some() || self.is_tainted_by_errors() { return; @@ -2201,35 +2293,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[AmbiguousSelection], + impls: &[DefId], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - let mut or_where_clause = false; - for ambig in impls { - match ambig { - AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); - } + for def_id in impls { + match self.tcx.span_of_impl(*def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { + post.push(header); } - }, - AmbiguousSelection::ParamEnv(span) => { - or_where_clause = true; - spans.push(*span); } } } - let msg = format!( - "multiple `impl`s{} satisfying `{}` found", - if or_where_clause { " or `where` clauses" } else { "" }, - predicate - ); + let msg = format!("multiple `impl`s satisfying `{}` found", predicate); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); crate_names.sort(); crate_names.dedup(); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 7c9ee64a0c2..7e8872d9018 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) }), hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { body, movability, .. }, + kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }), .. }) => self.describe_generator(*body).or_else(|| { Some(if movability.is_some() { "an async closure" } else { "a closure" }) 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 33585de6001..6c8faed0df4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -24,7 +24,8 @@ use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable, - ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_session::Limit; @@ -172,6 +173,7 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, + proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, body_id: hir::HirId, ); @@ -182,7 +184,7 @@ pub trait InferCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>; + fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>; fn suggest_fn_call( &self, @@ -376,7 +378,7 @@ fn suggest_restriction<'tcx>( replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name)) .to_ty(tcx), }); - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return; } // We know we have an `impl Trait` that doesn't satisfy a required projection. @@ -415,7 +417,7 @@ fn suggest_restriction<'tcx>( Applicability::MaybeIncorrect, ); } else { - if !trait_pred.is_suggestable(tcx) { + if !trait_pred.is_suggestable(tcx, false) { return; } // Trivial case: `T` needs an extra bound: `T: Bound`. @@ -457,6 +459,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, mut err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, + proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, body_id: hir::HirId, ) { let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); @@ -583,15 +586,34 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // else in the predicate. if !trait_pred.skip_binder().trait_ref.substs[1..] .iter() - .all(|g| g.is_suggestable(self.tcx)) + .all(|g| g.is_suggestable(self.tcx, false)) { return; } // Missing generic type parameter bound. let param_name = self_ty.to_string(); - let constraint = with_no_trimmed_paths!( + let mut constraint = with_no_trimmed_paths!( trait_pred.print_modifiers_and_trait_path().to_string() ); + + if let Some(proj_pred) = proj_pred { + let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder(); + let item = self.tcx.associated_item(projection_ty.item_def_id); + + // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. + // That should be extracted into a helper function. + if constraint.ends_with('>') { + constraint = format!( + "{}, {}={}>", + &constraint[..constraint.len() - 1], + item.name, + term + ); + } else { + constraint.push_str(&format!("<{}={}>", item.name, term)); + } + } + if suggest_constraining_type_param( self.tcx, generics, @@ -715,13 +737,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// Given a closure's `DefId`, return the given name of the closure. /// /// This doesn't account for reassignments, but it's only used for suggestions. - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String> { - let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<String> { + fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> { + let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { - Some(format!("{}", name)) + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) => { + Some(ident.name) } _ => { err.note(msg); @@ -786,7 +808,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Get the name of the callable and the arguments to be used in the suggestion. let (snippet, sugg) = match hir.get_if_local(def_id) { Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl, fn_decl_span, .. }, + kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }), .. })) => { err.span_label(*fn_decl_span, "consider calling this closure"); @@ -1311,7 +1333,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { visitor.visit_body(&body); let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); - let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; }; + let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor .returns @@ -1543,7 +1565,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Generator(..) => "generator", _ => "function", }; - let span = self.tcx.sess.source_map().guess_head_span(span); let mut err = struct_span_err!( self.tcx.sess, span, @@ -2661,11 +2682,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) }); } - ObligationCauseCode::CompareImplMethodObligation { trait_item_def_id, .. } => { + ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => { let item_name = self.tcx.item_name(trait_item_def_id); let msg = format!( - "the requirement `{}` appears on the impl method `{}` but not on the \ - corresponding trait method", + "the requirement `{}` appears on the `impl`'s {kind} `{}` but not on the \ + corresponding trait's {kind}", predicate, item_name, ); let sp = self @@ -2676,32 +2697,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut assoc_span: MultiSpan = sp.into(); assoc_span.push_span_label( sp, - format!("this trait method doesn't have the requirement `{}`", predicate), - ); - if let Some(ident) = self - .tcx - .opt_associated_item(trait_item_def_id) - .and_then(|i| self.tcx.opt_item_ident(i.container.id())) - { - assoc_span.push_span_label(ident.span, "in this trait"); - } - err.span_note(assoc_span, &msg); - } - ObligationCauseCode::CompareImplTypeObligation { trait_item_def_id, .. } => { - let item_name = self.tcx.item_name(trait_item_def_id); - let msg = format!( - "the requirement `{}` appears on the associated impl type `{}` but not on the \ - corresponding associated trait type", - predicate, item_name, - ); - let sp = self.tcx.def_span(trait_item_def_id); - let mut assoc_span: MultiSpan = sp.into(); - assoc_span.push_span_label( - sp, - format!( - "this trait associated type doesn't have the requirement `{}`", - predicate, - ), + format!("this trait's {kind} doesn't have the requirement `{}`", predicate), ); if let Some(ident) = self .tcx @@ -2712,13 +2708,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } err.span_note(assoc_span, &msg); } - ObligationCauseCode::CompareImplConstObligation => { - err.note(&format!( - "the requirement `{}` appears on the associated impl constant \ - but not on the corresponding associated trait constant", - predicate - )); - } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); if tcx.sess.opts.unstable_features.is_nightly_build() { @@ -2826,7 +2815,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { - ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span, + ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span, _ => return, }; match ( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 78600652254..556ef466cd1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -6,7 +6,7 @@ use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProce use rustc_infer::traits::ProjectionCacheKey; use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation}; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::ToPredicate; @@ -58,19 +58,6 @@ pub struct FulfillmentContext<'tcx> { relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>, - // Should this fulfillment context register type-lives-for-region - // obligations on its parent infcx? In some cases, region - // obligations are either already known to hold (normalization) or - // hopefully verified elsewhere (type-impls-bound), and therefore - // should not be checked. - // - // Note that if we are normalizing a type that we already - // know is well-formed, there should be no harm setting this - // to true - all the region variables should be determinable - // using the RFC 447 rules, which don't depend on - // type-lives-for-region constraints, and because the type - // is well-formed, the constraints should hold. - register_region_obligations: bool, // Is it OK to register obligations into this infcx inside // an infcx snapshot? // @@ -103,7 +90,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), - register_region_obligations: true, usable_in_snapshot: false, } } @@ -112,30 +98,18 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), - register_region_obligations: true, usable_in_snapshot: true, } } - pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> { - FulfillmentContext { - predicates: ObligationForest::new(), - relationships: FxHashMap::default(), - register_region_obligations: false, - usable_in_snapshot: false, - } - } - /// Attempts to select obligations using `selcx`. fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); // Process pending obligations. - let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut FulfillProcessor { - selcx, - register_region_obligations: self.register_region_obligations, - }); + let outcome: Outcome<_, _> = + self.predicates.process_obligations(&mut FulfillProcessor { selcx }); // FIXME: if we kept the original cache key, we could mark projection // obligations as complete for the projection cache here. @@ -239,7 +213,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { struct FulfillProcessor<'a, 'b, 'tcx> { selcx: &'a mut SelectionContext<'b, 'tcx>, - register_region_obligations: bool, } fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> { @@ -295,6 +268,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { /// This is called much less often than `needs_process_obligation`, so we /// never inline it. #[inline(never)] + #[instrument(level = "debug", skip(self, pending_obligation))] fn process_obligation( &mut self, pending_obligation: &mut PendingPredicateObligation<'tcx>, @@ -303,7 +277,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { let obligation = &mut pending_obligation.obligation; - debug!(?obligation, "process_obligation pre-resolve"); + debug!(?obligation, "pre-resolve"); if obligation.predicate.has_infer_types_or_consts() { obligation.predicate = @@ -312,8 +286,6 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { let obligation = &pending_obligation.obligation; - debug!(?obligation, ?obligation.cause, "process_obligation"); - let infcx = self.selcx.infcx(); if obligation.predicate.has_projections() { @@ -386,19 +358,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::RegionOutlives(data) => { - match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) { - Ok(()) => ProcessResult::Changed(vec![]), - Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + if infcx.considering_regions || data.has_placeholders() { + infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } + + ProcessResult::Changed(vec![]) } ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { - if self.register_region_obligations { - self.selcx.infcx().register_region_obligation_with_cause( - t_a, - r_b, - &obligation.cause, - ); + if infcx.considering_regions { + infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); } ProcessResult::Changed(vec![]) } @@ -603,7 +572,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ), (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { span_bug!( - obligation.cause.span(self.selcx.tcx()), + obligation.cause.span(), "ConstEquate: const_eval_resolve returned an unexpected error" ) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 74d2eb17b6b..d0a17f712d3 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -47,7 +47,7 @@ pub use self::SelectionError::*; pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; -pub use self::engine::TraitEngineExt; +pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; @@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind}; +pub use self::structural_match::{ + search_for_adt_const_param_violation, search_for_structural_match_violation, +}; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, @@ -163,7 +164,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( // The handling of regions in this area of the code is terrible, // see issue #29149. We should be able to improve on this with // NLL. - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let mut fulfill_cx = FulfillmentContext::new(); // We can use a dummy node-id here because we won't pay any mind // to region obligations that arise (there shouldn't really be any @@ -207,21 +208,21 @@ fn do_normalize_predicates<'tcx>( predicates: Vec<ty::Predicate<'tcx>>, ) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> { let span = cause.span; - tcx.infer_ctxt().enter(|infcx| { - // FIXME. We should really... do something with these region - // obligations. But this call just continues the older - // behavior (i.e., doesn't cause any new bugs), and it would - // take some further refactoring to actually solve them. In - // particular, we would have to handle implied bounds - // properly, and that code is currently largely confined to - // regionck (though I made some efforts to extract it - // out). -nmatsakis - // - // @arielby: In any case, these obligations are checked - // by wfcheck anyway, so I'm not sure we have to check - // them here too, and we will remove this function when - // we move over to lazy normalization *anyway*. - let fulfill_cx = FulfillmentContext::new_ignoring_regions(); + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + tcx.infer_ctxt().ignoring_regions().enter(|infcx| { + let fulfill_cx = FulfillmentContext::new(); let predicates = match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, predicates) { Ok(predicates) => predicates, @@ -237,29 +238,37 @@ fn do_normalize_predicates<'tcx>( // cares about declarations like `'a: 'b`. let outlives_env = OutlivesEnvironment::new(elaborated_env); - infcx.resolve_regions_and_report_errors(&outlives_env); + // FIXME: It's very weird that we ignore region obligations but apparently + // still need to use `resolve_regions` as we need the resolved regions in + // the normalized predicates. + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + tcx.sess.delay_span_bug( + span, + format!( + "failed region resolution while normalizing {elaborated_env:?}: {errors:?}" + ), + ); + } - let predicates = match infcx.fully_resolve(predicates) { - Ok(predicates) => predicates, + match infcx.fully_resolve(predicates) { + Ok(predicates) => Ok(predicates), Err(fixup_err) => { // If we encounter a fixup error, it means that some type // variable wound up unconstrained. I actually don't know // if this can happen, and I certainly don't expect it to // happen often, but if it did happen it probably // represents a legitimate failure due to some kind of - // unconstrained variable, and it seems better not to ICE, - // all things considered. - let reported = tcx.sess.span_err(span, &fixup_err.to_string()); - return Err(reported); + // unconstrained variable. + // + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + span_bug!( + span, + "inference variables in normalized parameter environment: {}", + fixup_err + ); } - }; - if predicates.needs_infer() { - let reported = tcx - .sess - .delay_span_bug(span, "encountered inference variables after `fully_resolve`"); - Err(reported) - } else { - Ok(predicates) } }) } @@ -845,20 +854,6 @@ pub fn provide(providers: &mut ty::query::Providers) { vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, - thir_abstract_const: |tcx, def_id| { - let def_id = def_id.expect_local(); - if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { - tcx.thir_abstract_const_of_const_arg(def) - } else { - const_evaluatable::thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) - } - }, - thir_abstract_const_of_const_arg: |tcx, (did, param_did)| { - const_evaluatable::thir_abstract_const( - tcx, - ty::WithOptConstParam { did, const_param_did: Some(param_did) }, - ) - }, try_unify_abstract_consts: |tcx, param_env_and| { let (param_env, (a, b)) = param_env_and.into_parts(); const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index ac1811244ca..2921ce0ffef 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -11,12 +11,12 @@ use super::elaborate_predicates; use crate::infer::TyCtxtInferExt; -use crate::traits::const_evaluatable::{self, AbstractConst}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; use rustc_errors::{FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, @@ -841,15 +841,13 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( // // This shouldn't really matter though as we can't really use any // constants which are not considered const evaluatable. - use rustc_middle::thir::abstract_const::Node; + use rustc_middle::ty::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| { - match node.root(self.tcx) { - Node::Leaf(leaf) => self.visit_const(leaf), - Node::Cast(_, _, ty) => self.visit_ty(ty), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } + walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { + Node::Leaf(leaf) => self.visit_const(leaf), + Node::Cast(_, _, ty) => self.visit_ty(ty), + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE } }) } else { diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index ed7d16f7a54..4d3b0b4cf07 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString { } } // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => { + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { let reported = struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index b3e7fbb3578..9de4d3a646c 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -753,10 +753,13 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { .tcx .mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(p), ty: ct.ty() }) } - _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self), - _ => ct, + _ => ct.super_fold_with(self), } } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } } // The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came. diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index db45ee3fed7..32669e23db9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -84,7 +84,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { // Run canonical query. If overflow occurs, rerun from scratch but this time // in standard trait query mode so that overflow is handled appropriately // within `SelectionContext`. - self.tcx.at(obligation.cause.span(self.tcx)).evaluate_obligation(c_pred) + self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) } // Helper function that canonicalizes and runs the query. If an diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index eccfb3477b9..449d7a7b47b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -254,7 +254,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - return Err(NoSolution); + bug!("unexpected ambiguity: {:?} {:?}", c_data, result); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -293,7 +293,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - return Err(NoSolution); + bug!("unexpected ambiguity: {:?} {:?}", c_data, result); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index c9d46b2810d..c99564936aa 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -9,7 +9,6 @@ use rustc_infer::traits::TraitEngineExt as _; use rustc_span::source_map::DUMMY_SP; use std::fmt; -use std::rc::Rc; pub struct CustomTypeOp<F, G> { closure: F, @@ -109,7 +108,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( Ok(( TypeOpOutput { output: value, - constraints: Some(Rc::new(region_constraints)), + constraints: Some(infcx.tcx.arena.alloc(region_constraints)), error_info: None, }, region_constraint_data, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 1e72dd69339..578e1d00cf9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -10,7 +10,6 @@ use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use std::fmt; -use std::rc::Rc; pub mod ascribe_user_type; pub mod custom; @@ -41,7 +40,7 @@ pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> { /// The output from the type op. pub output: Op::Output, /// Any region constraints from performing the type op. - pub constraints: Option<Rc<QueryRegionConstraints<'tcx>>>, + pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>, /// Used for error reporting to be able to rerun the query pub error_info: Option<Op::ErrorInfo>, } @@ -156,11 +155,14 @@ where } } - // Promote the final query-region-constraints into a - // (optional) ref-counted vector: - let region_constraints = - if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) }; - - Ok(TypeOpOutput { output, constraints: region_constraints, error_info }) + Ok(TypeOpOutput { + output, + constraints: if region_constraints.is_empty() { + None + } else { + Some(infcx.tcx.arena.alloc(region_constraints)) + }, + error_info, + }) } } diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 56bdeafeeca..8148e2b7871 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -31,14 +31,14 @@ pub(crate) fn update<'tcx, T>( obligation .predicate .kind() - .map_bound(|_| { + .rebind( // (*) binder moved here ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: tpred.constness, polarity: tpred.polarity, }) - }) + ) .to_predicate(infcx.tcx), ); // Don't report overflow errors. Otherwise equivalent to may_hold. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 96d83deeeb7..6e8581128dd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,11 +6,9 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; -use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::traits::util::elaborate_predicates_with_span; -use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; +use rustc_infer::traits::TraitEngine; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); - - // Collect a list of (probable) spans that point to a param-env candidate - let tcx = self.infcx.tcx; - let owner = stack.obligation.cause.body_id.owner.to_def_id(); - let predicates = tcx.predicates_of(owner).instantiate_identity(tcx); - let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span( - tcx, - std::iter::zip(predicates.predicates, predicates.spans), - ) - .filter_map(|obligation| { - let kind = obligation.predicate.kind(); - if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() { - if trait_pred.trait_ref - == ty::TraitRef::identity(tcx, trait_pred.def_id()) - .skip_binder() - { - // HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span - Some(( - kind.rebind(trait_pred), - tcx.def_span(trait_pred.def_id()), - )) - } else { - Some((kind.rebind(trait_pred), obligation.cause.span)) - } - } else { - None - } - }) - .collect(); - return Err(Ambiguous( candidates .into_iter() .filter_map(|c| match c.candidate { - SelectionCandidate::ImplCandidate(def_id) => { - Some(AmbiguousSelection::Impl(def_id)) - } - SelectionCandidate::ParamCandidate(predicate) => { - Some(AmbiguousSelection::ParamEnv( - *param_env_spans.get(&predicate)?, - )) - } + SelectionCandidate::ImplCandidate(def_id) => Some(def_id), _ => None, }) .collect(), diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e1131140c39..d4c9fd1c5f9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -12,7 +12,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_middle::ty::{ToPolyTraitRef, ToPredicate}; use rustc_span::def_id::DefId; @@ -42,115 +42,96 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>, ) -> Result<Selection<'tcx>, SelectionError<'tcx>> { - let mut obligation = obligation; - let new_obligation; - - // HACK(const_trait_impl): the surrounding environment is remapped to a non-const context - // because nested obligations might be actually `~const` then (incorrectly) requiring - // const impls. for example: - // ``` - // pub trait Super {} - // pub trait Sub: Super {} - // - // impl<A> const Super for &A where A: ~const Super {} - // impl<A> const Sub for &A where A: ~const Sub {} - // ``` - // - // The procedure to check the code above without the remapping code is as follows: - // ``` - // CheckWf(impl const Sub for &A where A: ~const Sub) // <- const env - // CheckPredicate(&A: Super) - // CheckPredicate(A: ~const Super) // <- still const env, failure - // ``` - if obligation.param_env.is_const() && !obligation.predicate.is_const_if_const() { - new_obligation = TraitObligation { - cause: obligation.cause.clone(), - param_env: obligation.param_env.without_const(), - ..*obligation - }; - obligation = &new_obligation; - } - - match candidate { + let mut impl_src = match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); - Ok(ImplSource::Builtin(data)) + ImplSource::Builtin(data) } ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - Ok(ImplSource::Param(obligations, param.skip_binder().constness)) + ImplSource::Param(obligations, param.skip_binder().constness) } ImplCandidate(impl_def_id) => { - Ok(ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id))) + ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)) } AutoImplCandidate(trait_def_id) => { let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); - Ok(ImplSource::AutoImpl(data)) + ImplSource::AutoImpl(data) } ProjectionCandidate(idx) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; // FIXME(jschievink): constness - Ok(ImplSource::Param(obligations, ty::BoundConstness::NotConst)) + ImplSource::Param(obligations, ty::BoundConstness::NotConst) } ObjectCandidate(idx) => { let data = self.confirm_object_candidate(obligation, idx)?; - Ok(ImplSource::Object(data)) + ImplSource::Object(data) } ClosureCandidate => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - Ok(ImplSource::Closure(vtable_closure)) + ImplSource::Closure(vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - Ok(ImplSource::Generator(vtable_generator)) + ImplSource::Generator(vtable_generator) } FnPointerCandidate { .. } => { let data = self.confirm_fn_pointer_candidate(obligation)?; - Ok(ImplSource::FnPointer(data)) + ImplSource::FnPointer(data) } DiscriminantKindCandidate => { - Ok(ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)) + ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) } - PointeeCandidate => Ok(ImplSource::Pointee(ImplSourcePointeeData)), + PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData), TraitAliasCandidate(alias_def_id) => { let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); - Ok(ImplSource::TraitAlias(data)) + ImplSource::TraitAlias(data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - Ok(ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst)) + ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst) } BuiltinUnsizeCandidate => { let data = self.confirm_builtin_unsize_candidate(obligation)?; - Ok(ImplSource::Builtin(data)) + ImplSource::Builtin(data) } TraitUpcastingUnsizeCandidate(idx) => { let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; - Ok(ImplSource::TraitUpcasting(data)) + ImplSource::TraitUpcasting(data) } ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - Ok(ImplSource::ConstDestruct(data)) + ImplSource::ConstDestruct(data) } + }; + + if !obligation.predicate.is_const_if_const() { + // normalize nested predicates according to parent predicate's constness. + impl_src = impl_src.map(|mut o| { + o.predicate = o.predicate.without_const(self.tcx()); + o + }); } + + Ok(impl_src) } fn confirm_projection_candidate( @@ -555,7 +536,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter()); let bound = - EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs); + bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs); tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars)) }; let normalized_bound = normalize_with_depth_to( @@ -683,7 +664,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME: Chalk - if !self.tcx().sess.opts.debugging_opts.chalk { + if !self.tcx().sess.opts.unstable_opts.chalk { nested.push(Obligation::new( obligation.cause.clone(), obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2bb53a466ca..17f34012d1d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,7 +32,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -742,7 +742,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | (_, Err(ErrorHandled::Reported(_))) => Ok(EvaluatedToErr), (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { span_bug!( - obligation.cause.span(self.tcx()), + obligation.cause.span(), "ConstEquate: const_eval_resolve returned an unexpected error" ) } @@ -789,7 +789,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut param_env = obligation.param_env; fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| { - pred.remap_constness(self.tcx(), &mut param_env); + pred.remap_constness(&mut param_env); pred }); @@ -1321,7 +1321,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let tcx = self.tcx(); let mut pred = cache_fresh_trait_pred.skip_binder(); - pred.remap_constness(tcx, &mut param_env); + pred.remap_constness(&mut param_env); if self.can_use_global_caches(param_env) { if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) { @@ -1375,7 +1375,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let mut pred = cache_fresh_trait_pred.skip_binder(); - pred.remap_constness(tcx, &mut param_env); + pred.remap_constness(&mut param_env); if !self.can_cache_candidate(&candidate) { debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable"); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 2c4a453aefc..5f77aae6f22 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -207,18 +207,7 @@ fn fulfill_implication<'a, 'tcx>( // (which are packed up in penv) infcx.save_and_restore_in_snapshot_flag(|infcx| { - // If we came from `translate_substs`, we already know that the - // predicates for our impl hold (after all, we know that a more - // specialized impl holds, so our impl must hold too), and - // we only want to process the projections to determine the - // the types in our substs using RFC 447, so we can safely - // ignore region obligations, which allows us to avoid threading - // a node-id to assign them with. - // - // If we came from specialization graph construction, then - // we already make a mockery out of the region system, so - // why not ignore them a bit earlier? - let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); + let mut fulfill_cx = FulfillmentContext::new(); for oblig in obligations.chain(more_obligations) { fulfill_cx.register_predicate_obligation(&infcx, oblig); } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 6c0b83fbd03..c278752e3d9 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; -#[derive(Debug)] -pub struct NonStructuralMatchTy<'tcx> { - pub ty: Ty<'tcx>, - pub kind: NonStructuralMatchTyKind<'tcx>, -} - -#[derive(Debug)] -pub enum NonStructuralMatchTyKind<'tcx> { - Adt(AdtDef<'tcx>), - Param, - Dynamic, - Foreign, - Opaque, - Closure, - Generator, - Projection, - Float, -} - /// This method traverses the structure of `ty`, trying to find an /// instance of an ADT (i.e. struct or enum) that doesn't implement /// the structural-match traits, or a generic type parameter @@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> { /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -/// -/// The floats_allowed flag is used to deny constants in floating point pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - floats_allowed: bool, -) -> Option<NonStructuralMatchTy<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed }) +) -> Option<Ty<'tcx>> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) + .break_value() +} + +/// This method traverses the structure of `ty`, trying to find any +/// types that are not allowed to be used in a const generic. +/// +/// This is either because the type does not implement `StructuralEq` +/// and `StructuralPartialEq`, or because the type is intentionally +/// not supported in const generics (such as floats and raw pointers, +/// which are allowed in match blocks). +pub fn search_for_adt_const_param_violation<'tcx>( + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<Ty<'tcx>> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) .break_value() } @@ -125,7 +119,10 @@ struct Search<'tcx> { /// we will not recur on them again. seen: FxHashSet<hir::def_id::DefId>, - floats_allowed: bool, + // Additionally deny things that have been allowed in patterns, + // but are not allowed in adt const params, such as floats and + // fn ptrs. + adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -135,7 +132,7 @@ impl<'tcx> Search<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { - type BreakTy = NonStructuralMatchTy<'tcx>; + type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { debug!("Search visiting ty: {:?}", ty); @@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { - let kind = NonStructuralMatchTyKind::Param; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Dynamic(..) => { - let kind = NonStructuralMatchTyKind::Dynamic; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Foreign(_) => { - let kind = NonStructuralMatchTyKind::Foreign; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Opaque(..) => { - let kind = NonStructuralMatchTyKind::Opaque; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Projection(..) => { - let kind = NonStructuralMatchTyKind::Projection; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Closure(..) => { - let kind = NonStructuralMatchTyKind::Closure; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Generator(..) | ty::GeneratorWitness(..) => { - let kind = NonStructuralMatchTyKind::Generator; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); - } - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::CONTINUE; + return ControlFlow::Break(ty); } - ty::FnDef(..) | ty::FnPtr(..) => { + ty::FnDef(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` return ControlFlow::CONTINUE; @@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { return ControlFlow::CONTINUE; } + ty::FnPtr(..) => { + if !self.adt_const_param { + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + + ty::RawPtr(..) => { + if !self.adt_const_param { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + ty::Float(_) => { - if self.floats_allowed { + if !self.adt_const_param { return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::Float, - }); + return ControlFlow::Break(ty); } } @@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.type_marked_structural(ty) { debug!("Search found ty: {:?}", ty); - let kind = NonStructuralMatchTyKind::Adt(adt_def); - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } // structural-match does not care about the diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 7b5e1498f26..414857f0acc 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -60,12 +60,19 @@ pub fn obligations<'a, 'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = - WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; + let mut wf = WfPredicates { + tcx: infcx.tcx, + param_env, + body_id, + span, + out: vec![], + recursion_depth, + item: None, + }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); - let result = wf.normalize(); + let result = wf.normalize(infcx); debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); Some(result) } @@ -78,12 +85,12 @@ pub fn trait_obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, - trait_ref: &ty::TraitRef<'tcx>, + trait_pred: &ty::TraitPredicate<'tcx>, span: Span, item: &'tcx hir::Item<'tcx>, ) -> Vec<traits::PredicateObligation<'tcx>> { let mut wf = WfPredicates { - infcx, + tcx: infcx.tcx, param_env, body_id, span, @@ -91,9 +98,9 @@ pub fn trait_obligations<'a, 'tcx>( recursion_depth: 0, item: Some(item), }; - wf.compute_trait_ref(trait_ref, Elaborate::All); + wf.compute_trait_pred(trait_pred, Elaborate::All); debug!(obligations = ?wf.out); - wf.normalize() + wf.normalize(infcx) } pub fn predicate_obligations<'a, 'tcx>( @@ -104,7 +111,7 @@ pub fn predicate_obligations<'a, 'tcx>( span: Span, ) -> Vec<traits::PredicateObligation<'tcx>> { let mut wf = WfPredicates { - infcx, + tcx: infcx.tcx, param_env, body_id, span, @@ -116,7 +123,7 @@ pub fn predicate_obligations<'a, 'tcx>( // It's ok to skip the binder here because wf code is prepared for it match predicate.kind().skip_binder() { ty::PredicateKind::Trait(t) => { - wf.compute_trait_ref(&t.trait_ref, Elaborate::None); + wf.compute_trait_pred(&t, Elaborate::None); } ty::PredicateKind::RegionOutlives(..) => {} ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { @@ -159,11 +166,11 @@ pub fn predicate_obligations<'a, 'tcx>( } } - wf.normalize() + wf.normalize(infcx) } -struct WfPredicates<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, +struct WfPredicates<'tcx> { + tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, span: Span, @@ -260,18 +267,17 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } -impl<'a, 'tcx> WfPredicates<'a, 'tcx> { +impl<'tcx> WfPredicates<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx + self.tcx } fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } - fn normalize(mut self) -> Vec<traits::PredicateObligation<'tcx>> { + fn normalize(self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<traits::PredicateObligation<'tcx>> { let cause = self.cause(traits::WellFormed(None)); - let infcx = &mut self.infcx; let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); for mut obligation in self.out { @@ -295,11 +301,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) { - let tcx = self.infcx.tcx; - let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs); + fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) { + let tcx = self.tcx; + let trait_ref = &trait_pred.trait_ref; + + // if the trait predicate is not const, the wf obligations should not be const as well. + let obligations = if trait_pred.constness == ty::BoundConstness::NotConst { + self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs) + } else { + self.nominal_obligations(trait_ref.def_id, trait_ref.substs) + }; - debug!("compute_trait_ref obligations {:?}", obligations); + debug!("compute_trait_pred obligations {:?}", obligations); let param_env = self.param_env; let depth = self.recursion_depth; @@ -410,14 +423,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); let trait_ref = ty::TraitRef { - def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None), - substs: self.infcx.tcx.mk_substs_trait(subty, &[]), + def_id: self.tcx.require_lang_item(LangItem::Sized, None), + substs: self.tcx.mk_substs_trait(subty, &[]), }; self.out.push(traits::Obligation::with_depth( cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(trait_ref).without_const().to_predicate(self.infcx.tcx), + ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx), )); } } @@ -452,26 +465,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { predicate, )); } - ty::ConstKind::Infer(infer) => { - let resolved = self.infcx.shallow_resolve(infer); - // the `InferConst` changed, meaning that we made progress. - if resolved != infer { - let cause = self.cause(traits::WellFormed(None)); - - let resolved_constant = self.infcx.tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Infer(resolved), - ty: constant.ty(), - }); - self.out.push(traits::Obligation::with_depth( - cause, - self.recursion_depth, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed( - resolved_constant.into(), - )) + ty::ConstKind::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + + self.out.push(traits::Obligation::with_depth( + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(constant.into())) .to_predicate(self.tcx()), - )); - } + )); } ty::ConstKind::Error(_) | ty::ConstKind::Param(_) @@ -563,7 +566,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - ty::Generator(..) => { + ty::Generator(did, substs, ..) => { // Walk ALL the types in the generator: this will // include the upvar types as well as the yield // type. Note that this is mildly distinct from @@ -571,6 +574,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // about the signature of the closure. We don't // have the problem of implied bounds here since // generators don't take arguments. + let obligations = self.nominal_obligations(did, substs); + self.out.extend(obligations); } ty::Closure(did, substs) => { @@ -622,12 +627,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } ty::Opaque(did, substs) => { - // all of the requirements on type parameters - // should've been checked by the instantiation - // of whatever returned this exact `impl Trait`. - - // for named opaque `impl Trait` types we still need to check them - if ty::is_impl_trait_defn(self.infcx.tcx, did).is_none() { + // All of the requirements on type parameters + // have already been checked for `impl Trait` in + // return position. We do need to check type-alias-impl-trait though. + if ty::is_impl_trait_defn(self.tcx, did).is_none() { let obligations = self.nominal_obligations(did, substs); self.out.extend(obligations); } @@ -675,57 +678,71 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // See also the comment on `fn obligations`, describing "livelock" // prevention, which happens before this can be reached. ty::Infer(_) => { - let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(ty::TyVar(_)) = ty.kind() { - // Not yet resolved, but we've made progress. - let cause = self.cause(traits::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - cause, - self.recursion_depth, - param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())) - .to_predicate(self.tcx()), - )); - } else { - // Yes, resolved, proceed with the result. - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(ty.into()); - } + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + cause, + self.recursion_depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())) + .to_predicate(self.tcx()), + )); } } } } - fn nominal_obligations( + #[instrument(level = "debug", skip(self))] + fn nominal_obligations_inner( &mut self, def_id: DefId, substs: SubstsRef<'tcx>, + remap_constness: bool, ) -> Vec<traits::PredicateObligation<'tcx>> { - let predicates = self.infcx.tcx.predicates_of(def_id); + let predicates = self.tcx.predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; let mut head = predicates; while let Some(parent) = head.parent { - head = self.infcx.tcx.predicates_of(parent); + head = self.tcx.predicates_of(parent); origins.extend(iter::repeat(parent).take(head.predicates.len())); } - let predicates = predicates.instantiate(self.infcx.tcx, substs); + let predicates = predicates.instantiate(self.tcx, substs); + trace!("{:#?}", predicates); debug_assert_eq!(predicates.predicates.len(), origins.len()); iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev()) - .map(|((pred, span), origin_def_id)| { + .map(|((mut pred, span), origin_def_id)| { let code = if span.is_dummy() { traits::MiscObligation } else { traits::BindingObligation(origin_def_id, span) }; let cause = self.cause(code); + if remap_constness { + pred = pred.without_const(self.tcx); + } traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() } + fn nominal_obligations( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Vec<traits::PredicateObligation<'tcx>> { + self.nominal_obligations_inner(def_id, substs, false) + } + + fn nominal_obligations_without_const( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Vec<traits::PredicateObligation<'tcx>> { + self.nominal_obligations_inner(def_id, substs, true) + } + fn from_object_ty( &mut self, ty: Ty<'tcx>, @@ -764,7 +781,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Note: in fact we only permit builtin traits, not `Bar<'d>`, I // am looking forward to the future here. if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.infcx.tcx, data); + let implicit_bounds = object_region_bounds(self.tcx, data); let explicit_bound = region; @@ -777,7 +794,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, self.param_env, - outlives.to_predicate(self.infcx.tcx), + outlives.to_predicate(self.tcx), )); } } diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 3fc141471b9..49c9ba45963 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -1,4 +1,4 @@ -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::source_map::DUMMY_SP; @@ -16,7 +16,9 @@ fn evaluate_obligation<'tcx>( canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result<EvaluationResult, OverflowError> { debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); - tcx.infer_ctxt().enter_with_canonical( + // HACK This bubble is required for this tests to pass: + // impl-trait/issue99642.rs + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_with_canonical( DUMMY_SP, &canonical_goal, |ref infcx, goal, _canonical_inference_vars| { diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index c7cac8fca89..4d4d55de5f4 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -47,7 +47,7 @@ fn compute_implied_outlives_bounds<'tcx>( // 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::stable_set::FxHashSet::default(); + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); let mut wf_args = vec![ty.into()]; let mut implied_bounds = vec![]; diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index f8bac1d7b26..d895b647db0 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -2,7 +2,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; @@ -258,10 +258,15 @@ fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { - tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { - type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy()); - Ok(()) - }) + // HACK This bubble is required for this test to pass: + // impl-trait/issue-99642.rs + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_canonical_trait_query( + &canonicalized, + |infcx, fulfill_cx, key| { + type_op_prove_predicate_with_cause(infcx, fulfill_cx, key, ObligationCause::dummy()); + Ok(()) + }, + ) } /// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors, diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index d03d675bfd2..caad2ed4274 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -15,3 +15,4 @@ rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_type_ir = { path = "../rustc_type_ir" } +rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 0b83cdb78dc..7c2f4db94ff 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -1,4 +1,12 @@ -use rustc_middle::ty::{self, TyCtxt}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; +use rustc_middle::{mir, thir}; +use rustc_span::Span; use rustc_target::abi::VariantIdx; use std::iter; @@ -72,6 +80,390 @@ pub(crate) fn destructure_const<'tcx>( ty::DestructuredConst { variant, fields } } +pub struct AbstractConstBuilder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body_id: thir::ExprId, + body: &'a thir::Thir<'tcx>, + /// The current WIP node tree. + nodes: IndexVec<NodeId, Node<'tcx>>, +} + +impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { + fn root_span(&self) -> Span { + self.body.exprs[self.body_id].span + } + + fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { + let reported = self + .tcx + .sess + .struct_span_err(self.root_span(), "overly complex generic constant") + .span_label(span, msg) + .help("consider moving this anonymous constant into a `const` function") + .emit(); + + Err(reported) + } + fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { + let reported = self + .tcx + .sess + .struct_span_err(self.root_span(), "overly complex generic constant") + .span_label(span, msg) + .help("consider moving this anonymous constant into a `const` function") + .note("this operation may be supported in the future") + .emit(); + + Err(reported) + } + + #[instrument(skip(tcx, body, body_id), level = "debug")] + pub fn new( + tcx: TyCtxt<'tcx>, + (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), + ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { + let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; + + struct IsThirPolymorphic<'a, 'tcx> { + is_poly: bool, + thir: &'a thir::Thir<'tcx>, + } + + use crate::rustc_middle::thir::visit::Visitor; + use thir::visit; + + impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { + fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { + if expr.ty.has_param_types_or_consts() { + return true; + } + + match expr.kind { + thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), + thir::ExprKind::ConstParam { .. } => true, + thir::ExprKind::Repeat { value, count } => { + self.visit_expr(&self.thir()[value]); + count.has_param_types_or_consts() + } + _ => false, + } + } + + fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { + if pat.ty.has_param_types_or_consts() { + return true; + } + + match pat.kind.as_ref() { + thir::PatKind::Constant { value } => value.has_param_types_or_consts(), + thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { + lo.has_param_types_or_consts() || hi.has_param_types_or_consts() + } + _ => false, + } + } + } + + impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + fn thir(&self) -> &'a thir::Thir<'tcx> { + &self.thir + } + + #[instrument(skip(self), level = "debug")] + fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { + self.is_poly |= self.expr_is_poly(expr); + if !self.is_poly { + visit::walk_expr(self, expr) + } + } + + #[instrument(skip(self), level = "debug")] + fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { + self.is_poly |= self.pat_is_poly(pat); + if !self.is_poly { + visit::walk_pat(self, pat); + } + } + } + + let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; + visit::walk_expr(&mut is_poly_vis, &body[body_id]); + debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); + if !is_poly_vis.is_poly { + return Ok(None); + } + + Ok(Some(builder)) + } + + /// We do not allow all binary operations in abstract consts, so filter disallowed ones. + fn check_binop(op: mir::BinOp) -> bool { + use mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le + | Ne | Ge | Gt => true, + Offset => false, + } + } + + /// While we currently allow all unary operations, we still want to explicitly guard against + /// future changes here. + fn check_unop(op: mir::UnOp) -> bool { + use mir::UnOp::*; + match op { + Not | Neg => true, + } + } + + /// Builds the abstract const by walking the thir and bailing out when + /// encountering an unsupported operation. + pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { + debug!("AbstractConstBuilder::build: body={:?}", &*self.body); + self.recurse_build(self.body_id)?; + + for n in self.nodes.iter() { + if let Node::Leaf(ct) = n { + if let ty::ConstKind::Unevaluated(ct) = ct.kind() { + // `AbstractConst`s should not contain any promoteds as they require references which + // are not allowed. + assert_eq!(ct.promoted, None); + assert_eq!(ct, self.tcx.erase_regions(ct)); + } + } + } + + Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) + } + + fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { + use thir::ExprKind; + let node = &self.body.exprs[node]; + Ok(match &node.kind { + // I dont know if handling of these 3 is correct + &ExprKind::Scope { value, .. } => self.recurse_build(value)?, + &ExprKind::PlaceTypeAscription { source, .. } + | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, + &ExprKind::Literal { lit, neg} => { + let sp = node.span; + let constant = + match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported) => { + self.tcx.const_error(node.ty) + } + Err(LitToConstError::TypeError) => { + bug!("encountered type error in lit_to_const") + } + }; + + self.nodes.push(Node::Leaf(constant)) + } + &ExprKind::NonHirLiteral { lit , user_ty: _} => { + let val = ty::ValTree::from_scalar_int(lit); + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + } + &ExprKind::ZstLiteral { user_ty: _ } => { + let val = ty::ValTree::zst(); + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) + } + &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { + let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); + + let constant = self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Unevaluated(uneval), + ty: node.ty, + }); + + self.nodes.push(Node::Leaf(constant)) + } + + ExprKind::ConstParam {param, ..} => { + let const_param = self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Param(*param), + ty: node.ty, + }); + self.nodes.push(Node::Leaf(const_param)) + } + + ExprKind::Call { fun, args, .. } => { + let fun = self.recurse_build(*fun)?; + + let mut new_args = Vec::<NodeId>::with_capacity(args.len()); + for &id in args.iter() { + new_args.push(self.recurse_build(id)?); + } + let new_args = self.tcx.arena.alloc_slice(&new_args); + self.nodes.push(Node::FunctionCall(fun, new_args)) + } + &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { + let lhs = self.recurse_build(lhs)?; + let rhs = self.recurse_build(rhs)?; + self.nodes.push(Node::Binop(op, lhs, rhs)) + } + &ExprKind::Unary { op, arg } if Self::check_unop(op) => { + let arg = self.recurse_build(arg)?; + self.nodes.push(Node::UnaryOp(op, arg)) + } + // This is necessary so that the following compiles: + // + // ``` + // fn foo<const N: usize>(a: [(); N + 1]) { + // bar::<{ N + 1 }>(); + // } + // ``` + ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => { + self.recurse_build(*e)? + } + // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a + // "coercion cast" i.e. using a coercion or is a no-op. + // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) + &ExprKind::Use { source } => { + let arg = self.recurse_build(source)?; + self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) + } + &ExprKind::Cast { source } => { + let arg = self.recurse_build(source)?; + self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) + } + ExprKind::Borrow{ arg, ..} => { + let arg_node = &self.body.exprs[*arg]; + + // Skip reborrows for now until we allow Deref/Borrow/AddressOf + // expressions. + // FIXME(generic_const_exprs): Verify/explain why this is sound + if let ExprKind::Deref { arg } = arg_node.kind { + self.recurse_build(arg)? + } else { + self.maybe_supported_error( + node.span, + "borrowing is not supported in generic constants", + )? + } + } + // FIXME(generic_const_exprs): We may want to support these. + ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error( + node.span, + "dereferencing or taking the address is not supported in generic constants", + )?, + ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( + node.span, + "array construction is not supported in generic constants", + )?, + ExprKind::Block { .. } => self.maybe_supported_error( + node.span, + "blocks are not supported in generic constant", + )?, + ExprKind::NeverToAny { .. } => self.maybe_supported_error( + node.span, + "converting nevers to any is not supported in generic constant", + )?, + ExprKind::Tuple { .. } => self.maybe_supported_error( + node.span, + "tuple construction is not supported in generic constants", + )?, + ExprKind::Index { .. } => self.maybe_supported_error( + node.span, + "indexing is not supported in generic constant", + )?, + ExprKind::Field { .. } => self.maybe_supported_error( + node.span, + "field access is not supported in generic constant", + )?, + ExprKind::ConstBlock { .. } => self.maybe_supported_error( + node.span, + "const blocks are not supported in generic constant", + )?, + ExprKind::Adt(_) => self.maybe_supported_error( + node.span, + "struct/enum construction is not supported in generic constants", + )?, + // dont know if this is correct + ExprKind::Pointer { .. } => + self.error(node.span, "pointer casts are not allowed in generic constants")?, + ExprKind::Yield { .. } => + self.error(node.span, "generator control flow is not allowed in generic constants")?, + ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self + .error( + node.span, + "loops and loop control flow are not supported in generic constants", + )?, + ExprKind::Box { .. } => + self.error(node.span, "allocations are not allowed in generic constants")?, + + ExprKind::Unary { .. } => unreachable!(), + // we handle valid unary/binary ops above + ExprKind::Binary { .. } => + self.error(node.span, "unsupported binary operation in generic constants")?, + ExprKind::LogicalOp { .. } => + self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?, + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { + self.error(node.span, "assignment is not supported in generic constants")? + } + ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error( + node.span, + "closures and function keywords are not supported in generic constants", + )?, + // let expressions imply control flow + ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => + self.error(node.span, "control flow is not supported in generic constants")?, + ExprKind::InlineAsm { .. } => { + self.error(node.span, "assembly is not supported in generic constants")? + } + + // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen + ExprKind::VarRef { .. } + | ExprKind::UpvarRef { .. } + | ExprKind::StaticRef { .. } + | ExprKind::ThreadLocalRef(_) => { + self.error(node.span, "unsupported operation in generic constant")? + } + }) + } +} + +/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. +pub fn thir_abstract_const<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam<LocalDefId>, +) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { + if tcx.features().generic_const_exprs { + match tcx.def_kind(def.did) { + // FIXME(generic_const_exprs): We currently only do this for anonymous constants, + // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether + // we want to look into them or treat them as opaque projections. + // + // Right now we do neither of that and simply always fail to unify them. + DefKind::AnonConst | DefKind::InlineConst => (), + _ => return Ok(None), + } + + let body = tcx.thir_body(def)?; + + AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? + .map(AbstractConstBuilder::build) + .transpose() + } else { + Ok(None) + } +} + pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { destructure_const, ..*providers }; + *providers = ty::query::Providers { + destructure_const, + thir_abstract_const: |tcx, def_id| { + let def_id = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.thir_abstract_const_of_const_arg(def) + } else { + thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) + } + }, + thir_abstract_const_of_const_arg: |tcx, (did, param_did)| { + thir_abstract_const( + tcx, + ty::WithOptConstParam { did, const_param_did: Some(param_did) }, + ) + }, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 5e58f237982..979e997f244 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -280,6 +280,11 @@ fn resolve_associated_item<'tcx>( return Ok(None); } + // If the item does not have a value, then we cannot return an instance. + if !leaf_def.item.defaultness.has_value() { + return Ok(None); + } + let substs = tcx.erase_regions(substs); // Check if we just resolved an associated `const` declaration from diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 7624d31b40b..09f5c2a11aa 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -7,6 +7,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(control_flow_enum)] #![feature(let_else)] +#![feature(never_type)] +#![feature(box_patterns)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 7efc82efd15..eded7891682 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -1,5 +1,5 @@ //! Check whether a type is representable. -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 1d345caf699..d542a9b5997 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -126,7 +126,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // are any errors at that point, so outside of type inference you can be // sure that this will succeed without errors anyway. - if tcx.sess.opts.debugging_opts.chalk { + if tcx.sess.opts.unstable_opts.chalk { let environment = well_formed_types_in_env(tcx, def_id); predicates.extend(environment); } diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs index 6a9ea790a30..ee249050cc6 100644 --- a/compiler/rustc_type_ir/src/codec.rs +++ b/compiler/rustc_type_ir/src/codec.rs @@ -1,6 +1,6 @@ use crate::Interner; -use rustc_data_structures::stable_map::FxHashMap; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decoder, Encoder}; /// The shorthand encoding uses an enum's variant index `usize` diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index fd6376ef6ee..791e9e0f5a3 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -310,6 +310,7 @@ impl DebruijnIndex { /// for<'a> fn(for<'b> fn(&'a x)) /// /// you would need to shift the index for `'a` into a new binder. + #[inline] #[must_use] pub fn shifted_in(self, amount: u32) -> DebruijnIndex { DebruijnIndex::from_u32(self.as_u32() + amount) @@ -317,18 +318,21 @@ impl DebruijnIndex { /// Update this index in place by shifting it "in" through /// `amount` number of binders. + #[inline] pub fn shift_in(&mut self, amount: u32) { *self = self.shifted_in(amount); } /// Returns the resulting index when this value is moved out from /// `amount` number of new binders. + #[inline] #[must_use] pub fn shifted_out(self, amount: u32) -> DebruijnIndex { DebruijnIndex::from_u32(self.as_u32() - amount) } /// Update in place by shifting out from `amount` binders. + #[inline] pub fn shift_out(&mut self, amount: u32) { *self = self.shifted_out(amount); } @@ -353,6 +357,7 @@ impl DebruijnIndex { /// If we invoke `shift_out_to_binder` and the region is in fact /// bound by one of the binders we are shifting out of, that is an /// error (and should fail an assertion failure). + #[inline] pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self { self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32()) } diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index c873cf27e42..99a8101dc96 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -8,7 +8,7 @@ use rustc_middle::ty; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{Span, Symbol, DUMMY_SP}; use std::collections::BTreeSet; @@ -17,7 +17,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// the type parameter's name as a placeholder. pub(crate) fn complain_about_missing_type_params( &self, - missing_type_params: Vec<String>, + missing_type_params: Vec<Symbol>, def_id: DefId, span: Span, empty_generic_args: bool, diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 612dc384521..40aa27a29e9 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_type = tcx.infer_ctxt().enter(|infcx| { infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)) }); - if param_type.is_suggestable(tcx) { + if param_type.is_suggestable(tcx, false) { err.span_suggestion( tcx.def_span(src_def_id), "consider changing this type parameter to be a `const` generic", diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 0a2b54eec47..08e8e6f7d0f 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.mk_region(ty::ReLateBound(debruijn, br)) } - Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(index), - kind: ty::BrAnon(anon_index), - }; - tcx.mk_region(ty::ReLateBound(debruijn, br)) - } - Some(rl::Region::EarlyBound(index, id)) => { let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) @@ -382,7 +374,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def_id: DefId, generic_args: &'a GenericArgs<'a>, span: Span, - missing_type_params: Vec<String>, + missing_type_params: Vec<Symbol>, inferred_params: Vec<Span>, infer_args: bool, is_object: bool, @@ -439,7 +431,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // as the rest of the type. As such, we ignore missing // stability attributes. }, - ) + ); } if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) { self.inferred_params.push(ty.span); @@ -514,7 +506,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // defaults. This will lead to an ICE if we are not // careful! if self.default_needs_object_self(param) { - self.missing_type_params.push(param.name.to_string()); + self.missing_type_params.push(param.name); tcx.ty_error().into() } else { // This is a default type parameter. @@ -550,7 +542,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); if !infer_args && has_default { - EarlyBinder(tcx.const_param_default(param.def_id)) + tcx.bound_const_param_default(param.def_id) .subst(tcx, substs.unwrap()) .into() } else { @@ -1150,17 +1142,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .expect("missing associated type"); if !assoc_item.vis.is_accessible_from(def_scope, tcx) { - let kind = match assoc_item.kind { - ty::AssocKind::Type => "type", - ty::AssocKind::Const => "const", - _ => unreachable!(), - }; tcx.sess .struct_span_err( binding.span, - &format!("associated {kind} `{}` is private", binding.item_name), + &format!("{} `{}` is private", assoc_item.kind, binding.item_name), ) - .span_label(binding.span, &format!("private associated {kind}")) + .span_label(binding.span, &format!("private {}", assoc_item.kind)) .emit(); } tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); @@ -1958,11 +1945,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } - if adt_def.did().is_local() { - err.span_label( - tcx.def_span(adt_def.did()), - format!("variant `{assoc_ident}` not found for this enum"), - ); + if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { + err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); } err.emit() @@ -2489,7 +2473,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { concrete type's name `{type_name}` instead if you want to \ specify its type parameters" ), - type_name.to_string(), + type_name, Applicability::MaybeIncorrect, ); } @@ -2679,7 +2663,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, ty, opt_sugg: Some((span, Applicability::MachineApplicable)) - .filter(|_| ty.is_suggestable(tcx)), + .filter(|_| ty.is_suggestable(tcx, false)), }); ty diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 79e402b542a..147d87e7594 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -1,6 +1,6 @@ use crate::check::coercion::{AsCoercionSite, CoerceMany}; use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; @@ -9,7 +9,6 @@ use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - StatementAsExpression, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -75,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut other_arms = vec![]; // Used only for diagnostics. - let mut prior_arm_ty = None; - for (i, arm) in arms.iter().enumerate() { + let mut prior_arm = None; + for arm in arms { if let Some(g) = &arm.guard { self.diverges.set(Diverges::Maybe); match g { @@ -96,21 +95,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected); - let (arm_span, semi_span) = - self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty); - let (span, code) = match i { + let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { + (Some(blk.hir_id), self.find_block_span(blk)) + } else { + (None, arm.body.span) + }; + + let (span, code) = match prior_arm { // The reason for the first arm to fail is not that the match arms diverge, // but rather that there's a prior obligation that doesn't hold. - 0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), - _ => ( + None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), + Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => ( expr.span, ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause { + arm_block_id, arm_span, + arm_ty, + prior_arm_block_id, + prior_arm_ty, + prior_arm_span, scrut_span: scrut.span, - semi_span, source: match_src, prior_arms: other_arms.clone(), - last_ty: prior_arm_ty.unwrap(), scrut_hir_id: scrut.hir_id, opt_suggest_box_span, })), @@ -127,7 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, Some(&arm.body), arm_ty, - Some(&mut |err: &mut Diagnostic| { + Some(&mut |err| { let Some(ret) = self.ret_type_span else { return; }; @@ -139,7 +145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); self.can_coerce(arm_ty, ret_ty) - && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty)) + && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) // The match arms need to unify for the case of `impl Trait`. && !matches!(ret_ty.kind(), ty::Opaque(..)) } @@ -181,7 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if other_arms.len() > 5 { other_arms.remove(0); } - prior_arm_ty = Some(arm_ty); + + prior_arm = Some((arm_block_id, arm_ty, arm_span)); } // If all of the arms in the `match` diverge, @@ -207,28 +214,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match_ty } - fn get_appropriate_arm_semicolon_removal_span( - &self, - arms: &'tcx [hir::Arm<'tcx>], - i: usize, - prior_arm_ty: Option<Ty<'tcx>>, - arm_ty: Ty<'tcx>, - ) -> (Span, Option<(Span, StatementAsExpression)>) { - let arm = &arms[i]; - let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { - self.find_block_span(blk, prior_arm_ty) - } else { - (arm.body.span, None) - }; - if semi_span.is_none() && i > 0 { - if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind { - let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty)); - semi_span = semi_span_prev; - } - } - (arm_span, semi_span) - } - /// When the previously checked expression (the scrutinee) diverges, /// warn the user about the match arms being unreachable. fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { @@ -259,7 +244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, &mut |err| { if let Some((span, msg)) = &ret_reason { - err.span_label(*span, msg.as_str()); + err.span_label(*span, msg); } else if let ExprKind::Block(block, _) = &then_expr.kind && let Some(expr) = &block.expr { @@ -313,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else_ty: Ty<'tcx>, opt_suggest_box_span: Option<Span>, ) -> ObligationCause<'tcx> { - let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) { + let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) { // The `if`/`else` isn't in one line in the output, include some context to make it // clear it is an if/else expression: // ``` @@ -339,69 +324,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - let mut remove_semicolon = None; - let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind { - let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty)); - remove_semicolon = semi_sp; - if block.expr.is_none() && block.stmts.is_empty() { - // Avoid overlapping spans that aren't as readable: - // ``` - // 2 | let x = if true { - // | _____________- - // 3 | | 3 - // | | - expected because of this - // 4 | | } else { - // | |____________^ - // 5 | || - // 6 | || }; - // | || ^ - // | ||_____| - // | |______if and else have incompatible types - // | expected integer, found `()` - // ``` - // by not pointing at the entire expression: - // ``` - // 2 | let x = if true { - // | ------- `if` and `else` have incompatible types - // 3 | 3 - // | - expected because of this - // 4 | } else { - // | ____________^ - // 5 | | - // 6 | | }; - // | |_____^ expected integer, found `()` - // ``` - if outer_sp.is_some() { - outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span)); - } + let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind { + let block = block.innermost_block(); + + // Avoid overlapping spans that aren't as readable: + // ``` + // 2 | let x = if true { + // | _____________- + // 3 | | 3 + // | | - expected because of this + // 4 | | } else { + // | |____________^ + // 5 | || + // 6 | || }; + // | || ^ + // | ||_____| + // | |______if and else have incompatible types + // | expected integer, found `()` + // ``` + // by not pointing at the entire expression: + // ``` + // 2 | let x = if true { + // | ------- `if` and `else` have incompatible types + // 3 | 3 + // | - expected because of this + // 4 | } else { + // | ____________^ + // 5 | | + // 6 | | }; + // | |_____^ expected integer, found `()` + // ``` + if block.expr.is_none() && block.stmts.is_empty() + && let Some(outer_span) = &mut outer_span + { + *outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span); } - error_sp + + (self.find_block_span(block), block.hir_id) } else { - // shouldn't happen unless the parser has done something weird - else_expr.span + (else_expr.span, else_expr.hir_id) }; - // Compute `Span` of `then` part of `if`-expression. - let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind { - let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty)); - remove_semicolon = remove_semicolon.or(semi_sp); + let then_id = if let ExprKind::Block(block, _) = &then_expr.kind { + let block = block.innermost_block(); + // Exclude overlapping spans if block.expr.is_none() && block.stmts.is_empty() { - outer_sp = None; // same as in `error_sp`; cleanup output + outer_span = None; } - then_sp + block.hir_id } else { - // shouldn't happen unless the parser has done something weird - then_expr.span + then_expr.hir_id }; // Finally construct the cause: self.cause( error_sp, ObligationCauseCode::IfExpression(Box::new(IfExpressionCause { - then: then_sp, - else_sp: error_sp, - outer: outer_sp, - semicolon: remove_semicolon, + else_id, + then_id, + then_ty, + else_ty, + outer_span, opt_suggest_box_span, })), ) @@ -482,22 +465,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn find_block_span( - &self, - block: &'tcx hir::Block<'tcx>, - expected_ty: Option<Ty<'tcx>>, - ) -> (Span, Option<(Span, StatementAsExpression)>) { - if let Some(expr) = &block.expr { - (expr.span, None) - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - (stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty))) - } else { - // empty block; point at its entirety - (block.span, None) - } - } - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` // we check if the different arms would work with boxed trait objects instead and // provide a structured suggestion in that case. @@ -521,7 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref: ty::TraitRef { def_id: t.def_id(), - substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]), + substs: self.tcx.mk_substs_trait(outer_ty, &[]), }, constness: t.constness, polarity: t.polarity, @@ -529,9 +496,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let obl = Obligation::new( o.cause.clone(), self.param_env, - pred.to_predicate(self.infcx.tcx), + pred.to_predicate(self.tcx), ); - suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl); + suggest_box &= self.predicate_must_hold_modulo_regions(&obl); if !suggest_box { // We've encountered some obligation that didn't hold, so the // return expression can't just be boxed. We don't need to diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 3af73abe5ce..0836f15a122 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -4,7 +4,7 @@ use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, Diagnostic}; use rustc_hir as hir; -use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def::{self, Namespace, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::{ infer, @@ -285,29 +285,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_node = hir.get(parent_hir_id); if let ( hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, body, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }), .. }), hir::ExprKind::Block(..), ) = (parent_node, callee_node) { - let fn_decl_span = if hir.body(*body).generator_kind + let fn_decl_span = if hir.body(body).generator_kind == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) { // Actually need to unwrap a few more layers of HIR to get to // the _real_ closure... let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, + kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. }) = hir.get(async_closure) { - *fn_decl_span + fn_decl_span } else { return; } } else { - *fn_decl_span + fn_decl_span }; let start = fn_decl_span.shrink_to_lo(); @@ -376,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.param_env, *predicate, ); - let result = self.infcx.evaluate_obligation(&obligation); + let result = self.evaluate_obligation(&obligation); self.tcx .sess .struct_span_err( @@ -390,17 +390,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (fn_sig, Some(def_id)) } ty::FnPtr(sig) => (sig, None), - ref t => { + _ => { let mut unit_variant = None; - let mut removal_span = call_expr.span; - if let ty::Adt(adt_def, ..) = t - && adt_def.is_enum() - && let hir::ExprKind::Call(expr, _) = call_expr.kind + if let hir::ExprKind::Path(qpath) = &callee_expr.kind + && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) + = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + // Only suggest removing parens if there are no arguments + && arg_exprs.is_empty() { - removal_span = - expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + let descr = match kind { + def::CtorOf::Struct => "struct", + def::CtorOf::Variant => "enum variant", + }; + let removal_span = + callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); unit_variant = - self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); + Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); } let callee_ty = self.resolve_vars_if_possible(callee_ty); @@ -410,8 +415,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_ty, E0618, "expected function, found {}", - match unit_variant { - Some(ref path) => format!("enum variant `{path}`"), + match &unit_variant { + Some((_, kind, path)) => format!("{kind} `{path}`"), None => format!("`{callee_ty}`"), } ); @@ -423,11 +428,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_expr.span, ); - if let Some(ref path) = unit_variant { + if let Some((removal_span, kind, path)) = &unit_variant { err.span_suggestion_verbose( - removal_span, + *removal_span, &format!( - "`{path}` is a unit variant, you need to write it without the parentheses", + "`{path}` is a unit {kind}, and does not take parentheses to be constructed", ), "", Applicability::MachineApplicable, @@ -470,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = self.tcx.hir().res_span(def) { let callee_ty = callee_ty.to_string(); let label = match (unit_variant, inner_callee_path) { - (Some(path), _) => Some(format!("`{path}` defined here")), + (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), (_, Some(hir::QPath::Resolved(_, path))) => self .tcx .sess diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 66dd5582490..7aaddc2bd7a 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -69,7 +69,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - Vtable(Option<DefId>), + VTable(Option<DefId>), /// Slice Length, /// The unsize info of this projection @@ -102,7 +102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), + ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { None => Some(PointerKind::Thin), Some(f) => { @@ -951,7 +951,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), - Some(PointerKind::Vtable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), + Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))), Some( PointerKind::OfProjection(_) diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index e9709b64d93..9497d5c4528 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1,10 +1,9 @@ -use crate::check::wfcheck::for_item; +use crate::check::intrinsicck::InlineAsmCtxt; use super::coercion::CoerceMany; use super::compare_method::check_type_bounds; use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; use super::*; - use rustc_attr as attr; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; @@ -15,8 +14,9 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node, PathSegment}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; +use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; @@ -28,8 +28,8 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE use rustc_span::symbol::sym; use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::{self, ObligationCtxt}; use rustc_ty_utils::representability::{self, Representability}; use std::iter; @@ -374,8 +374,9 @@ fn check_alloc_error_fn( } } -fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) { +fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); + let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated check_representable(tcx, span, def_id); @@ -387,8 +388,9 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) { check_packed(tcx, span, def); } -fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) { +fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); + let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated check_representable(tcx, span, def_id); check_transparent(tcx, span, def); @@ -401,11 +403,37 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b let item_type = tcx.type_of(item_def_id); if let ty::Adt(def, substs) = item_type.kind() { assert!(def.is_union()); - let fields = &def.non_enum_variant().fields; + + fn allowed_union_field<'tcx>( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + ) -> bool { + // We don't just accept all !needs_drop fields, due to semver concerns. + match ty.kind() { + ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) + ty::Tuple(tys) => { + // allow tuples of allowed types + tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span)) + } + ty::Array(elem, _len) => { + // Like `Copy`, we do *not* special-case length 0. + allowed_union_field(*elem, tcx, param_env, span) + } + _ => { + // Fallback case: allow `ManuallyDrop` and things that are `Copy`. + ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) + || ty.is_copy_modulo_regions(tcx.at(span), param_env) + } + } + } + let param_env = tcx.param_env(item_def_id); - for field in fields { + for field in &def.non_enum_variant().fields { let field_ty = field.ty(tcx, substs); - if field_ty.needs_drop(tcx, param_env) { + + if !allowed_union_field(field_ty, tcx, param_env, span) { let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { // We are currently checking the type this field came from, so it must be local. Some(Node::Field(field)) => (field.span, field.ty.span), @@ -432,6 +460,9 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b ) .emit(); return false; + } else if field_ty.needs_drop(tcx, param_env) { + // This should never happen. But we can get here e.g. in case of name resolution errors. + tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields"); } } } else { @@ -441,13 +472,14 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b } /// Check that a `static` is inhabited. -fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { +fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { // Make sure statics are inhabited. // Other parts of the compiler assume that there are no uninhabited places. In principle it // would be enough to check this for `extern` statics, as statics with an initializer will // have UB during initialization if they are uninhabited, but there also seems to be no good // reason to allow any statics to be uninhabited. let ty = tcx.type_of(def_id); + let span = tcx.def_span(def_id); let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) { Ok(l) => l, // Foreign statics that overflow their allowed size should emit an error @@ -494,9 +526,9 @@ pub(super) fn check_opaque<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, substs: SubstsRef<'tcx>, - span: Span, origin: &hir::OpaqueTyOrigin, ) { + let span = tcx.def_span(def_id); check_opaque_for_inheriting_lifetimes(tcx, def_id, span); if tcx.type_of(def_id).references_error() { return; @@ -699,50 +731,52 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).enter(move |infcx| { - let inh = Inherited::new(infcx, def_id); - let infcx = &inh.infcx; - let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter( + move |infcx| { + let ocx = ObligationCtxt::new(&infcx); + let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); - let misc_cause = traits::ObligationCause::misc(span, hir_id); + let misc_cause = traits::ObligationCause::misc(span, hir_id); - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { - Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok), - Err(ty_err) => { - tcx.sess.delay_span_bug( - span, - &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), - ); + match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { + Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), + Err(ty_err) => { + tcx.sess.delay_span_bug( + span, + &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), + ); + } } - } - // Additionally require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())).to_predicate(tcx); - inh.register_predicate(Obligation::new(misc_cause, param_env, predicate)); - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => { - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors(&outlives_environment); + // Additionally require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())) + .to_predicate(tcx); + ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); } - } - - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - }); + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + defining_use_anchor, + &outlives_environment, + ); + } + } + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + }, + ); } fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { @@ -755,8 +789,8 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { match tcx.def_kind(id.def_id) { DefKind::Static(..) => { tcx.ensure().typeck(id.def_id); - maybe_check_static_with_link_section(tcx, id.def_id, tcx.def_span(id.def_id)); - check_static_inhabited(tcx, id.def_id, tcx.def_span(id.def_id)); + maybe_check_static_with_link_section(tcx, id.def_id); + check_static_inhabited(tcx, id.def_id); } DefKind::Const => { tcx.ensure().typeck(id.def_id); @@ -766,7 +800,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { return; }; - check_enum(tcx, item.span, &enum_definition.variants, item.def_id); + check_enum(tcx, &enum_definition.variants, item.def_id); } DefKind::Fn => {} // entirely within check_item_body DefKind::Impl => { @@ -817,10 +851,10 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { } } DefKind::Struct => { - check_struct(tcx, id.def_id, tcx.def_span(id.def_id)); + check_struct(tcx, id.def_id); } DefKind::Union => { - check_union(tcx, id.def_id, tcx.def_span(id.def_id)); + check_union(tcx, id.def_id); } DefKind::OpaqueTy => { let item = tcx.hir().item(id); @@ -833,7 +867,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { // See https://github.com/rust-lang/rust/issues/75100 if !tcx.sess.opts.actually_rustdoc { let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); - check_opaque(tcx, item.def_id, substs, item.span, &origin); + check_opaque(tcx, item.def_id, substs, &origin); } } DefKind::TyAlias => { @@ -897,7 +931,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span); } hir::ForeignItemKind::Static(..) => { - check_static_inhabited(tcx, def_id, item.span); + check_static_inhabited(tcx, def_id); } _ => {} } @@ -907,10 +941,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { DefKind::GlobalAsm => { let it = tcx.hir().item(id); let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) }; - for_item(tcx, it).with_fcx(|fcx| { - fcx.check_asm(asm, it.hir_id()); - Default::default() - }) + InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id()); } _ => {} } @@ -1298,7 +1329,6 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD if !adt.repr().transparent() { return; } - let sp = tcx.sess.source_map().guess_head_span(sp); if adt.is_union() && !tcx.features().transparent_unions { feature_err( @@ -1318,7 +1348,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD } } - // For each field, figure out if it's known to be a ZST and align(1) + // For each field, figure out if it's known to be a ZST and align(1), with "known" + // respecting #[non_exhaustive] attributes. let field_infos = adt.all_fields().map(|field| { let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); let param_env = tcx.param_env(field.did); @@ -1327,16 +1358,56 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD let span = tcx.hir().span_if_local(field.did).unwrap(); let zst = layout.map_or(false, |layout| layout.is_zst()); let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1); - (span, zst, align1) + if !zst { + return (span, zst, align1, None); + } + + fn check_non_exhaustive<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> { + match t.kind() { + ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), + ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), + ty::Adt(def, subst) => { + if !def.did().is_local() { + let non_exhaustive = def.is_variant_list_non_exhaustive() + || def + .variants() + .iter() + .any(ty::VariantDef::is_field_list_non_exhaustive); + let has_priv = def.all_fields().any(|f| !f.vis.is_public()); + if non_exhaustive || has_priv { + return ControlFlow::Break(( + def.descr(), + def.did(), + subst, + non_exhaustive, + )); + } + } + def.all_fields() + .map(|field| field.ty(tcx, subst)) + .try_for_each(|t| check_non_exhaustive(tcx, t)) + } + _ => ControlFlow::Continue(()), + } + } + + (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) }); - let non_zst_fields = - field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); + let non_zst_fields = field_infos + .clone() + .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); let non_zst_count = non_zst_fields.clone().count(); if non_zst_count >= 2 { bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); } - for (span, zst, align1) in field_infos { + let incompatible_zst_fields = + field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); + let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; + for (span, zst, align1, non_exhaustive) in field_infos { if zst && !align1 { struct_span_err!( tcx.sess, @@ -1348,17 +1419,32 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD .span_label(span, "has alignment larger than 1") .emit(); } + if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive { + tcx.struct_span_lint_hir( + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, + tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), + span, + |lint| { + let note = if non_exhaustive { + "is marked with `#[non_exhaustive]`" + } else { + "contains private fields" + }; + let field_ty = tcx.def_path_str_with_substs(def_id, substs); + lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types") + .note(format!("this {descr} contains `{field_ty}`, which {note}, \ + and makes it not a breaking change to become non-zero-sized in the future.")) + .emit(); + }, + ) + } } } #[allow(trivial_numeric_casts)] -fn check_enum<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - vs: &'tcx [hir::Variant<'tcx>], - def_id: LocalDefId, -) { +fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) { let def = tcx.adt_def(def_id); + let sp = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated if vs.is_empty() { diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 1681e6af812..fee872155f5 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -96,7 +96,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), ); - let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { + let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::ClosureSynthetic, span: self.tcx.hir().span(expr.hir_id), }); @@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.infcx.next_ty_var(TypeVariableOrigin { + None => self.next_ty_var(TypeVariableOrigin { // FIXME(eddyb) distinguish closure kind inference variables from the rest. kind: TypeVariableOriginKind::ClosureSynthetic, span: expr.span, @@ -182,9 +182,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::PredicateKind::Projection(proj_predicate) => self .deduce_sig_from_projection( Some(span.0), - pred.0.kind().rebind( - pred.map_bound(|_| proj_predicate).subst(self.tcx, substs), - ), + pred.0 + .kind() + .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)), ), _ => None, }); @@ -531,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 - self.infcx.commit_if_ok(|_| { + self.commit_if_ok(|_| { let mut all_obligations = vec![]; // The liberated version of this signature should be a subtype @@ -544,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. ) { // Instantiate (this part of..) S to S', i.e., with fresh variables. - let supplied_ty = self.infcx.replace_bound_vars_with_fresh_vars( + let supplied_ty = self.replace_bound_vars_with_fresh_vars( hir_ty.span, LateBoundRegionConversionTime::FnCall, supplied_sig.inputs().rebind(supplied_ty), @@ -557,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { all_obligations.extend(obligations); } - let supplied_output_ty = self.infcx.replace_bound_vars_with_fresh_vars( + let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( decl.output.span(), LateBoundRegionConversionTime::FnCall, supplied_sig.output(), diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index acd7e4a92dc..639cab98f17 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -241,13 +241,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, ) -> CoerceResult<'tcx> { debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); - assert!(a.is_ty_var() && self.infcx.shallow_resolve(a) == a); - assert!(self.infcx.shallow_resolve(b) == b); + assert!(a.is_ty_var() && self.shallow_resolve(a) == a); + assert!(self.shallow_resolve(b) == b); if b.is_ty_var() { // Two unresolved type variables: create a `Coerce` predicate. let target_ty = if self.use_lub { - self.infcx.next_ty_var(TypeVariableOrigin { + self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::LatticeVariable, span: self.cause.span, }) @@ -991,7 +991,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { self.infcx .type_implements_trait( - self.infcx.tcx.lang_items().deref_mut_trait()?, + self.tcx.lang_items().deref_mut_trait()?, expr_ty, ty::List::empty(), self.param_env, @@ -1577,8 +1577,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let parent_id = fcx.tcx.hir().get_parent_node(id); let parent = fcx.tcx.hir().get(parent_id); if let Some(expr) = expression - && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { body, .. }, .. }) = parent - && !matches!(fcx.tcx.hir().body(*body).value.kind, hir::ExprKind::Block(..)) + && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent + && !matches!(fcx.tcx.hir().body(body).value.kind, hir::ExprKind::Block(..)) { fcx.suggest_missing_semicolon(&mut err, expr, expected, true); } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 2bfb9343877..af77efc3c2d 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,13 +1,15 @@ +use super::potentially_plural_count; use crate::check::regionck::OutlivesEnvironmentExt; +use crate::check::wfcheck; use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -16,11 +18,11 @@ use rustc_middle::ty::{self, DefIdTree}; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, +}; use std::iter; -use super::{potentially_plural_count, FnCtxt, Inherited}; - /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. /// @@ -88,9 +90,10 @@ fn compare_predicate_entailment<'tcx>( let mut cause = ObligationCause::new( impl_m_span, impl_m_hir_id, - ObligationCauseCode::CompareImplMethodObligation { + ObligationCauseCode::CompareImplItemObligation { impl_item_def_id: impl_m.def_id.expect_local(), trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, }, ); @@ -171,14 +174,7 @@ fn compare_predicate_entailment<'tcx>( let trait_m_predicates = tcx.predicates_of(trait_m.def_id); // Check region bounds. - check_region_bounds_on_impl_item( - tcx, - impl_m_span, - impl_m, - trait_m, - &trait_m_generics, - &impl_m_generics, - )?; + check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?; // Create obligations for each predicate declared by the impl // definition in the context of the trait's parameter @@ -212,30 +208,29 @@ fn compare_predicate_entailment<'tcx>( ); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); - tcx.infer_ctxt().enter(|infcx| { - let inh = Inherited::new(infcx, impl_m.def_id.expect_local()); - let infcx = &inh.infcx; + tcx.infer_ctxt().enter(|ref infcx| { + let ocx = ObligationCtxt::new(infcx); debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); let mut selcx = traits::SelectionContext::new(&infcx); - let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); let traits::Normalized { value: predicate, obligations } = traits::normalize(&mut selcx, param_env, normalize_cause, predicate); - inh.register_predicates(obligations); + ocx.register_obligations(obligations); let cause = ObligationCause::new( span, impl_m_hir_id, - ObligationCauseCode::CompareImplMethodObligation { + ObligationCauseCode::CompareImplItemObligation { impl_item_def_id: impl_m.def_id.expect_local(), trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, }, ); - inh.register_predicate(traits::Obligation::new(cause, param_env, predicate)); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); } // We now need to check that the signature of the impl method is @@ -261,32 +256,31 @@ fn compare_predicate_entailment<'tcx>( infer::HigherRankedType, tcx.fn_sig(impl_m.def_id), ); - let impl_sig = - inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, impl_sig); + + let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); + let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig); let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); - let trait_sig = - inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig); + let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); // Add the resulting inputs and output as well-formed. wf_tys.extend(trait_sig.inputs_and_output.iter()); let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); debug!("compare_impl_method: trait_fty={:?}", trait_fty); - let sub_result = infcx.at(&cause, param_env).sup(trait_fty, impl_fty).map( - |InferOk { obligations, .. }| { - // FIXME: We'd want to keep more accurate spans than "the method signature" when - // processing the comparison between the trait and impl fn, but we sadly lose them - // and point at the whole signature when a trait bound or specific input or output - // type would be more appropriate. In other places we have a `Vec<Span>` - // corresponding to their `Vec<Predicate>`, but we don't have that here. - // Fixing this would improve the output of test `issue-83765.rs`. - inh.register_predicates(obligations); - }, - ); + // FIXME: We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // type would be more appropriate. In other places we have a `Vec<Span>` + // corresponding to their `Vec<Predicate>`, but we don't have that here. + // Fixing this would improve the output of test `issue-83765.rs`. + let sub_result = infcx + .at(&cause, param_env) + .sup(trait_fty, impl_fty) + .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); if let Err(terr) = sub_result { debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); @@ -298,7 +292,7 @@ fn compare_predicate_entailment<'tcx>( let mut diag = struct_span_err!( tcx.sess, - cause.span(tcx), + cause.span(), E0053, "method `{}` has an incompatible type for trait", trait_m.name @@ -392,7 +386,7 @@ fn compare_predicate_entailment<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { let reported = infcx.report_fulfillment_errors(&errors, None, false); return Err(reported); @@ -402,7 +396,10 @@ fn compare_predicate_entailment<'tcx>( // lifetime parameters. let mut outlives_environment = OutlivesEnvironment::new(param_env); outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id); - infcx.check_region_obligations_and_report_errors(&outlives_environment); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); Ok(()) }) @@ -410,7 +407,6 @@ fn compare_predicate_entailment<'tcx>( fn check_region_bounds_on_impl_item<'tcx>( tcx: TyCtxt<'tcx>, - span: Span, impl_m: &ty::AssocItem, trait_m: &ty::AssocItem, trait_generics: &ty::Generics, @@ -436,21 +432,25 @@ fn check_region_bounds_on_impl_item<'tcx>( // are zero. Since I don't quite know how to phrase things at // the moment, give a kind of vague error message. if trait_params != impl_params { - let item_kind = assoc_item_kind_str(impl_m); - let def_span = tcx.sess.source_map().guess_head_span(span); - let span = impl_m - .def_id - .as_local() - .and_then(|did| tcx.hir().get_generics(did)) - .map_or(def_span, |g| g.span); - let generics_span = trait_m.def_id.as_local().map(|did| { - let def_sp = tcx.def_span(did); - tcx.hir().get_generics(did).map_or(def_sp, |g| g.span) - }); + let span = tcx + .hir() + .get_generics(impl_m.def_id.expect_local()) + .expect("expected impl item to have generics or else we can't compare them") + .span; + let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() { + Some( + tcx.hir() + .get_generics(local_def_id) + .expect("expected trait item to have generics or else we can't compare them") + .span, + ) + } else { + None + }; let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, - item_kind, + item_kind: assoc_item_kind_str(impl_m), ident: impl_m.ident(tcx), generics_span, }); @@ -490,7 +490,7 @@ fn extract_spans_for_error_reporting<'a, 'tcx>( TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) } - _ => (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)), + _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)), } } @@ -546,7 +546,7 @@ fn compare_self_type<'tcx>( if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("trait method declared without `{self_descr}`")); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } let reported = err.emit(); return Err(reported); @@ -566,7 +566,7 @@ fn compare_self_type<'tcx>( if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("`{self_descr}` used in trait")); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } let reported = err.emit(); return Err(reported); @@ -805,7 +805,7 @@ fn compare_number_of_method_arguments<'tcx>( ), ); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } err.span_label( impl_span, @@ -1062,8 +1062,7 @@ pub(crate) fn compare_const_impl<'tcx>( tcx.infer_ctxt().enter(|infcx| { let param_env = tcx.param_env(impl_c.def_id); - let inh = Inherited::new(infcx, impl_c.def_id.expect_local()); - let infcx = &inh.infcx; + let ocx = ObligationCtxt::new(&infcx); // The below is for the most part highly similar to the procedure // for methods above. It is simpler in many respects, especially @@ -1082,24 +1081,26 @@ pub(crate) fn compare_const_impl<'tcx>( let mut cause = ObligationCause::new( impl_c_span, impl_c_hir_id, - ObligationCauseCode::CompareImplConstObligation, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_c.def_id.expect_local(), + trait_item_def_id: trait_c.def_id, + kind: impl_c.kind, + }, ); // There is no "body" here, so just pass dummy id. - let impl_ty = - inh.normalize_associated_types_in(impl_c_span, impl_c_hir_id, param_env, impl_ty); + let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); debug!("compare_const_impl: impl_ty={:?}", impl_ty); - let trait_ty = - inh.normalize_associated_types_in(impl_c_span, impl_c_hir_id, param_env, trait_ty); + let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty); debug!("compare_const_impl: trait_ty={:?}", trait_ty); let err = infcx .at(&cause, param_env) .sup(trait_ty, impl_ty) - .map(|ok| inh.register_infer_ok_obligations(ok)); + .map(|ok| ocx.register_infer_ok_obligations(ok)); if let Err(terr) = err { debug!( @@ -1146,14 +1147,15 @@ pub(crate) fn compare_const_impl<'tcx>( // Check that all obligations are satisfied by the implementation's // version. - let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { infcx.report_fulfillment_errors(&errors, None, false); return; } let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(&outlives_environment); + infcx + .resolve_regions_and_report_errors(impl_c.def_id.expect_local(), &outlives_environment); }); } @@ -1199,7 +1201,6 @@ fn compare_type_predicate_entailment<'tcx>( check_region_bounds_on_impl_item( tcx, - impl_ty_span, impl_ty, trait_ty, &trait_ty_generics, @@ -1217,15 +1218,6 @@ fn compare_type_predicate_entailment<'tcx>( // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); - let cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::CompareImplTypeObligation { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - }, - ); - debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs); // The predicates declared by the impl definition, the trait and the @@ -1244,26 +1236,38 @@ fn compare_type_predicate_entailment<'tcx>( Reveal::UserFacing, hir::Constness::NotConst, ); - let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause.clone()); + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); tcx.infer_ctxt().enter(|infcx| { - let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); - let infcx = &inh.infcx; + let ocx = ObligationCtxt::new(&infcx); debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); let mut selcx = traits::SelectionContext::new(&infcx); - for predicate in impl_ty_own_bounds.predicates { + assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); + for (span, predicate) in + std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) + { + let cause = ObligationCause::misc(span, impl_ty_hir_id); let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause.clone(), predicate); + traits::normalize(&mut selcx, param_env, cause, predicate); - inh.register_predicates(obligations); - inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate)); + let cause = ObligationCause::new( + span, + impl_ty_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + kind: impl_ty.kind, + }, + ); + ocx.register_obligations(obligations); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); } // Check that all obligations are satisfied by the implementation's // version. - let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { let reported = infcx.report_fulfillment_errors(&errors, None, false); return Err(reported); @@ -1272,7 +1276,10 @@ fn compare_type_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors(&outlives_environment); + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); Ok(()) }) @@ -1436,10 +1443,9 @@ pub fn check_type_bounds<'tcx>( impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs); tcx.infer_ctxt().enter(move |infcx| { - let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); - let infcx = &inh.infcx; - let mut selcx = traits::SelectionContext::new(&infcx); + let ocx = ObligationCtxt::new(&infcx); + let mut selcx = traits::SelectionContext::new(&infcx); let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let normalize_cause = ObligationCause::new( impl_ty_span, @@ -1464,6 +1470,7 @@ pub fn check_type_bounds<'tcx>( .map(|e| e.map_bound(|e| *e).transpose_tuple2()) .map(|(bound, span)| { debug!(?bound); + // this is where opaque type is found let concrete_ty_bound = bound.subst(tcx, rebased_substs); debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); @@ -1482,13 +1489,12 @@ pub fn check_type_bounds<'tcx>( debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); obligation.predicate = normalized_predicate; - inh.register_predicates(obligations); - inh.register_predicate(obligation); + ocx.register_obligations(obligations); + ocx.register_obligation(obligation); } - // Check that all obligations are satisfied by the implementation's // version. - let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { let reported = infcx.report_fulfillment_errors(&errors, None, false); return Err(reported); @@ -1496,16 +1502,33 @@ pub fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - // - // FIXME: Remove that `FnCtxt`. - let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); let implied_bounds = match impl_ty.container { ty::TraitContainer(_) => FxHashSet::default(), - ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span), + ty::ImplContainer(def_id) => { + wfcheck::impl_implied_bounds(tcx, param_env, def_id.expect_local(), impl_ty_span) + } }; let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, implied_bounds, impl_ty_hir_id); - infcx.check_region_obligations_and_report_errors(&outlives_environment); + outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id); + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); + + let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + for (key, value) in constraints { + infcx + .report_mismatched_types( + &ObligationCause::misc( + value.hidden_type.span, + tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), + ), + tcx.mk_opaque(key.def_id.to_def_id(), key.substs), + value.hidden_type.ty, + TypeError::Mismatch, + ) + .emit(); + } Ok(()) }) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index dc553d1441e..58b0399c5c9 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -33,6 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_expected_due_to_let_ty(err, expr, error); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); self.suggest_compatible_variants(err, expr, expected, expr_ty); + self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } @@ -286,6 +287,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_ty: Ty<'tcx>, ) { if let ty::Adt(expected_adt, substs) = expected.kind() { + if let hir::ExprKind::Field(base, ident) = expr.kind { + let base_ty = self.typeck_results.borrow().expr_ty(base); + if self.can_eq(self.param_env, base_ty, expected).is_ok() + && let Some(base_span) = base.span.find_ancestor_inside(expr.span) + { + err.span_suggestion_verbose( + expr.span.with_lo(base_span.hi()), + format!("consider removing the tuple struct field `{ident}`"), + "", + Applicability::MaybeIncorrect, + ); + return + } + } + // If the expression is of type () and it's the return expression of a block, // we suggest adding a separate return expression instead. // (To avoid things like suggesting `Ok(while .. { .. })`.) @@ -347,7 +363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let compatible_variants: Vec<String> = expected_adt + let compatible_variants: Vec<(String, Option<String>)> = expected_adt .variants() .iter() .filter(|variant| { @@ -355,6 +371,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .filter_map(|variant| { let sole_field = &variant.fields[0]; + + let field_is_local = sole_field.did.is_local(); + let field_is_accessible = + sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx); + + if !field_is_local && !field_is_accessible { + return None; + } + + let note_about_variant_field_privacy = (field_is_local && !field_is_accessible) + .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)")); + let sole_field_ty = sole_field.ty(self.tcx, substs); if self.can_coerce(expr_ty, sole_field_ty) { let variant_path = @@ -363,9 +391,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(path) = variant_path.strip_prefix("std::prelude::") && let Some((_, path)) = path.split_once("::") { - return Some(path.to_string()); + return Some((path.to_string(), note_about_variant_field_privacy)); } - Some(variant_path) + Some((variant_path, note_about_variant_field_privacy)) } else { None } @@ -379,10 +407,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &compatible_variants[..] { [] => { /* No variants to format */ } - [variant] => { + [(variant, note)] => { // Just a single matching variant. err.multipart_suggestion_verbose( - &format!("try wrapping the expression in `{variant}`"), + &format!( + "try wrapping the expression in `{variant}`{note}", + note = note.as_deref().unwrap_or("") + ), vec![ (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")), (expr.span.shrink_to_hi(), ")".to_string()), @@ -397,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "try wrapping the expression in a variant of `{}`", self.tcx.def_path_str(expected_adt.did()) ), - compatible_variants.into_iter().map(|variant| { + compatible_variants.into_iter().map(|(variant, _)| { vec![ (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")), (expr.span.shrink_to_hi(), ")".to_string()), @@ -410,6 +441,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn suggest_non_zero_new_unwrap( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + expr_ty: Ty<'tcx>, + ) { + let tcx = self.tcx; + let (adt, unwrap) = match expected.kind() { + // In case Option<NonZero*> is wanted, but * is provided, suggest calling new + ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => { + // Unwrap option + let ty::Adt(adt, _) = substs.type_at(0).kind() else { return }; + + (adt, "") + } + // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types + ty::Adt(adt, _) => (adt, ".unwrap()"), + _ => return, + }; + + let map = [ + (sym::NonZeroU8, tcx.types.u8), + (sym::NonZeroU16, tcx.types.u16), + (sym::NonZeroU32, tcx.types.u32), + (sym::NonZeroU64, tcx.types.u64), + (sym::NonZeroU128, tcx.types.u128), + (sym::NonZeroI8, tcx.types.i8), + (sym::NonZeroI16, tcx.types.i16), + (sym::NonZeroI32, tcx.types.i32), + (sym::NonZeroI64, tcx.types.i64), + (sym::NonZeroI128, tcx.types.i128), + ]; + + let Some((s, _)) = map + .iter() + .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)) + else { return }; + + let path = self.tcx.def_path_str(adt.non_enum_variant().def_id); + + err.multipart_suggestion( + format!("consider calling `{s}::new`"), + vec![ + (expr.span.shrink_to_lo(), format!("{path}::new(")), + (expr.span.shrink_to_hi(), format!("){unwrap}")), + ], + Applicability::MaybeIncorrect, + ); + } + pub fn get_conversion_methods( &self, span: Span, @@ -483,7 +565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param_parent = self.tcx.hir().get_parent_node(*param_hir_id); let Some(Node::Expr(hir::Expr { hir_id: expr_hir_id, - kind: hir::ExprKind::Closure { fn_decl: closure_fn_decl, .. }, + kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }), .. })) = self.tcx.hir().find(param_parent) else { return None; @@ -748,7 +830,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), _, &ty::Ref(_, checked, _), - ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => { + ) if self.can_sub(self.param_env, checked, expected).is_ok() => { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { @@ -892,7 +974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For this suggestion to make sense, the type would need to be `Copy`, // or we have to be moving out of a `Box<T>` - if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + if self.type_is_copy_modulo_regions(self.param_env, expected, sp) // FIXME(compiler-errors): We can actually do this if the checked_ty is // `steps` layers of boxes, not just one, but this is easier and most likely. || (checked_ty.is_box() && steps == 1) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 58c01a34cad..ba5ef5edc86 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -35,7 +35,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath}; +use rustc_hir::{Closure, ExprKind, HirId, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; @@ -48,7 +48,7 @@ use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -282,11 +282,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expr.kind { ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected), ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs), + ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), ExprKind::Assign(lhs, rhs, span) => { self.check_expr_assign(expr, expected, lhs, rhs, span) } - ExprKind::AssignOp(op, lhs, rhs) => self.check_binop_assign(expr, op, lhs, rhs), + ExprKind::AssignOp(op, lhs, rhs) => { + self.check_binop_assign(expr, op, lhs, rhs, expected) + } ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) @@ -319,7 +321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Match(discrim, arms, match_src) => { self.check_match(expr, &discrim, arms, expected, match_src) } - ExprKind::Closure { capture_clause, fn_decl, body, movability, .. } => { + ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) } ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), @@ -404,14 +406,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::UnOp::Not => { - let result = self.check_user_unop(expr, oprnd_t, unop); + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); // If it's builtin, we can reuse the type, this helps inference. if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { oprnd_t = result; } } hir::UnOp::Neg => { - let result = self.check_user_unop(expr, oprnd_t, unop); + let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); // If it's builtin, we can reuse the type, this helps inference. if !oprnd_t.is_numeric() { oprnd_t = result; @@ -529,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.ty_error() } Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, expr.span); + report_unexpected_variant_res(tcx, res, qpath, expr.span); tcx.ty_error() } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, @@ -997,26 +999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce(self, &self.misc(sp), then_expr, then_ty); if let Some(else_expr) = opt_else_expr { - let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) { - // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)` - // for errors that point to the offending expression rather than the entire block. - // We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no - // way to detect that the expected type originated from let-else and provide - // a customized error. - let else_ty = self.check_expr(else_expr); - let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse); - - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - self.tcx.ty_error() - } else { - else_ty - } - } else { - self.check_expr_with_expectation(else_expr, expected) - }; + let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); @@ -1820,7 +1803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .source_map() .span_to_snippet(range_end.expr.span) .map(|s| format!(" from `{s}`")) - .unwrap_or(String::new()); + .unwrap_or_default(); err.span_suggestion( range_start.span.shrink_to_hi(), &format!("to set the remaining fields{instead}, separate the last named field with a comma"), @@ -1873,7 +1856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let remaining_private_fields_len = remaining_private_fields.len(); let names = match &remaining_private_fields .iter() - .map(|(name, _, _)| name.to_string()) + .map(|(name, _, _)| name) .collect::<Vec<_>>()[..] { _ if remaining_private_fields_len > 6 => String::new(), @@ -2162,14 +2145,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if !expr_t.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, expr_t); } else { - type_error_struct!( + let field_name = field.to_string(); + let mut err = type_error_struct!( self.tcx().sess, field.span, expr_t, E0610, "`{expr_t}` is a primitive type and therefore doesn't have fields", - ) - .emit(); + ); + let is_valid_suffix = |field: String| { + if field == "f32" || field == "f64" { + return true; + } + let mut chars = field.chars().peekable(); + match chars.peek() { + Some('e') | Some('E') => { + chars.next(); + if let Some(c) = chars.peek() + && !c.is_numeric() && *c != '-' && *c != '+' + { + return false; + } + while let Some(c) = chars.peek() { + if !c.is_numeric() { + break; + } + chars.next(); + } + } + _ => (), + } + let suffix = chars.collect::<String>(); + suffix.is_empty() || suffix == "f32" || suffix == "f64" + }; + if let ty::Infer(ty::IntVar(_)) = expr_t.kind() + && let ExprKind::Lit(Spanned { + node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), + .. + }) = base.kind + && !base.span.from_expansion() + && is_valid_suffix(field_name) + { + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "If the number is meant to be a floating point number, consider adding a `0` after the period", + '0', + Applicability::MaybeIncorrect, + ); + } + err.emit(); } self.tcx().ty_error() @@ -2211,7 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, ) { - let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; @@ -2338,7 +2362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false }; let expr_snippet = - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or(String::new()); + self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default(); let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')'); let after_open = expr.span.lo() + rustc_span::BytePos(1); let before_close = expr.span.hi() - rustc_span::BytePos(1); diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index 15788f410f1..4059b3403b1 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -1,9 +1,8 @@ use crate::check::FnCtxt; use rustc_data_structures::{ - fx::FxHashMap, + fx::{FxHashMap, FxHashSet}, graph::WithSuccessors, graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, - stable_set::FxHashSet, }; use rustc_middle::ty::{self, Ty}; @@ -219,9 +218,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .diverging_type_vars .borrow() .iter() - .map(|&ty| self.infcx.shallow_resolve(ty)) + .map(|&ty| self.shallow_resolve(ty)) .filter_map(|ty| ty.ty_vid()) - .map(|vid| self.infcx.root_var(vid)) + .map(|vid| self.root_var(vid)) .collect(); debug!( "calculate_diverging_fallback: diverging_type_vars={:?}", @@ -237,7 +236,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut diverging_vids = vec![]; let mut non_diverging_vids = vec![]; for unsolved_vid in unsolved_vids { - let root_vid = self.infcx.root_var(unsolved_vid); + let root_vid = self.root_var(unsolved_vid); debug!( "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", unsolved_vid, @@ -272,7 +271,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // variables. (Note that this set consists of "root variables".) let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph); for &non_diverging_vid in &non_diverging_vids { - let root_vid = self.infcx.root_var(non_diverging_vid); + let root_vid = self.root_var(non_diverging_vid); if roots_reachable_from_diverging.visited(root_vid) { continue; } @@ -295,7 +294,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { diverging_fallback.reserve(diverging_vids.len()); for &diverging_vid in &diverging_vids { let diverging_ty = self.tcx.mk_ty_var(diverging_vid); - let root_vid = self.infcx.root_var(diverging_vid); + let root_vid = self.root_var(diverging_vid); let can_reach_non_diverging = coercion_graph .depth_first_search(root_vid) .any(|n| roots_reachable_from_non_diverging.visited(n)); @@ -303,7 +302,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false }; for (vid, rel) in relationships.iter() { - if self.infcx.root_var(*vid) == root_vid { + if self.root_var(*vid) == root_vid { relationship.self_in_trait |= rel.self_in_trait; relationship.output |= rel.output; } @@ -388,12 +387,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }) .collect(); debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges); - let num_ty_vars = self.infcx.num_ty_vars(); + let num_ty_vars = self.num_ty_vars(); VecGraph::new(num_ty_vars, coercion_edges) } /// If `ty` is an unresolved type variable, returns its root vid. fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> { - Some(self.infcx.root_var(self.infcx.shallow_resolve(ty).ty_vid()?)) + Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index cf7de1dc016..d1c10a3b63c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -30,18 +30,15 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt, - WellFormedLoc, + self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; use std::collections::hash_map::Entry; -use std::iter; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -188,12 +185,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !method.substs.is_empty() { let method_generics = self.tcx.generics_of(method.def_id); if !method_generics.params.is_empty() { - let user_type_annotation = self.infcx.probe(|_| { + let user_type_annotation = self.probe(|_| { let user_substs = UserSubsts { substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { let i = param.index as usize; if i < method_generics.parent_count { - self.infcx.var_for_def(DUMMY_SP, param) + self.var_for_def(DUMMY_SP, param) } else { method.substs[i] } @@ -201,7 +198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { user_self_ty: None, // not relevant here }; - self.infcx.canonicalize_user_type_annotation(UserType::TypeOf( + self.canonicalize_user_type_annotation(UserType::TypeOf( method.def_id, user_substs, )) @@ -239,7 +236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("fcx {}", self.tag()); if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf( + let canonicalized = self.canonicalize_user_type_annotation(UserType::TypeOf( def_id, UserSubsts { substs, user_self_ty }, )); @@ -375,29 +372,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result, spans) } - /// Convenience method which tracks extra diagnostic information for normalization - /// that occurs as a result of WF checking. The `hir_id` is the `HirId` of the hir item - /// whose type is being wf-checked - this is used to construct a more precise span if - /// an error occurs. - /// - /// It is never necessary to call this method - calling `normalize_associated_types_in` will - /// just result in a slightly worse diagnostic span, and will still be sound. - pub(in super::super) fn normalize_associated_types_in_wf<T>( - &self, - span: Span, - value: T, - loc: WellFormedLoc, - ) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in_with_cause( - ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(Some(loc))), - self.param_env, - value, - ) - } - pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T where T: TypeFoldable<'tcx>, @@ -437,6 +411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), + output_pred: None, }, ), self.param_env, @@ -505,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.infcx.canonicalize_response(UserType::Ty(ty)); + let c_ty = self.canonicalize_response(UserType::Ty(ty)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); } @@ -786,12 +761,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path. if formal_ret.has_infer_types() { for ty in ret_ty.walk() { - if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() { - if let ty::Opaque(def_id, _) = *ty.kind() { - if self.infcx.opaque_type_origin(def_id, DUMMY_SP).is_some() { - return None; - } - } + if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() + && let ty::Opaque(def_id, _) = *ty.kind() + && let Some(def_id) = def_id.as_local() + && self.opaque_type_origin(def_id, DUMMY_SP).is_some() { + return None; } } } @@ -852,7 +826,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.tcx.bound_type_of(def_id) }; - let substs = self.infcx.fresh_substs_for_item(span, def_id); + let substs = self.fresh_substs_for_item(span, def_id); let ty = item_ty.subst(self.tcx, substs); self.write_resolution(hir_id, Ok((def_kind, def_id))); @@ -1083,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - pub(in super::super) fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option<(Span, StatementAsExpression)> { - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let last_stmt = blk.stmts.last()?; - let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { - return None; - }; - let last_expr_ty = self.node_ty(last_expr.hir_id); - let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { - (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _)) - if last_def_id == exp_def_id => - { - StatementAsExpression::CorrectType - } - (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { - debug!( - "both opaque, likely future {:?} {:?} {:?} {:?}", - last_def_id, last_bounds, exp_def_id, exp_bounds - ); - - let last_local_id = last_def_id.as_local()?; - let exp_local_id = exp_def_id.as_local()?; - - match ( - &self.tcx.hir().expect_item(last_local_id).kind, - &self.tcx.hir().expect_item(exp_local_id).kind, - ) { - ( - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), - ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { - match (left, right) { - ( - hir::GenericBound::Trait(tl, ml), - hir::GenericBound::Trait(tr, mr), - ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() - && ml == mr => - { - true - } - ( - hir::GenericBound::LangItemTrait(langl, _, _, argsl), - hir::GenericBound::LangItemTrait(langr, _, _, argsr), - ) if langl == langr => { - // FIXME: consider the bounds! - debug!("{:?} {:?}", argsl, argsr); - true - } - _ => false, - } - }) => - { - StatementAsExpression::NeedsBoxing - } - _ => StatementAsExpression::CorrectType, - } - } - _ => StatementAsExpression::CorrectType, - }; - if (matches!(last_expr_ty.kind(), ty::Error(_)) - || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()) - && matches!(needs_box, StatementAsExpression::CorrectType) - { - return None; - } - let span = if last_stmt.span.from_expansion() { - let mac_call = original_sp(last_stmt.span, blk.span); - self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? - } else { - last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) - }; - Some((span, needs_box)) - } - // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. #[instrument(skip(self, span), level = "debug")] @@ -1426,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } GenericParamDefKind::Const { has_default } => { if !infer_args && has_default { - EarlyBinder(tcx.const_param_default(param.def_id)) + tcx.bound_const_param_default(param.def_id) .subst(tcx, substs.unwrap()) .into() } else { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a7c7089234a..eb22938fb61 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -4,6 +4,7 @@ use crate::check::fn_ctxt::arg_matrix::{ ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx, }; use crate::check::gather_locals::Declaration; +use crate::check::intrinsicck::InlineAsmCtxt; use crate::check::method::MethodCallee; use crate::check::Expectation::*; use crate::check::TupleArgumentsFlag::*; @@ -14,22 +15,23 @@ use crate::check::{ use crate::structured_errors::StructuredDiagnostic; use rustc_ast as ast; -use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan}; +use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ExprKind, Node, QPath}; use rustc_index::vec::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; use std::slice; @@ -56,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); - self.check_asm(asm, enclosing_id); + InlineAsmCtxt::new_in_fn(self).check_asm(asm, enclosing_id); } } @@ -89,7 +91,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args_no_rcvr, false, tuple_arguments, - None, + method.ok().map(|method| method.def_id), ); return self.tcx.ty_error(); } @@ -393,41 +395,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !call_appears_satisfied { - // Next, let's construct the error - let (error_span, full_call_span, ctor_of) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { - span, - kind: - hir::ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. }, - )), - .. - }, - _, - ) => (call_span, *span, Some(of)), - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), - hir::ExprKind::MethodCall(path_segment, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - ( - *span, ident_span, None, // methods are never ctors - ) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - let call_name = match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }; - let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { minimum_input_count @@ -451,13 +418,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { compatibility_diagonal, formal_and_expected_inputs, provided_args, - full_call_span, - error_span, - args_span, - call_name, c_variadic, err_code, fn_def_id, + call_span, + call_expr, ); } } @@ -467,14 +432,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, - full_call_span: Span, - error_span: Span, - args_span: Span, - call_name: &str, c_variadic: bool, err_code: &str, fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &hir::Expr<'tcx>, ) { + // Next, let's construct the error + let (error_span, full_call_span, ctor_of) = match &call_expr.kind { + hir::ExprKind::Call( + hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, + _, + ) => { + if let Res::Def(DefKind::Ctor(of, _), _) = + self.typeck_results.borrow().qpath_res(qpath, *hir_id) + { + (call_span, *span, Some(of)) + } else { + (call_span, *span, None) + } + } + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), + hir::ExprKind::MethodCall(path_segment, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) + } else { + ident_span + }; + ( + *span, ident_span, None, // methods are never ctors + ) + } + k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), + }; + let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); + let call_name = match ctor_of { + Some(CtorOf::Struct) => "struct", + Some(CtorOf::Variant) => "enum variant", + None => "function", + }; + // Don't print if it has error types or is just plain `_` fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) @@ -483,6 +481,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(); let tcx = self.tcx; + // Get the argument span in the context of the call span so that + // suggestions and labels are (more) correct when an arg is a + // macro invocation. + let normalize_span = |span: Span| -> Span { + let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(error_span) { span } else { normalized_span } + }; + // Precompute the provided types and spans, since that's all we typically need for below let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args .iter() @@ -492,9 +501,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .expr_ty_adjusted_opt(*expr) .unwrap_or_else(|| tcx.ty_error()); - (self.resolve_vars_if_possible(ty), expr.span) + (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) }) .collect(); + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, callee, _) => { + if let Some((DefKind::AssocFn, def_id)) = + self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.fn_has_self_parameter + { + Some(&callee[0]) + } else { + None + } + } + _ => None, + }; + let callee_ty = callee_expr + .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); // A "softer" version of the `demand_compatible`, which checks types without persisting them, // and treats error types differently @@ -558,6 +584,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If so, we might have just forgotten to wrap some args in a tuple. if let Some(ty::Tuple(tys)) = formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() { // Wrap up the N provided arguments starting at this position in a tuple. @@ -583,11 +611,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Take some care with spans, so we don't suggest wrapping a macro's // innards in parenthesis, for example. if satisfied - && let Some(lo) = - provided_args[mismatch_idx.into()].span.find_ancestor_inside(error_span) - && let Some(hi) = provided_args[(mismatch_idx + tys.len() - 1).into()] - .span - .find_ancestor_inside(error_span) + && let Some((_, lo)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx)) + && let Some((_, hi)) = + provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1)) { let mut err; if tys.len() == 1 { @@ -595,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so don't do anything special here. err = self.report_and_explain_type_error( TypeTrace::types( - &self.misc(lo), + &self.misc(*lo), true, formal_and_expected_inputs[mismatch_idx.into()].1, provided_arg_tys[mismatch_idx.into()].0, @@ -618,7 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "argument" ), potentially_plural_count(provided_args.len(), "argument"), - if provided_args.len() == 1 { "was" } else { "were" } + pluralize!("was", provided_args.len()) ), DiagnosticId::Error(err_code.to_owned()), ); @@ -631,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); }; - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); err.emit(); return; } @@ -721,7 +748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); err.emit(); return; } @@ -743,7 +770,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if c_variadic { "at least " } else { "" }, potentially_plural_count(formal_and_expected_inputs.len(), "argument"), potentially_plural_count(provided_args.len(), "argument"), - if provided_args.len() == 1 { "was" } else { "were" } + pluralize!("was", provided_args.len()) ), DiagnosticId::Error(err_code.to_owned()), ) @@ -1003,7 +1030,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); + self.label_fn_like(&mut err, fn_def_id, callee_ty); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1035,7 +1062,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestion_text = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = - source_map.span_to_snippet(provided_span.source_callsite()) + source_map.span_to_snippet(provided_span) { arg_text } else { @@ -1043,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; if expected_ty.is_unit() { "()".to_string() - } else if expected_ty.is_suggestable(tcx) { + } else if expected_ty.is_suggestable(tcx, false) { format!("/* {} */", expected_ty) } else { "/* value */".to_string() @@ -1215,6 +1242,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); + + if let Some(blk) = decl.els { + let previous_diverges = self.diverges.get(); + let else_ty = self.check_block_with_expected(blk, NoExpectation); + let cause = self.cause(blk.span, ObligationCauseCode::LetElse); + if let Some(mut err) = + self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + { + err.emit(); + } + self.diverges.set(previous_diverges); + } } /// Type check a `let` statement. @@ -1236,8 +1275,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_has_errors = self.has_errors.replace(false); match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); + hir::StmtKind::Local(l) => { + self.check_decl_local(l); } // Ignore for now. hir::StmtKind::Item(_) => {} @@ -1381,7 +1420,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self.misc(sp), &mut |err| { if let Some(expected_ty) = expected.only_has_type(self) { - self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + if !self.consider_removing_semicolon(blk, expected_ty, err) { + self.consider_returning_binding(blk, expected_ty, err); + } if expected_ty == self.tcx.types.bool { // If this is caused by a missing `let` in a `while let`, // silence this redundant error, as we already emit E0070. @@ -1449,42 +1490,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - fn consider_hint_about_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diagnostic, - ) { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - err.span_suggestion_short( - span_semi, - "remove this semicolon", - "", - Applicability::MachineApplicable, - ); - } - } - } - fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); match node { @@ -1515,21 +1520,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ``` fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { let check_in_progress = |elem: &hir::Expr<'_>| { - self.in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(elem.hir_id)) - .and_then(|ty| { - if ty.is_never() { - None - } else { - Some(match elem.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.map_or(block.span, |e| e.span) - } - _ => elem.span, - }) - } - }) + self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( + |_| match elem.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), + _ => elem.span, + }, + ) }; if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { @@ -1762,19 +1759,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .flat_map(|a| a.args.iter()) { if let hir::GenericArg::Type(hir_ty) = &arg { - if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = - &hir_ty.kind - { - // Avoid ICE with associated types. As this is best - // effort only, it's ok to ignore the case. It - // would trigger in `is_send::<T::AssocType>();` - // from `typeck-default-trait-impl-assoc-type.rs`. - } else { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, hir_ty); - let ty = self.resolve_vars_if_possible(ty); - if ty == predicate.self_ty() { - error.obligation.cause.span = hir_ty.span; - } + let ty = self.resolve_vars_if_possible( + self.typeck_results.borrow().node_type(hir_ty.hir_id), + ); + if ty == predicate.self_ty() { + error.obligation.cause.span = hir_ty.span; } } } @@ -1783,47 +1772,126 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } -} -fn label_fn_like<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, - def_id: Option<DefId>, -) { - let Some(def_id) = def_id else { - return; - }; - - if let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| tcx.hir().body(id).params); - - for param in params { - spans.push_span_label(param.span, ""); + fn label_fn_like( + &self, + err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, + callable_def_id: Option<DefId>, + callee_ty: Option<Ty<'tcx>>, + ) { + let Some(mut def_id) = callable_def_id else { + return; + }; + + if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // Possibly points at either impl or trait item, so try to get it + // to point to trait item, then get the parent. + // This parent might be an impl in the case of an inherent function, + // but the next check will fail. + && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) + && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) + // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" + && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) + && let Some(callee_ty) = callee_ty + { + let callee_ty = callee_ty.peel_refs(); + match *callee_ty.kind() { + ty::Param(param) => { + let param = + self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); + if param.kind.is_synthetic() { + // if it's `impl Fn() -> ..` then just fall down to the def-id based logic + def_id = param.def_id; + } else { + // Otherwise, find the predicate that makes this generic callable, + // and point at that. + let instantiated = self + .tcx + .explicit_predicates_of(self.body_id.owner) + .instantiate_identity(self.tcx); + // FIXME(compiler-errors): This could be problematic if something has two + // fn-like predicates with different args, but callable types really never + // do that, so it's OK. + for (predicate, span) in + std::iter::zip(instantiated.predicates, instantiated.spans) + { + if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() + && pred.self_ty().peel_refs() == callee_ty + && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() + { + err.span_note(span, "callable defined here"); + return; + } + } + } + } + ty::Opaque(new_def_id, _) + | ty::Closure(new_def_id, _) + | ty::FnDef(new_def_id, _) => { + def_id = new_def_id; + } + _ => { + // Look for a user-provided impl of a `Fn` trait, and point to it. + let new_def_id = self.probe(|_| { + let trait_ref = ty::TraitRef::new( + call_kind.to_def_id(self.tcx), + self.tcx.mk_substs([ + ty::GenericArg::from(callee_ty), + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: rustc_span::DUMMY_SP, + }) + .into(), + ].into_iter()), + ); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + self.param_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }), + ); + match SelectionContext::new(&self).select(&obligation) { + Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { + Some(impl_source.impl_def_id) + } + _ => None + } + }); + if let Some(new_def_id) = new_def_id { + def_id = new_def_id; + } else { + return; + } + } + } } - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } else { - match tcx.hir().get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure { fn_decl_span, .. }, - .. - })) => { - let spans: MultiSpan = (*fn_decl_span).into(); + if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() { + let mut spans: MultiSpan = def_span.into(); - // Note: We don't point to param spans here because they overlap - // with the closure span itself + let params = self + .tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .flat_map(|id| self.tcx.hir().body(id).params); - err.span_note(spans, "closure defined here"); + for param in params { + spans.push_span_label(param.span, ""); } - _ => {} + + let def_kind = self.tcx.def_kind(def_id); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else { + let def_kind = self.tcx.def_kind(def_id); + err.span_note( + self.tcx.def_span(def_id), + &format!("{} defined here", def_kind.descr(def_id)), + ); } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 863a981134f..097fff6418e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -11,14 +11,12 @@ use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_infer::infer::{self, TyCtxtInferExt}; -use rustc_infer::traits; +use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; - -use std::iter; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { @@ -183,59 +181,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if let (ty::FnDef(def_id, ..), true) = (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) { - if def_id.is_local() { - err.span_label(self.tcx.def_span(def_id), &format!("{} defined here", found)); + if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { + err.span_label(sp, format!("{found} defined here")); } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let is_struct_pat_shorthand_field = - self.maybe_get_struct_pattern_shorthand_field(expr).is_some(); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { - if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) - .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.name); - if receiver.ends_with(&method_call) { - None // do not suggest code that is already there (#53348) - } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let mut sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - vec![( - expr.span, - format!("{}{}", &receiver[..max_len], method_call), - )] - } else { - if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!("){}", method_call)), - ] - } else { - vec![(expr.span.shrink_to_hi(), method_call)] - } - }; - if is_struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", receiver)), - ); - } - Some(sugg) - } - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } + let mut suggestions = methods.iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) + } + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. + { + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] + } else { + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] + }; + let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert( + 0, + (expr.span.shrink_to_lo(), format!("{}: ", name)), + ); + } + Some(sugg) + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); } } else if let ty::Adt(found_adt, found_substs) = found.kind() && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) @@ -507,7 +506,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that // haven't set a return type at all (and aren't `fn main()` or an impl). - match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) { + match ( + &fn_decl.output, + found.is_suggestable(self.tcx, false), + can_suggest, + expected.is_unit(), + ) { (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found }); true @@ -865,4 +869,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + + /// A common error is to add an extra semicolon: + /// + /// ```compile_fail,E0308 + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + pub(crate) fn consider_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let StatementAsExpression::NeedsBoxing = boxed { + err.span_suggestion_verbose( + span_semi, + "consider removing this semicolon and boxing the expression", + "", + Applicability::HasPlaceholders, + ); + } else { + err.span_suggestion_short( + span_semi, + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + } + true + } else { + false + } + } } diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 576dc6f127c..8f34a970f6f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -16,19 +16,20 @@ pub(super) struct Declaration<'a> { pub ty: Option<&'a hir::Ty<'a>>, pub span: Span, pub init: Option<&'a hir::Expr<'a>>, + pub els: Option<&'a hir::Block<'a>>, } impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, .. } = *local; - Declaration { hir_id, pat, ty, span, init } + let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; + Declaration { hir_id, pat, ty, span, init, els } } } impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { fn from(let_expr: &'a hir::Let<'a>) -> Self { let hir::Let { hir_id, pat, ty, span, init } = *let_expr; - Declaration { hir_id, pat, ty, span, init: Some(init) } + Declaration { hir_id, pat, ty, span, init: Some(init), els: None } } } @@ -101,7 +102,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.declare(local.into()); - intravisit::walk_local(self, local); + intravisit::walk_local(self, local) } fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) { diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 6ee989070b4..d4f8001493c 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -14,7 +14,7 @@ use rustc_hir::hir_id::HirIdSet; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt}; +use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::sym; use rustc_span::Span; use tracing::debug; @@ -69,7 +69,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { yield_data.expr_and_pat_count, self.expr_count, source_span ); - if self.fcx.sess().opts.debugging_opts.drop_tracking + if self.fcx.sess().opts.unstable_opts.drop_tracking && self .drop_ranges .is_dropped_at(hir_id, yield_data.expr_and_pat_count) @@ -376,6 +376,17 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); + let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); + let may_need_drop = |ty: Ty<'tcx>| { + // Avoid ICEs in needs_drop. + let ty = self.fcx.resolve_vars_if_possible(ty); + let ty = self.fcx.tcx.erase_regions(ty); + if ty.needs_infer() { + return true; + } + ty.needs_drop(self.fcx.tcx, self.fcx.param_env) + }; + // Typically, the value produced by an expression is consumed by its parent in some way, // so we only have to check if the parent contains a yield (note that the parent may, for // example, store the value into a local variable, but then we already consider local @@ -384,7 +395,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // However, in the case of temporary values, we are going to store the value into a // temporary on the stack that is live for the current temporary scope and then return a // reference to it. That value may be live across the entire temporary scope. - let scope = if self.drop_ranges.is_borrowed_temporary(expr) { + // + // There's another subtlety: if the type has an observable drop, it must be dropped after + // the yield, even if it's not borrowed or referenced after the yield. Ideally this would + // *only* happen for types with observable drop, not all types which wrap them, but that + // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in + // src/test/ui/generator/drop-tracking-parent-expression.rs. + let scope = if self.drop_ranges.is_borrowed_temporary(expr) + || ty.map_or(true, |ty| { + let needs_drop = may_need_drop(ty); + debug!(?needs_drop, ?ty); + needs_drop + }) { self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) } else { debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id)); @@ -398,7 +420,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // If there are adjustments, then record the final type -- // this is the actual value that is being produced. - if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) { + if let Some(adjusted_ty) = ty { self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs index 887c791af76..518cd734236 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -17,8 +17,7 @@ use self::record_consumed_borrow::find_consumed_and_borrowed; use crate::check::FnCtxt; use hir::def_id::DefId; use hir::{Body, HirId, HirIdMap, Node}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; @@ -38,7 +37,7 @@ pub fn compute_drop_ranges<'a, 'tcx>( def_id: DefId, body: &'tcx Body<'tcx>, ) -> DropRanges { - if fcx.sess().opts.debugging_opts.drop_tracking { + if fcx.sess().opts.unstable_opts.drop_tracking { let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); let typeck_results = &fcx.typeck_results.borrow(); diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index 111d534abf8..a2c23db162b 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -6,7 +6,7 @@ use hir::{ intravisit::{self, Visitor}, Body, Expr, ExprKind, Guard, HirId, LoopIdError, }; -use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_index::vec::IndexVec; use rustc_middle::{ @@ -33,7 +33,7 @@ pub(super) fn build_control_flow_graph<'tcx>( intravisit::walk_body(&mut drop_range_visitor, body); drop_range_visitor.drop_ranges.process_deferred_edges(); - if let Some(filename) = &tcx.sess.opts.debugging_opts.dump_drop_tracking_cfg { + if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs index 67cc46f21f0..ded0888c33e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -4,7 +4,7 @@ use crate::{ expr_use_visitor::{self, ExprUseVisitor}, }; use hir::{def_id::DefId, Body, HirId, HirIdMap}; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::ty::{ParamEnv, TyCtxt}; @@ -72,9 +72,8 @@ impl<'tcx> ExprUseDelegate<'tcx> { } fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) { - if !self.places.consumed.contains_key(&consumer) { - self.places.consumed.insert(consumer, <_>::default()); - } + self.places.consumed.entry(consumer).or_insert_with(|| <_>::default()); + debug!(?consumer, ?target, "mark_consumed"); self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); } diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 2ce258cf69c..a499179b95f 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -1,5 +1,4 @@ use super::callee::DeferredCallResolution; -use super::MaybeInProgressTables; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -29,7 +28,7 @@ use std::ops::Deref; pub struct Inherited<'a, 'tcx> { pub(super) infcx: InferCtxt<'a, 'tcx>, - pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>, + pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>, pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, @@ -86,7 +85,10 @@ impl<'tcx> Inherited<'_, 'tcx> { let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), + infcx: tcx + .infer_ctxt() + .ignoring_regions() + .with_fresh_in_progress_typeck_results(hir_owner), def_id, } } @@ -103,15 +105,15 @@ impl<'tcx> InheritedBuilder<'tcx> { } impl<'a, 'tcx> Inherited<'a, 'tcx> { - pub(super) fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { let tcx = infcx.tcx; let item_id = tcx.hir().local_def_id_to_hir_id(def_id); let body_id = tcx.hir().maybe_body_owned_by(item_id); + let typeck_results = + infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results"); Inherited { - typeck_results: MaybeInProgressTables { - maybe_typeck_results: infcx.in_progress_typeck_results, - }, + typeck_results, infcx, fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)), locals: RefCell::new(Default::default()), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 7fe710cf8f4..3f2a0da8d65 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -400,6 +400,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)), + sym::vtable_size | sym::vtable_align => { + (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize) + } + other => { tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs index a5add1e9a8a..df94abbafb1 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_typeck/src/check/intrinsicck.rs @@ -1,5 +1,5 @@ use rustc_ast::InlineAsmTemplatePiece; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; @@ -111,6 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } false } +} + +pub struct InlineAsmCtxt<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + fcx: Option<&'a FnCtxt<'a, 'tcx>>, +} + +impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { + pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { + InlineAsmCtxt { tcx, fcx: None } + } + + pub fn new_in_fn(fcx: &'a FnCtxt<'a, 'tcx>) -> Self { + InlineAsmCtxt { tcx: fcx.tcx, fcx: Some(fcx) } + } fn check_asm_operand_type( &self, @@ -122,9 +137,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>, target_features: &FxHashSet<Symbol>, ) -> Option<InlineAsmType> { + let fcx = self.fcx.unwrap_or_else(|| span_bug!(expr.span, "asm operand for global asm")); // Check the type against the allowed types for inline asm. - let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); - let ty = self.resolve_vars_if_possible(ty); + let ty = fcx.typeck_results.borrow().expr_ty_adjusted(expr); + let ty = fcx.resolve_vars_if_possible(ty); let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -134,7 +150,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Expect types to be fully resolved, no const or type variables. if ty.has_infer_types_or_consts() { - assert!(self.is_tainted_by_errors()); + assert!(fcx.is_tainted_by_errors()); return None; } @@ -151,7 +167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::FnPtr(_) => Some(asm_ty_isize), - ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { + ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if fcx.is_thin_ptr_ty(ty) => { Some(asm_ty_isize) } ty::Adt(adt, substs) if adt.repr().simd() => { @@ -203,7 +219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) { + if !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, ty, DUMMY_SP) { let msg = "arguments for inline assembly must be copyable"; let mut err = self.tcx.sess.struct_span_err(expr.span, msg); err.note(&format!("`{ty}` does not implement the Copy trait")); @@ -224,8 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = "incompatible types for asm inout argument"; let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); - let in_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(in_expr); - let in_expr_ty = self.resolve_vars_if_possible(in_expr_ty); + let in_expr_ty = fcx.typeck_results.borrow().expr_ty_adjusted(in_expr); + let in_expr_ty = fcx.resolve_vars_if_possible(in_expr_ty); err.span_label(in_expr.span, &format!("type `{in_expr_ty}`")); err.span_label(expr.span, &format!("type `{ty}`")); err.note( diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index c0b3a23fde4..c09f63f1e8f 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -10,7 +10,7 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; -use crate::check::FnCtxt; +use crate::check::{Expectation, FnCtxt}; use crate::ObligationCause; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -20,8 +20,10 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; -use rustc_middle::ty::{DefIdTree, GenericParamDefKind}; +use rustc_middle::ty::{ + self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term, + ToPredicate, Ty, TypeVisitable, +}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; @@ -318,6 +320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_type: Option<Ty<'tcx>>, opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) { // Construct a trait-reference `self_ty : Trait<input_tys>` @@ -339,6 +342,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); + let opt_output_ty = + expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); + let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind( + self.tcx, + Ident::from_str("Output"), + AssocKind::Type, + trait_def_id, + ); + let output_pred = + opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| { + ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate { + projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id }, + term: Term::Ty(output_ty), + })) + .to_predicate(self.tcx) + }); + ( traits::Obligation::new( traits::ObligationCause::new( @@ -348,6 +368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred, }, ), self.param_env, @@ -397,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_type: Option<Ty<'tcx>>, opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { let (obligation, substs) = self.obligation_for_op_method( span, @@ -404,6 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, opt_input_type, opt_input_expr, + expected, ); self.construct_obligation_for_trait( span, @@ -505,6 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred: None, }, ) } else { diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index e9b91414a07..8f5f3657fc9 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -21,9 +21,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{ - self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable, -}; +use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ @@ -345,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>, { let mut orig_values = OriginalQueryValues::default(); - let param_env_and_self_ty = self.infcx.canonicalize_query( + let param_env_and_self_ty = self.canonicalize_query( ParamEnvAnd { param_env: self.param_env, value: self_ty }, &mut orig_values, ); @@ -353,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let steps = if mode == Mode::MethodCall { self.tcx.method_autoderef_steps(param_env_and_self_ty) } else { - self.infcx.probe(|_| { + self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This // is a bit inefficient, but I don't think that writing @@ -713,7 +711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs); + let impl_ty = impl_ty.subst(self.tcx, impl_substs); debug!("impl_ty: {:?}", impl_ty); @@ -1067,7 +1065,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let pick = self.pick_all_method(Some(&mut unstable_candidates)); // In this case unstable picking is done by `pick_method`. - if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { return pick; } @@ -1271,7 +1269,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty: Ty<'tcx>, mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { - if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { return self.pick_method_with_unstable(self_ty); } @@ -1811,9 +1809,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.erase_late_bound_regions(xform_fn_sig) } - /// Gets the type of an impl and generate substitutions with placeholders. - fn impl_ty_and_substs(&self, impl_def_id: DefId) -> (Ty<'tcx>, SubstsRef<'tcx>) { - (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) + /// Gets the type of an impl and generate substitutions with inference vars. + fn impl_ty_and_substs( + &self, + impl_def_id: DefId, + ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) { + (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7bf167426f7..56fcd9e0a89 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -864,27 +865,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .join("\n"); let actual_prefix = actual.prefix_string(self.tcx); info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let (primary_message, label) = if unimplemented_traits.len() == 1 - && unimplemented_traits_only - { - unimplemented_traits - .into_iter() - .next() - .map(|(_, (trait_ref, obligation))| { - if trait_ref.self_ty().references_error() - || actual.references_error() - { - // Avoid crashing. - return (None, None); - } - let OnUnimplementedNote { message, label, .. } = - self.infcx.on_unimplemented_note(trait_ref, &obligation); - (message, label) - }) - .unwrap_or((None, None)) - } else { - (None, None) - }; + let (primary_message, label) = + if unimplemented_traits.len() == 1 && unimplemented_traits_only { + unimplemented_traits + .into_iter() + .next() + .map(|(_, (trait_ref, obligation))| { + if trait_ref.self_ty().references_error() + || actual.references_error() + { + // Avoid crashing. + return (None, None); + } + let OnUnimplementedNote { message, label, .. } = + self.on_unimplemented_note(trait_ref, &obligation); + (message, label) + }) + .unwrap_or((None, None)) + } else { + (None, None) + }; let primary_message = primary_message.unwrap_or_else(|| format!( "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" )); @@ -1122,7 +1122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { add a `use` for {one_of_them}:", an = if candidates.len() == 1 { "an" } else { "" }, s = pluralize!(candidates.len()), - were = if candidates.len() == 1 { "was" } else { "were" }, + were = pluralize!("was", candidates.len()), one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, ); self.suggest_use_candidates(&mut err, help, candidates); @@ -1548,7 +1548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option<ObligationCause<'tcx>>, )], ) { - let mut derives = Vec::<(String, Span, String)>::new(); + let mut derives = Vec::<(String, Span, Symbol)>::new(); let mut traits = Vec::<Span>::new(); for (pred, _, _) in unsatisfied_predicates { let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; @@ -1581,12 +1581,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { derives.push(( self_name.clone(), self_span, - parent_diagnostic_name.to_string(), + parent_diagnostic_name, )); } } } - derives.push((self_name, self_span, diagnostic_name.to_string())); + derives.push((self_name, self_span, diagnostic_name)); } else { traits.push(self.tcx.def_span(trait_pred.def_id())); } @@ -1609,7 +1609,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } } - derives_grouped.push((self_name, self_span, trait_name)); + derives_grouped.push((self_name, self_span, trait_name.to_string())); } let len = traits.len(); @@ -1647,7 +1647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call: &hir::Expr<'_>, span: Span, ) { - let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), _ => return, }; diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index dee58791cec..17c2e4868aa 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -25,7 +25,7 @@ can be broken down into several distinct phases: - regionck: after main is complete, the regionck pass goes over all types looking for regions and making sure that they did not escape - into places they are not in scope. This may also influence the + into places where they are not in scope. This may also influence the final assignments of the various region variables if there is some flexibility. @@ -87,10 +87,10 @@ mod op; mod pat; mod place_op; mod region; -mod regionck; +pub mod regionck; pub mod rvalue_scopes; mod upvar; -mod wfcheck; +pub mod wfcheck; pub mod writeback; use check::{check_abi, check_fn, check_mod_item_types}; @@ -128,8 +128,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; - -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::RefCell; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; @@ -534,7 +533,7 @@ fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { } } -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) { +fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { // Only restricted on wasm target for now if !tcx.sess.target.is_like_wasm { return; @@ -560,7 +559,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ extra levels of indirection such as references"; - tcx.sess.span_err(span, msg); + tcx.sess.span_err(tcx.def_span(id), msg); } } @@ -621,9 +620,8 @@ fn missing_items_err( // adding the associated item at the end of its body. let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); // Obtain the level of indentation ending in `sugg_sp`. - let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0); - // Make the whitespace that will make the suggestion have the right indentation. - let padding: String = " ".repeat(indentation); + let padding = + tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); for trait_item in missing_items { let snippet = suggestion_signature(trait_item, tcx); @@ -864,17 +862,14 @@ fn bad_non_zero_sized_fields<'tcx>( err.emit(); } -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { +fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { struct_span_err!( tcx.sess, span, E0533, - "expected unit struct, unit variant or constant, found {}{}", + "expected unit struct, unit variant or constant, found {} `{}`", res.descr(), - tcx.sess - .source_map() - .span_to_snippet(span) - .map_or_else(|_| String::new(), |s| format!(" `{s}`",)), + rustc_hir_pretty::qpath_to_string(qpath), ) .emit(); } @@ -904,32 +899,6 @@ enum TupleArgumentsFlag { TupleArguments, } -/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. -#[derive(Copy, Clone)] -struct MaybeInProgressTables<'a, 'tcx> { - maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, -} - -impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { - fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" - ), - } - } - - fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow_mut(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" - ), - } - } -} - fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); } diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 0887c27ea36..920b3e68808 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -2,10 +2,12 @@ use super::method::MethodCallee; use super::{has_expected_num_generic_args, FnCtxt}; +use crate::check::Expectation; use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; @@ -30,9 +32,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::BinOp, lhs: &'tcx hir::Expr<'tcx>, rhs: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes); + self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { @@ -50,6 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs), Op::Binary(op, IsAssign::Yes), + expected, ) .is_ok() { @@ -70,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op: hir::BinOp, lhs_expr: &'tcx hir::Expr<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -94,8 +99,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::No); + let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( + expr, + lhs_expr, + rhs_expr, + op, + IsAssign::No, + expected, + ); // Supply type inference hints if relevant. Probably these // hints should be enforced during select as part of the @@ -176,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, + expected: Expectation<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { debug!( "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", @@ -222,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty_var), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ); // see `NB` above @@ -282,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), Err(errors) => { let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, _use_output) = match is_assign { + let (mut err, missing_trait, use_output) = match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( self.tcx.sess, @@ -395,8 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, ), }; - let mut err = - struct_span_err!(self.tcx.sess, op.span, E0369, "{}", message.as_str()); + let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { self.add_type_neq_err_label( &mut err, @@ -406,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_expr, op, is_assign, + expected, ); self.add_type_neq_err_label( &mut err, @@ -415,6 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr, op, is_assign, + expected, ); } self.note_unmet_impls_on_type(&mut err, errors); @@ -429,6 +443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ) .is_ok() { @@ -460,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_deref_binop(lhs_deref_ty); } else if is_assign == IsAssign::No && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() { - if self.infcx.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { + if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { suggest_deref_binop(*lhs_deref_ty); } } @@ -490,19 +505,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rhs_ty), Some(rhs_expr), Op::Binary(op, is_assign), + expected, ) .unwrap_err(); - let predicates = errors - .into_iter() - .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred()) - .collect::<Vec<_>>(); - if !predicates.is_empty() { - for pred in predicates { - self.infcx.suggest_restricting_param_bound( - &mut err, - pred, - self.body_id, - ); + if !errors.is_empty() { + for error in errors { + if let Some(trait_pred) = + error.obligation.predicate.to_opt_poly_trait_pred() + { + let proj_pred = match error.obligation.cause.code() { + ObligationCauseCode::BinOp { + output_pred: Some(output_pred), + .. + } if use_output => { + output_pred.to_opt_poly_projection_pred() + } + _ => None, + }; + + self.suggest_restricting_param_bound( + &mut err, + trait_pred, + proj_pred, + self.body_id, + ); + } } } else if *ty != lhs_ty { // When we know that a missing bound is responsible, we don't show @@ -532,6 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, is_assign: IsAssign, + expected: Expectation<'tcx>, ) -> bool /* did we suggest to call a function because of missing parentheses? */ { err.span_label(span, ty.to_string()); if let FnDef(def_id, _) = *ty.kind() { @@ -561,6 +589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(other_ty), Some(other_expr), Op::Binary(op, is_assign), + expected, ) .is_ok() { @@ -601,18 +630,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; let to_owned_msg = "create an owned `String` from a string reference"; - let string_type = self.tcx.get_diagnostic_item(sym::String); - let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() { - Some(ty_def) => Some(ty_def.did()) == string_type, - None => false, + let is_std_string = |ty: Ty<'tcx>| { + ty.ty_adt_def() + .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) }; match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (*l_ty.kind() == Str || is_std_string(l_ty)) && ( - *r_ty.kind() == Str || is_std_string(r_ty) || - &format!("{:?}", rhs_ty) == "&&str" - ) => + if (*l_ty.kind() == Str || is_std_string(l_ty)) + && (*r_ty.kind() == Str + || is_std_string(r_ty) + || matches!( + r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str + )) => { if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); @@ -677,9 +707,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex: &'tcx hir::Expr<'tcx>, operand_ty: Ty<'tcx>, op: hir::UnOp, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) { + match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { Ok(method) => { self.write_method_call(ex.hir_id, method); method.sig.output() @@ -709,9 +740,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.predicate.to_opt_poly_trait_pred() }); for pred in predicates { - self.infcx.suggest_restricting_param_bound( + self.suggest_restricting_param_bound( &mut err, pred, + None, self.body_id, ); } @@ -772,6 +804,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { other_ty: Option<Ty<'tcx>>, other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, op: Op, + expected: Expectation<'tcx>, ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { let lang = self.tcx.lang_items(); @@ -856,7 +889,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opname = Ident::with_dummy_span(opname); let method = trait_did.and_then(|trait_did| { - self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr) + self.lookup_op_method_in_trait( + span, + opname, + trait_did, + lhs_ty, + other_ty, + other_ty_expr, + expected, + ) }); match (method, trait_did) { @@ -867,8 +908,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (None, None) => Err(vec![]), (None, Some(trait_did)) => { - let (obligation, _) = - self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr); + let (obligation, _) = self.obligation_for_op_method( + span, + trait_did, + lhs_ty, + other_ty, + other_ty_expr, + expected, + ); let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); fulfill.register_predicate_obligation(self, obligation); Err(fulfill.select_where_possible(&self.infcx)) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index fbfbfba5c2a..c7318cd6e53 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -183,7 +183,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) } - PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti), + PatKind::Path(ref qpath) => { + self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) + } PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti) } @@ -800,6 +802,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path<'b>( &self, pat: &Pat<'_>, + qpath: &hir::QPath<'_>, path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]), expected: Ty<'tcx>, ti: TopInfo<'tcx>, @@ -814,7 +817,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return tcx.ty_error(); } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, pat.span); + report_unexpected_variant_res(tcx, res, qpath, pat.span); return tcx.ty_error(); } Res::SelfCtor(..) diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs index 9f1368a3e07..0c33a243e10 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_typeck/src/check/region.rs @@ -335,7 +335,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h match expr.kind { // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. - hir::ExprKind::Closure { body, .. } + hir::ExprKind::Closure(&hir::Closure { body, .. }) | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); @@ -460,6 +460,7 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, + els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -537,13 +538,18 @@ fn resolve_local<'tcx>( } } - // Make sure we visit the initializer first, so expr_and_pat_count remains correct + // Make sure we visit the initializer first, so expr_and_pat_count remains correct. + // The correct order, as shared between generator_interior, drop_ranges and intravisitor, + // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. if let Some(expr) = init { visitor.visit_expr(expr); } if let Some(pat) = pat { visitor.visit_pat(pat); } + if let Some(els) = els { + visitor.visit_block(els); + } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value)); + resolve_local(self, None, Some(&body.value), None); } if body.generator_kind.is_some() { @@ -791,7 +797,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init); + resolve_local(self, Some(&l.pat), l.init, l.els) } } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 1c3c5f999bc..d49a6138f7a 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -1,5 +1,5 @@ use crate::outlives::outlives_bounds::InferCtxtExt as _; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::InferCtxt; diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 74546ed9080..d72e215934a 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -49,8 +49,7 @@ use rustc_span::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_data_structures::stable_map::FxHashMap; -use rustc_data_structures::stable_set::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::vec::Idx; use rustc_target::abi::VariantIdx; @@ -142,7 +141,7 @@ struct InferBorrowKindVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { - hir::ExprKind::Closure { capture_clause, body: body_id, .. } => { + hir::ExprKind::Closure(&hir::Closure { capture_clause, body: body_id, .. }) => { let body = self.fcx.tcx.hir().body(body_id); self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); @@ -315,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads); - if self.tcx.sess.opts.debugging_opts.profile_closures { + if self.tcx.sess.opts.unstable_opts.profile_closures { self.typeck_results.borrow_mut().closure_size_eval.insert( closure_def_id, ClosureSizeProfileData { @@ -949,7 +948,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?; - let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); let ty = match closure_clause { hir::CaptureBy::Value => ty, // For move closure the capture kind should be by value @@ -1065,7 +1064,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_clause: hir::CaptureBy, var_hir_id: hir::HirId, ) -> Option<FxHashSet<UpvarMigrationInfo>> { - let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { debug!("does not have significant drop"); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 5621cf2e1a4..faab862cc3c 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,18 +1,16 @@ use crate::check::regionck::OutlivesEnvironmentExt; -use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; - use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; use rustc_infer::infer::outlives::obligations::TypeOutlives; -use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::Normalized; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -23,53 +21,95 @@ use rustc_middle::ty::{ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::autoderef::Autoderef; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc}; +use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::NoSolution; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, +}; use std::cell::LazyCell; use std::convert::TryInto; use std::iter; -use std::ops::ControlFlow; +use std::ops::{ControlFlow, Deref}; -/// Helper type of a temporary returned by `.for_item(...)`. -/// This is necessary because we can't write the following bound: -/// -/// ```ignore (illustrative) -/// F: for<'b, 'tcx> where 'tcx FnOnce(FnCtxt<'b, 'tcx>) -/// ``` -pub(super) struct CheckWfFcxBuilder<'tcx> { - inherited: super::InheritedBuilder<'tcx>, - id: hir::HirId, +pub(super) struct WfCheckingCtxt<'a, 'tcx> { + pub(super) ocx: ObligationCtxt<'a, 'tcx>, span: Span, + body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, } +impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> { + type Target = ObligationCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.ocx + } +} -impl<'tcx> CheckWfFcxBuilder<'tcx> { - pub(super) fn with_fcx<F>(&mut self, f: F) +impl<'tcx> WfCheckingCtxt<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.ocx.infcx.tcx + } + + fn normalize<T>(&self, span: Span, loc: Option<WellFormedLoc>, value: T) -> T where - F: for<'b> FnOnce(&FnCtxt<'b, 'tcx>) -> FxHashSet<Ty<'tcx>>, + T: TypeFoldable<'tcx>, { - let id = self.id; - let span = self.span; - let param_env = self.param_env; - self.inherited.enter(|inh| { - let fcx = FnCtxt::new(&inh, param_env, id); - if !inh.tcx.features().trivial_bounds { - // As predicates are cached rather than obligations, this - // needs to be called first so that they are checked with an - // empty `param_env`. - check_false_global_bounds(&fcx, span, id); - } - let wf_tys = f(&fcx); - fcx.select_all_obligations_or_error(); + self.ocx.normalize( + ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)), + self.param_env, + value, + ) + } - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&fcx.infcx, wf_tys, id); - fcx.infcx.check_region_obligations_and_report_errors(&outlives_environment); - }); + fn register_wf_obligation( + &self, + span: Span, + loc: Option<WellFormedLoc>, + arg: ty::GenericArg<'tcx>, + ) { + let cause = + traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)); + self.ocx.register_obligation(traits::Obligation::new( + cause, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), + )); } } +pub(super) fn enter_wf_checking_ctxt<'tcx, F>( + tcx: TyCtxt<'tcx>, + span: Span, + body_def_id: LocalDefId, + f: F, +) where + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>, +{ + let param_env = tcx.param_env(body_def_id); + let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id); + tcx.infer_ctxt().enter(|ref infcx| { + let ocx = ObligationCtxt::new(infcx); + let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + + if !tcx.features().trivial_bounds { + wfcx.check_false_global_bounds() + } + let wf_tys = f(&mut wfcx); + let errors = wfcx.select_all_or_error(); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); + return; + } + + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(infcx, wf_tys, body_id); + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); + }) +} + fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { let node = tcx.hir().expect_owner(def_id); match node { @@ -142,7 +182,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. match (tcx.impl_polarity(def_id), impl_.polarity) { (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, impl_.self_ty, &impl_.of_trait); + check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness); } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? @@ -174,17 +214,17 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { check_item_type(tcx, item.def_id, ty.span, false); } hir::ItemKind::Struct(ref struct_def, ref ast_generics) => { - check_type_defn(tcx, item, false, |fcx| vec![fcx.non_enum_variant(struct_def)]); + check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); check_variances_for_type_defn(tcx, item, ast_generics); } hir::ItemKind::Union(ref struct_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |fcx| vec![fcx.non_enum_variant(struct_def)]); + check_type_defn(tcx, item, true, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); check_variances_for_type_defn(tcx, item, ast_generics); } hir::ItemKind::Enum(ref enum_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |fcx| fcx.enum_variants(enum_def)); + check_type_defn(tcx, item, true, |wfcx| wfcx.enum_variants(enum_def)); check_variances_for_type_defn(tcx, item, ast_generics); } @@ -433,7 +473,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe unsatisfied_bounds.sort(); if !unsatisfied_bounds.is_empty() { - let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" }; + let plural = pluralize!(unsatisfied_bounds.len()); let mut err = tcx.sess.struct_span_err( gat_item_hir.span, &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), @@ -648,10 +688,7 @@ fn resolve_regions_with_wf_tys<'tcx>( id: hir::HirId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxHashSet<Ty<'tcx>>, - add_constraints: impl for<'a> FnOnce( - &'a InferCtxt<'a, 'tcx>, - &'a Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, - ), + add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each @@ -807,29 +844,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); if tcx.features().adt_const_params { - let err = match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - - if let Some(unsupported_type) = err { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ); - } - if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty, false) + traits::search_for_adt_const_param_violation(param.span, tcx, ty) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - match ty.peel_refs().kind() { + match non_structural_match_ty.kind() { ty::Param(_) => { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` @@ -861,6 +882,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") .emit(); } + ty::FnPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + .emit(); + } + ty::RawPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + .emit(); + } _ => { let mut diag = struct_span_err!( tcx.sess, @@ -868,10 +907,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { E0741, "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ the type of a const parameter", - non_structural_match_ty.ty, + non_structural_match_ty, ); - if ty == non_structural_match_ty.ty { + if ty == non_structural_match_ty { diag.span_label( hir_ty.span, format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), @@ -933,45 +972,45 @@ fn check_associated_item( span: Span, sig_if_method: Option<&hir::FnSig<'_>>, ) { - let code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id))); - for_id(tcx, item_id, span).with_fcx(|fcx| { - let item = fcx.tcx.associated_item(item_id); + let loc = Some(WellFormedLoc::Ty(item_id)); + enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { + let item = tcx.associated_item(item_id); let (mut implied_bounds, self_ty) = match item.container { - ty::TraitContainer(_) => (FxHashSet::default(), fcx.tcx.types.self_param), - ty::ImplContainer(def_id) => { - (fcx.impl_implied_bounds(def_id, span), fcx.tcx.type_of(def_id)) - } + ty::TraitContainer(_) => (FxHashSet::default(), tcx.types.self_param), + ty::ImplContainer(def_id) => ( + impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span), + tcx.type_of(def_id), + ), }; match item.kind { ty::AssocKind::Const => { - let ty = fcx.tcx.type_of(item.def_id); - let ty = fcx.normalize_associated_types_in_wf(span, ty, WellFormedLoc::Ty(item_id)); - fcx.register_wf_obligation(ty.into(), span, code.clone()); + let ty = tcx.type_of(item.def_id); + let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); + wfcx.register_wf_obligation(span, loc, ty.into()); } ty::AssocKind::Fn => { - let sig = fcx.tcx.fn_sig(item.def_id); + let sig = tcx.fn_sig(item.def_id); let hir_sig = sig_if_method.expect("bad signature for method"); check_fn_or_method( - fcx, - item.ident(fcx.tcx).span, + wfcx, + item.ident(tcx).span, sig, hir_sig.decl, item.def_id.expect_local(), &mut implied_bounds, ); - check_method_receiver(fcx, hir_sig, item, self_ty); + check_method_receiver(wfcx, hir_sig, item, self_ty); } ty::AssocKind::Type => { if let ty::AssocItemContainer::TraitContainer(_) = item.container { - check_associated_type_bounds(fcx, item, span) + check_associated_type_bounds(wfcx, item, span) } if item.defaultness.has_value() { - let ty = fcx.tcx.type_of(item.def_id); - let ty = - fcx.normalize_associated_types_in_wf(span, ty, WellFormedLoc::Ty(item_id)); - fcx.register_wf_obligation(ty.into(), span, code.clone()); + let ty = tcx.type_of(item.def_id); + let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); + wfcx.register_wf_obligation(span, loc, ty.into()); } } } @@ -980,19 +1019,6 @@ fn check_associated_item( }) } -pub(super) fn for_item<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'_>) -> CheckWfFcxBuilder<'tcx> { - for_id(tcx, item.def_id, item.span) -} - -fn for_id(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> CheckWfFcxBuilder<'_> { - CheckWfFcxBuilder { - inherited: Inherited::build(tcx, def_id), - id: hir::HirId::make_owner(def_id), - span, - param_env: tcx.param_env(def_id), - } -} - fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> { match kind { ItemKind::Struct(..) => Some(AdtKind::Struct), @@ -1009,19 +1035,19 @@ fn check_type_defn<'tcx, F>( all_sized: bool, mut lookup_fields: F, ) where - F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec<AdtVariant<'tcx>>, + F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, { - for_item(tcx, item).with_fcx(|fcx| { - let variants = lookup_fields(fcx); + enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + let variants = lookup_fields(wfcx); let packed = tcx.adt_def(item.def_id).repr().packed(); for variant in &variants { // All field types must be well-formed. for field in &variant.fields { - fcx.register_wf_obligation( - field.ty.into(), + wfcx.register_wf_obligation( field.span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))), + Some(WellFormedLoc::Ty(field.def_id)), + field.ty.into(), ) } @@ -1048,12 +1074,10 @@ fn check_type_defn<'tcx, F>( variant.fields[..variant.fields.len() - unsized_len].iter().enumerate() { let last = idx == variant.fields.len() - 1; - fcx.register_bound( - field.ty, - tcx.require_lang_item(LangItem::Sized, None), + wfcx.register_bound( traits::ObligationCause::new( field.span, - fcx.body_id, + wfcx.body_id, traits::FieldSized { adt_kind: match item_adt_kind(&item.kind) { Some(i) => i, @@ -1063,6 +1087,9 @@ fn check_type_defn<'tcx, F>( last, }, ), + wfcx.param_env, + field.ty, + tcx.require_lang_item(LangItem::Sized, None), ); } @@ -1072,12 +1099,12 @@ fn check_type_defn<'tcx, F>( let cause = traits::ObligationCause::new( tcx.def_span(discr_def_id), - fcx.body_id, + wfcx.body_id, traits::MiscObligation, ); - fcx.register_predicate(traits::Obligation::new( + wfcx.register_obligation(traits::Obligation::new( cause, - fcx.param_env, + wfcx.param_env, ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new( ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), discr_substs, @@ -1087,7 +1114,7 @@ fn check_type_defn<'tcx, F>( } } - check_where_clauses(fcx, item.span, item.def_id, None); + check_where_clauses(wfcx, item.span, item.def_id); // No implied bounds in a struct definition. FxHashSet::default() @@ -1113,9 +1140,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } } - // FIXME: this shouldn't use an `FnCtxt` at all. - for_item(tcx, item).with_fcx(|fcx| { - check_where_clauses(fcx, item.span, item.def_id, None); + enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + check_where_clauses(wfcx, item.span, item.def_id); FxHashSet::default() }); @@ -1130,27 +1156,22 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { /// /// Assuming the defaults are used, check that all predicates (bounds on the /// assoc type and where clauses on the trait) hold. -fn check_associated_type_bounds(fcx: &FnCtxt<'_, '_>, item: &ty::AssocItem, span: Span) { - let tcx = fcx.tcx; - - let bounds = tcx.explicit_item_bounds(item.def_id); +fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: &ty::AssocItem, span: Span) { + let bounds = wfcx.tcx().explicit_item_bounds(item.def_id); debug!("check_associated_type_bounds: bounds={:?}", bounds); let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { - let normalized_bound = fcx.normalize_associated_types_in(span, bound); + let normalized_bound = wfcx.normalize(span, None, bound); traits::wf::predicate_obligations( - fcx, - fcx.param_env, - fcx.body_id, + wfcx.infcx, + wfcx.param_env, + wfcx.body_id, normalized_bound, bound_span, ) }); - for obligation in wf_obligations { - debug!("next obligation cause: {:?}", obligation.cause); - fcx.register_predicate(obligation); - } + wfcx.register_obligations(wf_obligations); } fn check_item_fn( @@ -1160,10 +1181,10 @@ fn check_item_fn( span: Span, decl: &hir::FnDecl<'_>, ) { - for_id(tcx, def_id, span).with_fcx(|fcx| { + enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { let sig = tcx.fn_sig(def_id); let mut implied_bounds = FxHashSet::default(); - check_fn_or_method(fcx, ident.span, sig, decl, def_id, &mut implied_bounds); + check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds); implied_bounds }) } @@ -1171,28 +1192,25 @@ fn check_item_fn( fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_foreign_ty: bool) { debug!("check_item_type: {:?}", item_id); - for_id(tcx, item_id, ty_span).with_fcx(|fcx| { + enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| { let ty = tcx.type_of(item_id); - let item_ty = fcx.normalize_associated_types_in_wf(ty_span, ty, WellFormedLoc::Ty(item_id)); + let item_ty = wfcx.normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty); let mut forbid_unsized = true; if allow_foreign_ty { - let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env); + let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env); if let ty::Foreign(_) = tail.kind() { forbid_unsized = false; } } - fcx.register_wf_obligation( - item_ty.into(), - ty_span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(item_id))), - ); + wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into()); if forbid_unsized { - fcx.register_bound( + wfcx.register_bound( + traits::ObligationCause::new(ty_span, wfcx.body_id, traits::WellFormed(None)), + wfcx.param_env, item_ty, tcx.require_lang_item(LangItem::Sized, None), - traits::ObligationCause::new(ty_span, fcx.body_id, traits::WellFormed(None)), ); } @@ -1203,10 +1221,11 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo && !tcx.is_thread_local_static(item_id.to_def_id()); if should_check_for_sync { - fcx.register_bound( + wfcx.register_bound( + traits::ObligationCause::new(ty_span, wfcx.body_id, traits::SharedStatic), + wfcx.param_env, item_ty, tcx.require_lang_item(LangItem::Sync, Some(ty_span)), - traits::ObligationCause::new(ty_span, fcx.body_id, traits::SharedStatic), ); } @@ -1221,57 +1240,57 @@ fn check_impl<'tcx>( item: &'tcx hir::Item<'tcx>, ast_self_ty: &hir::Ty<'_>, ast_trait_ref: &Option<hir::TraitRef<'_>>, + constness: hir::Constness, ) { - for_item(tcx, item).with_fcx(|fcx| { + enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { match *ast_trait_ref { Some(ref ast_trait_ref) => { // `#[rustc_reservation_impl]` impls are not real impls and // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap(); - let trait_ref = - fcx.normalize_associated_types_in(ast_trait_ref.path.span, trait_ref); + let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); + let trait_pred = ty::TraitPredicate { + trait_ref, + constness: match constness { + hir::Constness::Const => ty::BoundConstness::ConstIfConst, + hir::Constness::NotConst => ty::BoundConstness::NotConst, + }, + polarity: ty::ImplPolarity::Positive, + }; let obligations = traits::wf::trait_obligations( - fcx, - fcx.param_env, - fcx.body_id, - &trait_ref, + wfcx.infcx, + wfcx.param_env, + wfcx.body_id, + &trait_pred, ast_trait_ref.path.span, item, ); debug!(?obligations); - for obligation in obligations { - fcx.register_predicate(obligation); - } + wfcx.register_obligations(obligations); } None => { let self_ty = tcx.type_of(item.def_id); - let self_ty = fcx.normalize_associated_types_in(item.span, self_ty); - fcx.register_wf_obligation( - self_ty.into(), + let self_ty = wfcx.normalize(item.span, None, self_ty); + wfcx.register_wf_obligation( ast_self_ty.span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty( - item.hir_id().expect_owner(), - ))), + Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + self_ty.into(), ); } } - check_where_clauses(fcx, item.span, item.def_id, None); + check_where_clauses(wfcx, item.span, item.def_id); - fcx.impl_implied_bounds(item.def_id.to_def_id(), item.span) + impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span) }); } /// Checks where-clauses and inline bounds that are declared on `def_id`. -#[instrument(skip(fcx), level = "debug")] -fn check_where_clauses<'tcx, 'fcx>( - fcx: &FnCtxt<'fcx, 'tcx>, - span: Span, - def_id: LocalDefId, - return_ty: Option<(Ty<'tcx>, Span)>, -) { - let tcx = fcx.tcx; +#[instrument(level = "debug", skip(wfcx))] +fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id: LocalDefId) { + let infcx = wfcx.infcx; + let tcx = wfcx.tcx(); let predicates = tcx.predicates_of(def_id); let generics = tcx.generics_of(def_id); @@ -1299,11 +1318,7 @@ fn check_where_clauses<'tcx, 'fcx>( // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { - fcx.register_wf_obligation( - ty.into(), - tcx.def_span(param.def_id), - ObligationCauseCode::MiscObligation, - ); + wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into()); } } } @@ -1315,10 +1330,10 @@ fn check_where_clauses<'tcx, 'fcx>( // we should eagerly error. let default_ct = tcx.const_param_default(param.def_id); if !default_ct.needs_subst() { - fcx.register_wf_obligation( - default_ct.into(), + wfcx.register_wf_obligation( tcx.def_span(param.def_id), - ObligationCauseCode::WellFormed(None), + None, + default_ct.into(), ); } } @@ -1429,48 +1444,41 @@ fn check_where_clauses<'tcx, 'fcx>( // Note the subtle difference from how we handle `predicates` // below: there, we are not trying to prove those predicates // to be *true* but merely *well-formed*. - let pred = fcx.normalize_associated_types_in(sp, pred); + let pred = wfcx.normalize(sp, None, pred); let cause = traits::ObligationCause::new( sp, - fcx.body_id, + wfcx.body_id, traits::ItemObligation(def_id.to_def_id()), ); - traits::Obligation::new(cause, fcx.param_env, pred) + traits::Obligation::new(cause, wfcx.param_env, pred) }); let predicates = predicates.instantiate_identity(tcx); - if let Some((return_ty, _)) = return_ty { - if return_ty.has_infer_types_or_consts() { - fcx.select_obligations_where_possible(false, |_| {}); - } - } - - let predicates = fcx.normalize_associated_types_in(span, predicates); + let predicates = wfcx.normalize(span, None, predicates); debug!(?predicates.predicates); assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| { - traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, sp) + traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp) }); - for obligation in wf_obligations.chain(default_obligations) { - debug!("next obligation cause: {:?}", obligation.cause); - fcx.register_predicate(obligation); - } + let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); + wfcx.register_obligations(obligations); } -#[tracing::instrument(level = "debug", skip(fcx, span, hir_decl))] -fn check_fn_or_method<'fcx, 'tcx>( - fcx: &FnCtxt<'fcx, 'tcx>, +#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))] +fn check_fn_or_method<'tcx>( + wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, sig: ty::PolyFnSig<'tcx>, hir_decl: &hir::FnDecl<'_>, def_id: LocalDefId, implied_bounds: &mut FxHashSet<Ty<'tcx>>, ) { - let sig = fcx.tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); + let tcx = wfcx.tcx(); + let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); // Normalize the input and output types one at a time, using a different // `WellFormedLoc` for each. We cannot call `normalize_associated_types` @@ -1478,70 +1486,61 @@ fn check_fn_or_method<'fcx, 'tcx>( // for each type, preventing the HIR wf check from generating // a nice error message. let ty::FnSig { mut inputs_and_output, c_variadic, unsafety, abi } = sig; - inputs_and_output = - fcx.tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| { - fcx.normalize_associated_types_in_wf( - span, - ty, - WellFormedLoc::Param { - function: def_id, - // Note that the `param_idx` of the output type is - // one greater than the index of the last input type. - param_idx: i.try_into().unwrap(), - }, - ) - })); + inputs_and_output = tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| { + wfcx.normalize( + span, + Some(WellFormedLoc::Param { + function: def_id, + // Note that the `param_idx` of the output type is + // one greater than the index of the last input type. + param_idx: i.try_into().unwrap(), + }), + ty, + ) + })); // Manually call `normalize_associated_types_in` on the other types // in `FnSig`. This ensures that if the types of these fields // ever change to include projections, we will start normalizing // them automatically. let sig = ty::FnSig { inputs_and_output, - c_variadic: fcx.normalize_associated_types_in(span, c_variadic), - unsafety: fcx.normalize_associated_types_in(span, unsafety), - abi: fcx.normalize_associated_types_in(span, abi), + c_variadic: wfcx.normalize(span, None, c_variadic), + unsafety: wfcx.normalize(span, None, unsafety), + abi: wfcx.normalize(span, None, abi), }; for (i, (&input_ty, ty)) in iter::zip(sig.inputs(), hir_decl.inputs).enumerate() { - fcx.register_wf_obligation( - input_ty.into(), + wfcx.register_wf_obligation( ty.span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Param { - function: def_id, - param_idx: i.try_into().unwrap(), - })), + Some(WellFormedLoc::Param { function: def_id, param_idx: i.try_into().unwrap() }), + input_ty.into(), ); } implied_bounds.extend(sig.inputs()); - fcx.register_wf_obligation( - sig.output().into(), - hir_decl.output.span(), - ObligationCauseCode::ReturnType, - ); + wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into()); // FIXME(#27579) return types should not be implied bounds implied_bounds.insert(sig.output()); debug!(?implied_bounds); - check_where_clauses(fcx, span, def_id, Some((sig.output(), hir_decl.output.span()))); + check_where_clauses(wfcx, span, def_id); } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \ `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \ of the previous types except `Self`)"; -#[tracing::instrument(level = "debug", skip(fcx))] -fn check_method_receiver<'fcx, 'tcx>( - fcx: &FnCtxt<'fcx, 'tcx>, +#[tracing::instrument(level = "debug", skip(wfcx))] +fn check_method_receiver<'tcx>( + wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_sig: &hir::FnSig<'_>, method: &ty::AssocItem, self_ty: Ty<'tcx>, ) { - // Check that the method has a valid receiver type, given the type `Self`. - debug!("check_method_receiver({:?}, self_ty={:?})", method, self_ty); + let tcx = wfcx.tcx(); if !method.fn_has_self_parameter { return; @@ -1549,28 +1548,28 @@ fn check_method_receiver<'fcx, 'tcx>( let span = fn_sig.decl.inputs[0].span; - let sig = fcx.tcx.fn_sig(method.def_id); - let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig); - let sig = fcx.normalize_associated_types_in(span, sig); + let sig = tcx.fn_sig(method.def_id); + let sig = tcx.liberate_late_bound_regions(method.def_id, sig); + let sig = wfcx.normalize(span, None, sig); debug!("check_method_receiver: sig={:?}", sig); - let self_ty = fcx.normalize_associated_types_in(span, self_ty); + let self_ty = wfcx.normalize(span, None, self_ty); let receiver_ty = sig.inputs()[0]; - let receiver_ty = fcx.normalize_associated_types_in(span, receiver_ty); + let receiver_ty = wfcx.normalize(span, None, receiver_ty); - if fcx.tcx.features().arbitrary_self_types { - if !receiver_is_valid(fcx, span, receiver_ty, self_ty, true) { + if tcx.features().arbitrary_self_types { + if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { // Report error; `arbitrary_self_types` was enabled. - e0307(fcx, span, receiver_ty); + e0307(tcx, span, receiver_ty); } } else { - if !receiver_is_valid(fcx, span, receiver_ty, self_ty, false) { - if receiver_is_valid(fcx, span, receiver_ty, self_ty, true) { + if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) { + if receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { // Report error; would have worked with `arbitrary_self_types`. feature_err( - &fcx.tcx.sess.parse_sess, + &tcx.sess.parse_sess, sym::arbitrary_self_types, span, &format!( @@ -1582,15 +1581,15 @@ fn check_method_receiver<'fcx, 'tcx>( .emit(); } else { // Report error; would not have worked with `arbitrary_self_types`. - e0307(fcx, span, receiver_ty); + e0307(tcx, span, receiver_ty); } } } } -fn e0307<'tcx>(fcx: &FnCtxt<'_, 'tcx>, span: Span, receiver_ty: Ty<'_>) { +fn e0307<'tcx>(tcx: TyCtxt<'tcx>, span: Span, receiver_ty: Ty<'_>) { struct_span_err!( - fcx.tcx.sess.diagnostic(), + tcx.sess.diagnostic(), span, E0307, "invalid `self` parameter type: {receiver_ty}" @@ -1609,26 +1608,30 @@ fn e0307<'tcx>(fcx: &FnCtxt<'_, 'tcx>, span: Span, receiver_ty: Ty<'_>) { /// N.B., there are cases this function returns `true` but causes an error to be emitted, /// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the /// wrong lifetime. Be careful of this if you are calling this function speculatively. -fn receiver_is_valid<'fcx, 'tcx>( - fcx: &FnCtxt<'fcx, 'tcx>, +fn receiver_is_valid<'tcx>( + wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, receiver_ty: Ty<'tcx>, self_ty: Ty<'tcx>, arbitrary_self_types_enabled: bool, ) -> bool { - let cause = fcx.cause(span, traits::ObligationCauseCode::MethodReceiver); + let infcx = wfcx.infcx; + let tcx = wfcx.tcx(); + let cause = + ObligationCause::new(span, wfcx.body_id, traits::ObligationCauseCode::MethodReceiver); - let can_eq_self = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok(); + let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty).is_ok(); // `self: Self` is always valid. if can_eq_self(receiver_ty) { - if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, receiver_ty) { - err.emit(); + if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) { + infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); } return true; } - let mut autoderef = fcx.autoderef(span, receiver_ty); + let mut autoderef = + Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty, span); // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`. if arbitrary_self_types_enabled { @@ -1638,7 +1641,7 @@ fn receiver_is_valid<'fcx, 'tcx>( // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. autoderef.next(); - let receiver_trait_def_id = fcx.tcx.require_lang_item(LangItem::Receiver, None); + let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, None); // Keep dereferencing `receiver_ty` until we get to `self_ty`. loop { @@ -1649,12 +1652,12 @@ fn receiver_is_valid<'fcx, 'tcx>( ); if can_eq_self(potential_self_ty) { - fcx.register_predicates(autoderef.into_obligations()); + wfcx.register_obligations(autoderef.into_obligations()); - if let Some(mut err) = - fcx.demand_eqtype_with_origin(&cause, self_ty, potential_self_ty) + if let Err(err) = + wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty) { - err.emit(); + infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit(); } break; @@ -1663,7 +1666,7 @@ fn receiver_is_valid<'fcx, 'tcx>( // deref chain implement `receiver` if !arbitrary_self_types_enabled && !receiver_is_implemented( - fcx, + wfcx, receiver_trait_def_id, cause.clone(), potential_self_ty, @@ -1682,7 +1685,7 @@ fn receiver_is_valid<'fcx, 'tcx>( // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`. if !arbitrary_self_types_enabled - && !receiver_is_implemented(fcx, receiver_trait_def_id, cause.clone(), receiver_ty) + && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty) { return false; } @@ -1691,23 +1694,21 @@ fn receiver_is_valid<'fcx, 'tcx>( } fn receiver_is_implemented<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, + wfcx: &WfCheckingCtxt<'_, 'tcx>, receiver_trait_def_id: DefId, cause: ObligationCause<'tcx>, receiver_ty: Ty<'tcx>, ) -> bool { + let tcx = wfcx.tcx(); let trait_ref = ty::Binder::dummy(ty::TraitRef { def_id: receiver_trait_def_id, - substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]), + substs: tcx.mk_substs_trait(receiver_ty, &[]), }); - let obligation = traits::Obligation::new( - cause, - fcx.param_env, - trait_ref.without_const().to_predicate(fcx.tcx), - ); + let obligation = + traits::Obligation::new(cause, wfcx.param_env, trait_ref.without_const().to_predicate(tcx)); - if fcx.predicate_must_hold_modulo_regions(&obligation) { + if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) { true } else { debug!( @@ -1809,55 +1810,56 @@ fn report_bivariance( err.emit() } -/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that -/// aren't true. -fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirId) { - let empty_env = ty::ParamEnv::empty(); - - let def_id = fcx.tcx.hir().local_def_id(id); - let predicates_with_span = - fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, span)| (*p, *span)); - // Check elaborated bounds. - let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span); - - for obligation in implied_obligations { - // We lower empty bounds like `Vec<dyn Copy>:` as - // `WellFormed(Vec<dyn Copy>)`, which will later get checked by - // regular WF checking - if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() { - continue; - } - let pred = obligation.predicate; - // Match the existing behavior. - if pred.is_global() && !pred.has_late_bound_regions() { - let pred = fcx.normalize_associated_types_in(span, pred); - let hir_node = fcx.tcx.hir().find(id); +impl<'tcx> WfCheckingCtxt<'_, 'tcx> { + /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that + /// aren't true. + fn check_false_global_bounds(&mut self) { + let tcx = self.ocx.infcx.tcx; + let mut span = self.span; + let empty_env = ty::ParamEnv::empty(); + + let def_id = tcx.hir().local_def_id(self.body_id); + let predicates_with_span = tcx.predicates_of(def_id).predicates.iter().copied(); + // Check elaborated bounds. + let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span); + + for obligation in implied_obligations { + // We lower empty bounds like `Vec<dyn Copy>:` as + // `WellFormed(Vec<dyn Copy>)`, which will later get checked by + // regular WF checking + if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() { + continue; + } + let pred = obligation.predicate; + // Match the existing behavior. + if pred.is_global() && !pred.has_late_bound_regions() { + let pred = self.normalize(span, None, pred); + let hir_node = tcx.hir().find(self.body_id); - // only use the span of the predicate clause (#90869) + // only use the span of the predicate clause (#90869) - if let Some(hir::Generics { predicates, .. }) = - hir_node.and_then(|node| node.generics()) - { - let obligation_span = obligation.cause.span(fcx.tcx); - - span = predicates - .iter() - // There seems to be no better way to find out which predicate we are in - .find(|pred| pred.span().contains(obligation_span)) - .map(|pred| pred.span()) - .unwrap_or(obligation_span); - } + if let Some(hir::Generics { predicates, .. }) = + hir_node.and_then(|node| node.generics()) + { + let obligation_span = obligation.cause.span(); + + span = predicates + .iter() + // There seems to be no better way to find out which predicate we are in + .find(|pred| pred.span().contains(obligation_span)) + .map(|pred| pred.span()) + .unwrap_or(obligation_span); + } - let obligation = traits::Obligation::new( - traits::ObligationCause::new(span, id, traits::TrivialBound), - empty_env, - pred, - ); - fcx.register_predicate(obligation); + let obligation = traits::Obligation::new( + traits::ObligationCause::new(span, self.body_id, traits::TrivialBound), + empty_env, + pred, + ); + self.ocx.register_obligation(obligation); + } } } - - fcx.select_all_obligations_or_error(); } fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { @@ -1887,17 +1889,16 @@ struct AdtField<'tcx> { span: Span, } -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { +impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { // FIXME(eddyb) replace this with getting fields through `ty::AdtDef`. fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> { let fields = struct_def .fields() .iter() .map(|field| { - let def_id = self.tcx.hir().local_def_id(field.hir_id); - let field_ty = self.tcx.type_of(def_id); - let field_ty = self.normalize_associated_types_in(field.ty.span, field_ty); - let field_ty = self.resolve_vars_if_possible(field_ty); + let def_id = self.tcx().hir().local_def_id(field.hir_id); + let field_ty = self.tcx().type_of(def_id); + let field_ty = self.normalize(field.ty.span, None, field_ty); debug!("non_enum_variant: type of field {:?} is {:?}", field, field_ty); AdtField { ty: field_ty, span: field.ty.span, def_id } }) @@ -1913,32 +1914,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: self.non_enum_variant(&variant.data).fields, explicit_discr: variant .disr_expr - .map(|explicit_discr| self.tcx.hir().local_def_id(explicit_discr.hir_id)), + .map(|explicit_discr| self.tcx().hir().local_def_id(explicit_discr.hir_id)), }) .collect() } +} - pub(super) fn impl_implied_bounds( - &self, - impl_def_id: DefId, - span: Span, - ) -> FxHashSet<Ty<'tcx>> { - match self.tcx.impl_trait_ref(impl_def_id) { +pub fn impl_implied_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: LocalDefId, + span: Span, +) -> FxHashSet<Ty<'tcx>> { + // We completely ignore any obligations caused by normalizing the types + // we assume to be well formed. Considering that the user of the implied + // bounds will also normalize them, we leave it to them to emit errors + // which should result in better causes and spans. + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id)); + match tcx.impl_trait_ref(impl_def_id) { Some(trait_ref) => { // Trait impl: take implied bounds from all types that // appear in the trait reference. - let trait_ref = self.normalize_associated_types_in(span, trait_ref); - trait_ref.substs.types().collect() + match infcx.at(&cause, param_env).normalize(trait_ref) { + Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(), + Err(NoSolution) => FxHashSet::default(), + } } None => { // Inherent impl: take implied bounds from the `self` type. - let self_ty = self.tcx.type_of(impl_def_id); - let self_ty = self.normalize_associated_types_in(span, self_ty); - FxHashSet::from_iter([self_ty]) + let self_ty = tcx.type_of(impl_def_id); + match infcx.at(&cause, param_env).normalize(self_ty) { + Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]), + Err(NoSolution) => FxHashSet::default(), + } } } - } + }) } fn error_392( diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 41d241f84ac..fa6053ac395 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -4,7 +4,8 @@ use crate::check::FnCtxt; -use rustc_data_structures::stable_map::FxHashMap; +use hir::def_id::LocalDefId; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -263,7 +264,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.fix_index_builtin_expr(e); match e.kind { - hir::ExprKind::Closure { body, .. } => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = self.fcx.tcx.hir().body(body); for param in body.params { self.visit_node_id(e.span, param.hir_id); @@ -509,13 +510,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); struct RecursionChecker { - def_id: DefId, + def_id: LocalDefId, } impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id { + if def_id == self.def_id.to_def_id() { return ControlFlow::Break(()); } } @@ -747,7 +748,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { // (e.g. keep `for<'a>` named `for<'a>`). // This allows NLL to generate error messages that // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.infcx.tcx }.fold_ty(t) + EraseEarlyRegions { tcx: self.tcx }.fold_ty(t) } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); @@ -765,7 +766,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match self.infcx.fully_resolve(ct) { - Ok(ct) => self.infcx.tcx.erase_regions(ct), + Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); self.report_const_error(ct); diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index d532050d050..d8e42729ff3 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -116,8 +116,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { // why this field does not implement Copy. This is useful because sometimes // it is not immediately clear why Copy is not implemented for a field, since // all we point at is the field itself. - tcx.infer_ctxt().enter(|infcx| { - let mut fulfill_cx = traits::FulfillmentContext::new_ignoring_regions(); + tcx.infer_ctxt().ignoring_regions().enter(|infcx| { + let mut fulfill_cx = traits::FulfillmentContext::new(); fulfill_cx.register_bound( &infcx, param_env, @@ -349,7 +349,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(&outlives_env); + infcx.resolve_regions_and_report_errors(impl_did, &outlives_env); } } _ => { @@ -606,7 +606,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - infcx.resolve_regions_and_report_errors(&outlives_env); + infcx.resolve_regions_and_report_errors(impl_did, &outlives_env); CoerceUnsizedInfo { custom_kind: kind } }) diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 7a9b874b5e4..52aad636fd8 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -219,8 +219,9 @@ impl<'tcx> InherentCollect<'tcx> { | ty::RawPtr(_) | ty::Ref(..) | ty::Never + | ty::FnPtr(_) | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), - ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { + ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { let mut err = struct_span_err!( self.tcx.sess, ty.span, diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index db67c1f7c9e..03e076bf5ec 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -76,17 +76,17 @@ impl<'tcx> InherentOverlapChecker<'tcx> { let name = item1.ident(self.tcx).normalize_to_macros_2_0(); let mut err = struct_span_err!( self.tcx.sess, - self.tcx.span_of_impl(item1.def_id).unwrap(), + self.tcx.def_span(item1.def_id), E0592, "duplicate definitions with name `{}`", name ); err.span_label( - self.tcx.span_of_impl(item1.def_id).unwrap(), + self.tcx.def_span(item1.def_id), format!("duplicate definitions for `{}`", name), ); err.span_label( - self.tcx.span_of_impl(item2.def_id).unwrap(), + self.tcx.def_span(item2.def_id), format!("other definition for `{}`", name), ); diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 44b9c8392f8..60c0694ca0e 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S match self.tcx.named_region(lt.hir_id) { Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} - Some( - rl::Region::LateBound(debruijn, _, _) - | rl::Region::LateBoundAnon(debruijn, _, _), - ) if debruijn < self.outer_index => {} - Some( - rl::Region::LateBound(..) - | rl::Region::LateBoundAnon(..) - | rl::Region::Free(..), - ) - | None => { + Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} + Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { self.has_late_bound_regions = Some(lt.span); } } @@ -1717,8 +1709,10 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // provide junk type parameter defs - the only place that // cares about anything but the length is instantiation, // and we don't do that for closures. - if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { movability: gen, .. }, .. }) = - node + if let Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }), + .. + }) = node { let dummy_args = if gen.is_some() { &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..] @@ -1927,7 +1921,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( visitor.visit_ty(ty); let mut diag = bad_placeholder(tcx, visitor.0, "return type"); let ret_ty = fn_sig.skip_binder().output(); - if ret_ty.is_suggestable(tcx) { + if ret_ty.is_suggestable(tcx, false) { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -1936,7 +1930,12 @@ fn infer_return_ty_for_fn_sig<'tcx>( ); } else if matches!(ret_ty.kind(), ty::FnDef(..)) { let fn_sig = ret_ty.fn_sig(tcx); - if fn_sig.skip_binder().inputs_and_output.iter().all(|t| t.is_suggestable(tcx)) { + if fn_sig + .skip_binder() + .inputs_and_output + .iter() + .all(|t| t.is_suggestable(tcx, false)) + { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -2083,10 +2082,17 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { // from the trait itself that *shouldn't* be shown as the source of // an obligation and instead be skipped. Otherwise we'd use // `tcx.def_span(def_id);` + + let constness = if tcx.has_attr(def_id, sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; + let span = rustc_span::DUMMY_SP; result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), + ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx), span, )))); } @@ -2564,7 +2570,7 @@ fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool { fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> { match tcx.hir().get_if_local(def_id) { Some(Node::Expr(&rustc_hir::Expr { - kind: rustc_hir::ExprKind::Closure { body, .. }, + kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }), .. })) => tcx.hir().body(body).generator_kind(), Some(_) => None, @@ -2768,6 +2774,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } } else if attr.has_name(sym::rustc_allocator_nounwind) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } else if attr.has_name(sym::rustc_reallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; + } else if attr.has_name(sym::rustc_deallocator) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR; + } else if attr.has_name(sym::rustc_allocator_zeroed) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED; } else if attr.has_name(sym::naked) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; } else if attr.has_name(sym::no_mangle) { @@ -2811,7 +2823,37 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { ) .emit(); } - None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED, + None => { + // Unfortunately, unconditionally using `llvm.used` causes + // issues in handling `.init_array` with the gold linker, + // but using `llvm.compiler.used` caused a nontrival amount + // of unintentional ecosystem breakage -- particularly on + // Mach-O targets. + // + // As a result, we emit `llvm.compiler.used` only on ELF + // targets. This is somewhat ad-hoc, but actually follows + // our pre-LLVM 13 behavior (prior to the ecosystem + // breakage), and seems to match `clang`'s behavior as well + // (both before and after LLVM 13), possibly because they + // have similar compatibility concerns to us. See + // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 + // and following comments for some discussion of this, as + // well as the comments in `rustc_codegen_llvm` where these + // flags are handled. + // + // Anyway, to be clear: this is still up in the air + // somewhat, and is subject to change in the future (which + // is a good thing, because this would ideally be a bit + // more firmed up). + let is_like_elf = !(tcx.sess.target.is_like_osx + || tcx.sess.target.is_like_windows + || tcx.sess.target.is_like_wasm); + codegen_fn_attrs.flags |= if is_like_elf { + CodegenFnAttrFlags::USED + } else { + CodegenFnAttrFlags::USED_LINKER + }; + } } } else if attr.has_name(sym::cmse_nonsecure_entry) { if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) { @@ -2937,6 +2979,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.has_name(sym::memtag) { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; + } else if item.has_name(sym::shadow_call_stack) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else if item.has_name(sym::hwaddress) { @@ -2944,7 +2988,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`") .emit(); } } @@ -3196,7 +3240,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { /// Computes the set of target features used in a function for the purposes of /// inline assembly. fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> { - let mut target_features = tcx.sess.target_features.clone(); + let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); target_features.extend(&attrs.target_features); diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index f942a4fb53a..64ac655e0c3 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -335,37 +335,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.mk_adt(def, substs) } ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { - find_opaque_ty_constraints(tcx, def_id) + find_opaque_ty_constraints_for_tait(tcx, def_id) } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => { - let concrete_ty = tcx - .mir_borrowck(owner) - .concrete_opaque_types - .get(&def_id.to_def_id()) - .copied() - .map(|concrete| concrete.ty) - .unwrap_or_else(|| { - let table = tcx.typeck(owner); - if let Some(_) = table.tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.ty_error() - } else { - table.concrete_opaque_types.get(&def_id.to_def_id()).copied().unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }).expect("RPIT always have a hidden type from typeck") - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - concrete_ty + find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) @@ -519,14 +493,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { /// fn b<T>() -> Foo<T, u32> { .. } /// ``` /// -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { +fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { use rustc_hir::{Expr, ImplItem, Item, TraitItem}; struct ConstraintLocator<'tcx> { tcx: TyCtxt<'tcx>, /// def_id of the opaque type whose defining uses are being checked - def_id: DefId, + def_id: LocalDefId, /// as we walk the defining uses, we are checking that all of them /// define the same hidden type. This variable is set to `Some` @@ -538,9 +512,9 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { impl ConstraintLocator<'_> { #[instrument(skip(self), level = "debug")] - fn check(&mut self, def_id: LocalDefId) { + fn check(&mut self, item_def_id: LocalDefId) { // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(def_id) { + if !self.tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); return; } @@ -555,26 +529,20 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { // // because we again need to reveal `Foo` so we can check whether the // // constant does not contain interior mutability. // ``` - let tables = self.tcx.typeck(def_id); + let tables = self.tcx.typeck(item_def_id); if let Some(_) = tables.tainted_by_errors { self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); return; } - if tables.concrete_opaque_types.get(&self.def_id).is_none() { + if !tables.concrete_opaque_types.contains_key(&self.def_id) { debug!("no constraints in typeck results"); return; } // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; debug!(?concrete_opaque_types); - for &(def_id, concrete_type) in concrete_opaque_types { - if def_id != self.def_id { - // Ignore constraints for other opaque types. - continue; - } - + if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { debug!(?concrete_type, "found constraint"); - if let Some(prev) = self.found { if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() { prev.report_mismatch(&concrete_type, self.tcx); @@ -602,7 +570,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { fn visit_item(&mut self, it: &'tcx Item<'tcx>) { trace!(?it.def_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id.to_def_id() != self.def_id { + if it.def_id != self.def_id { self.check(it.def_id); intravisit::walk_item(self, it); } @@ -610,7 +578,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { trace!(?it.def_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id.to_def_id() != self.def_id { + if it.def_id != self.def_id { self.check(it.def_id); intravisit::walk_impl_item(self, it); } @@ -624,7 +592,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id: def_id.to_def_id(), tcx, found: None }; + let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None }; debug!(?scope); @@ -666,6 +634,122 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { } } +fn find_opaque_ty_constraints_for_rpit( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + owner_def_id: LocalDefId, +) -> Ty<'_> { + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; + + struct ConstraintChecker<'tcx> { + tcx: TyCtxt<'tcx>, + + /// def_id of the opaque type whose defining uses are being checked + def_id: LocalDefId, + + found: ty::OpaqueHiddenType<'tcx>, + } + + impl ConstraintChecker<'_> { + #[instrument(skip(self), level = "debug")] + fn check(&self, def_id: LocalDefId) { + // Use borrowck to get the type with unerased regions. + let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; + debug!(?concrete_opaque_types); + for &(def_id, concrete_type) in concrete_opaque_types { + if def_id != self.def_id { + // Ignore constraints for other opaque types. + continue; + } + + debug!(?concrete_type, "found constraint"); + + if concrete_type.ty != self.found.ty + && !(concrete_type, self.found).references_error() + { + self.found.report_mismatch(&concrete_type, self.tcx); + } + } + } + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure { .. } = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + trace!(?it.def_id); + // The opaque type itself or its children are not within its reveal scope. + if it.def_id != self.def_id { + self.check(it.def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + trace!(?it.def_id); + self.check(it.def_id); + intravisit::walk_trait_item(self, it); + } + } + + let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); + + if let Some(concrete) = concrete { + let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); + debug!(?scope); + let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete }; + + match tcx.hir().get(scope) { + Node::Item(it) => intravisit::walk_item(&mut locator, it), + Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), + Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { + let table = tcx.typeck(owner_def_id); + if let Some(_) = table.tainted_by_errors { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.ty_error() + } else { + table + .concrete_opaque_types + .get(&def_id) + .copied() + .unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + Some(tcx.mk_diverging_default()) + }) + .expect("RPIT always have a hidden type from typeck") + } + }) +} + fn infer_placeholder_type<'a>( tcx: TyCtxt<'a>, def_id: LocalDefId, diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 4cdec615d82..0438ac02ea9 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -244,7 +244,7 @@ pub struct UnconstrainedOpaqueType { pub struct MissingTypeParams { pub span: Span, pub def_span: Span, - pub missing_type_params: Vec<String>, + pub missing_type_params: Vec<Symbol>, pub empty_generic_args: bool, } @@ -285,7 +285,15 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { err.span_suggestion( self.span, rustc_errors::fluent::typeck::suggestion, - format!("{}<{}>", snippet, self.missing_type_params.join(", ")), + format!( + "{}<{}>", + snippet, + self.missing_type_params + .iter() + .map(|n| n.to_string()) + .collect::<Vec<_>>() + .join(", ") + ), Applicability::HasPlaceholders, ); suggested = true; diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 920e3d578c8..9d7420acd26 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -2,7 +2,10 @@ //! normal visitor, which just walks the entire body in one shot, the //! `ExprUseVisitor` determines how expressions are being used. +use std::slice::from_ref; + use hir::def::DefKind; +use hir::Expr; // Export these here so that Clippy can use them. pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; @@ -252,96 +255,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } hir::ExprKind::Let(hir::Let { pat, init, .. }) => { - self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow)); + self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(discr)); - - // Matching should not always be considered a use of the place, hence - // discr does not necessarily need to be borrowed. - // We only want to borrow discr if the pattern contain something other - // than wildcards. - let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; - let mut needs_to_be_read = false; - for arm in arms.iter() { - return_if_err!(mc.cat_pattern(discr_place.clone(), arm.pat, |place, pat| { - match &pat.kind { - PatKind::Binding(.., opt_sub_pat) => { - // If the opt_sub_pat is None, than the binding does not count as - // a wildcard for the purpose of borrowing discr. - if opt_sub_pat.is_none() { - needs_to_be_read = true; - } - } - PatKind::Path(qpath) => { - // A `Path` pattern is just a name like `Foo`. This is either a - // named constant or else it refers to an ADT variant - - let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); - match res { - Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { - // Named constants have to be equated with the value - // being matched, so that's a read of the value being matched. - // - // FIXME: We don't actually reads for ZSTs. - needs_to_be_read = true; - } - _ => { - // Otherwise, this is a struct/enum variant, and so it's - // only a read if we need to read the discriminant. - needs_to_be_read |= is_multivariant_adt(place.place.ty()); - } - } - } - PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { - // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching - // against a multivariant enum or struct. In that case, we have to read - // the discriminant. Otherwise this kind of pattern doesn't actually - // read anything (we'll get invoked for the `...`, which may indeed - // perform some reads). - - let place_ty = place.place.ty(); - needs_to_be_read |= is_multivariant_adt(place_ty); - } - PatKind::Lit(_) | PatKind::Range(..) => { - // If the PatKind is a Lit or a Range then we want - // to borrow discr. - needs_to_be_read = true; - } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Slice(..) - | PatKind::Ref(..) - | PatKind::Wild => { - // If the PatKind is Or, Box, Slice 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 needs_to_be_read { - self.borrow_expr(discr, ty::ImmBorrow); - } else { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), - _ => None, - }; - - self.delegate.fake_read( - &discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); - - // We always want to walk the discriminant. We want to make sure, for instance, - // that the discriminant has been initialized. - self.walk_expr(discr); - } + self.maybe_read_scrutinee( + discr, + discr_place.clone(), + arms.iter().map(|arm| arm.pat), + ); // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -453,8 +376,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => { - self.walk_local(expr, pat, |_| {}); + hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => { + self.walk_local(expr, pat, *els, |_| {}) } hir::StmtKind::Local(_) => {} @@ -470,13 +393,114 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - fn walk_local<F>(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F) - where + fn maybe_read_scrutinee<'t>( + &mut self, + discr: &Expr<'_>, + discr_place: PlaceWithHirId<'tcx>, + pats: impl Iterator<Item = &'t hir::Pat<'t>>, + ) { + // Matching should not always be considered a use of the place, hence + // discr does not necessarily need to be borrowed. + // We only want to borrow discr if the pattern contain something other + // than wildcards. + let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; + let mut needs_to_be_read = false; + for pat in pats { + return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { + match &pat.kind { + PatKind::Binding(.., opt_sub_pat) => { + // If the opt_sub_pat is None, than the binding does not count as + // a wildcard for the purpose of borrowing discr. + if opt_sub_pat.is_none() { + needs_to_be_read = true; + } + } + PatKind::Path(qpath) => { + // A `Path` pattern is just a name like `Foo`. This is either a + // named constant or else it refers to an ADT variant + + let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); + match res { + Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + // Named constants have to be equated with the value + // being matched, so that's a read of the value being matched. + // + // FIXME: We don't actually reads for ZSTs. + needs_to_be_read = true; + } + _ => { + // Otherwise, this is a struct/enum variant, and so it's + // only a read if we need to read the discriminant. + needs_to_be_read |= is_multivariant_adt(place.place.ty()); + } + } + } + PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { + // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching + // against a multivariant enum or struct. In that case, we have to read + // the discriminant. Otherwise this kind of pattern doesn't actually + // read anything (we'll get invoked for the `...`, which may indeed + // perform some reads). + + let place_ty = place.place.ty(); + needs_to_be_read |= is_multivariant_adt(place_ty); + } + PatKind::Lit(_) | PatKind::Range(..) => { + // If the PatKind is a Lit or a Range then we want + // to borrow discr. + needs_to_be_read = true; + } + PatKind::Or(_) + | PatKind::Box(_) + | PatKind::Slice(..) + | PatKind::Ref(..) + | PatKind::Wild => { + // If the PatKind is Or, Box, Slice 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 needs_to_be_read { + self.borrow_expr(discr, ty::ImmBorrow); + } else { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + &discr_place, + FakeReadCause::ForMatchedPlace(closure_def_id), + discr_place.hir_id, + ); + + // We always want to walk the discriminant. We want to make sure, for instance, + // that the discriminant has been initialized. + self.walk_expr(discr); + } + } + + fn walk_local<F>( + &mut self, + expr: &hir::Expr<'_>, + pat: &hir::Pat<'_>, + els: Option<&hir::Block<'_>>, + mut f: F, + ) where F: FnMut(&mut Self), { self.walk_expr(expr); let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); + if let Some(els) = els { + // borrowing because we need to test the descriminant + self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); + self.walk_block(els) + } self.walk_irrefutable_pat(&expr_place, &pat); } @@ -667,7 +691,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); + debug!("walk_pat: binding place={:?} pat={:?}", place, pat); if let Some(bm) = mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) { diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index e7ca70de4ba..8c26c96816d 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -17,7 +17,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -123,12 +123,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) 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", - ¶m_ty.to_string(), - ); + report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name); } } ty::GenericParamDefKind::Lifetime => { @@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) tcx, tcx.def_span(param.def_id), "lifetime", - ¶m.name.to_string(), + param.name, ); } } @@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) tcx, tcx.def_span(param.def_id), "const", - ¶m_ct.to_string(), + param_ct.name, ); } } @@ -178,7 +173,7 @@ 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: &str) { +fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) { let mut err = struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index c46b825f457..f16888345e9 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -65,6 +65,8 @@ //! cause use after frees with purely safe code in the same way as specializing //! on traits with methods can. +use crate::check::regionck::OutlivesEnvironmentExt; +use crate::check::wfcheck::impl_implied_bounds; use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; @@ -148,9 +150,16 @@ fn get_impl_substs<'tcx>( let impl2_substs = translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); - // Conservatively use an empty `ParamEnv`. - let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(&outlives_env); + let mut outlives_env = OutlivesEnvironment::new(param_env); + let implied_bounds = + impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id)); + outlives_env.add_implied_bounds( + infcx, + implied_bounds, + tcx.hir().local_def_id_to_hir_id(impl1_def_id), + ); + infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); + infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index dd712fd7ed7..706b9ee40aa 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -65,13 +65,14 @@ This API is completely unstable and subject to change. #![feature(is_sorted)] #![feature(iter_intersperse)] #![feature(label_break_value)] -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] #![feature(once_cell)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] +#![feature(is_some_with)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs index bbf31de527e..7534482cce9 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -6,7 +6,7 @@ use super::utils::*; #[derive(Debug)] pub struct ExplicitPredicatesMap<'tcx> { - map: FxHashMap<DefId, RequiredPredicates<'tcx>>, + map: FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, } impl<'tcx> ExplicitPredicatesMap<'tcx> { @@ -14,11 +14,11 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ExplicitPredicatesMap { map: FxHashMap::default() } } - pub fn explicit_predicates_of( + pub(crate) fn explicit_predicates_of( &mut self, tcx: TyCtxt<'tcx>, def_id: DefId, - ) -> &RequiredPredicates<'tcx> { + ) -> &ty::EarlyBinder<RequiredPredicates<'tcx>> { self.map.entry(def_id).or_insert_with(|| { let predicates = if def_id.is_local() { tcx.explicit_predicates_of(def_id) @@ -63,7 +63,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { } } - required_predicates + ty::EarlyBinder(required_predicates) }) } } diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 52f9e386441..257a9520eeb 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -13,20 +13,19 @@ use super::utils::*; /// `global_inferred_outlives`: this is initially the empty map that /// was generated by walking the items in the crate. This will /// now be filled with inferred predicates. -pub fn infer_predicates<'tcx>( +pub(super) fn infer_predicates<'tcx>( tcx: TyCtxt<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, -) -> FxHashMap<DefId, RequiredPredicates<'tcx>> { +) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> { debug!("infer_predicates"); - let mut predicates_added = true; + let mut explicit_map = ExplicitPredicatesMap::new(); let mut global_inferred_outlives = FxHashMap::default(); // If new predicates were added then we need to re-calculate // all crates since there could be new implied predicates. - while predicates_added { - predicates_added = false; + 'outer: loop { + let mut predicates_added = false; // Visit all the crates and infer predicates for id in tcx.hir().items() { @@ -53,9 +52,9 @@ pub fn infer_predicates<'tcx>( tcx, field_ty, field_span, - &mut global_inferred_outlives, + &global_inferred_outlives, &mut item_required_predicates, - explicit_map, + &mut explicit_map, ); } } @@ -70,12 +69,17 @@ pub fn infer_predicates<'tcx>( // we walk the crates again and re-calculate predicates for all // items. let item_predicates_len: usize = - global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.len()); + global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len()); if item_required_predicates.len() > item_predicates_len { predicates_added = true; - global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates); + global_inferred_outlives + .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates)); } } + + if !predicates_added { + break 'outer; + } } global_inferred_outlives @@ -85,7 +89,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( tcx: TyCtxt<'tcx>, field_ty: Ty<'tcx>, field_span: Span, - global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>, + global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { @@ -133,11 +137,13 @@ fn insert_required_predicates_to_be_wf<'tcx>( // 'a` holds for `Foo`. debug!("Adt"); if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) { - for (unsubstituted_predicate, &span) in unsubstituted_predicates { + for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 { // `unsubstituted_predicate` is `U: 'b` in the // example above. So apply the substitution to // get `T: 'a` (or `predicate`): - let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs); + let predicate = unsubstituted_predicates + .rebind(*unsubstituted_predicate) + .subst(tcx, substs); insert_outlives_predicate( tcx, predicate.0, @@ -224,7 +230,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// will give us `U: 'static` and `U: Foo`. The latter we /// can ignore, but we will want to process `U: 'static`, /// applying the substitution as above. -pub fn check_explicit_predicates<'tcx>( +fn check_explicit_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, substs: &[GenericArg<'tcx>], @@ -242,7 +248,7 @@ pub fn check_explicit_predicates<'tcx>( ); let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id); - for (outlives_predicate, &span) in explicit_predicates { + for (outlives_predicate, &span) in &explicit_predicates.0 { debug!("outlives_predicate = {:?}", &outlives_predicate); // Careful: If we are inferring the effects of a `dyn Trait<..>` @@ -287,7 +293,7 @@ pub fn check_explicit_predicates<'tcx>( continue; } - let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs); + let predicate = explicit_predicates.rebind(*outlives_predicate).subst(tcx, substs); debug!("predicate = {:?}", &predicate); insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); } diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index dccfee19960..8fa65d51e3b 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -88,9 +88,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { // for the type. // Compute the inferred predicates - let mut exp_map = explicit::ExplicitPredicatesMap::new(); - - let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map); + let global_inferred_outlives = implicit_infer::infer_predicates(tcx); // Convert the inferred predicates into the "collected" form the // global data structure expects. @@ -100,7 +98,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { let predicates = global_inferred_outlives .iter() .map(|(&def_id, set)| { - let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map( + let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map( |(ty::OutlivesPredicate(kind1, region2), &span)| { match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index 14e3048cadc..b718ca94213 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -7,12 +7,12 @@ use std::collections::BTreeMap; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. -pub type RequiredPredicates<'tcx> = +pub(crate) type RequiredPredicates<'tcx> = BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` -pub fn insert_outlives_predicate<'tcx>( +pub(crate) fn insert_outlives_predicate<'tcx>( tcx: TyCtxt<'tcx>, kind: GenericArg<'tcx>, outlived_region: Region<'tcx>, diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 469f7d1172a..99729391e02 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -420,12 +420,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let provided_lt_args = self.num_provided_lifetime_args(); let provided_type_or_const_args = self.num_provided_type_or_const_args(); - let get_verb = |num_args| if num_args == 1 { "was" } else { "were" }; - let (provided_args_str, verb) = match self.gen_args_info { MissingLifetimes { .. } | ExcessLifetimes { .. } => ( format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)), - get_verb(provided_lt_args), + pluralize!("was", provided_lt_args), ), MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => ( format!( @@ -433,7 +431,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { provided_type_or_const_args, pluralize!(provided_type_or_const_args) ), - get_verb(provided_type_or_const_args), + pluralize!("was", provided_type_or_const_args), ), }; |
