diff options
Diffstat (limited to 'compiler')
183 files changed, 3305 insertions, 3097 deletions
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index a1233d62cb0..708fe8719a1 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,4 +1,6 @@ -use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + struct_span_err, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan, +}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -476,10 +478,11 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { self.infcx.tcx.sess.struct_span_err_with_code(sp, msg, code) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 8f3699553d9..e60e11f11df 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -879,7 +879,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } let mut found = false; - tcx.fold_regions(tcx.type_of(body_parent_did), &mut true, |r: ty::Region<'tcx>, _| { + tcx.fold_regions(tcx.type_of(body_parent_did), |r: ty::Region<'tcx>, _| { if *r == ty::ReEarlyBound(region) { found = true; } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 8ef2974c372..a2df072aa31 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(let_else)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![feature(trusted_step)] #![feature(try_blocks)] diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0fe3b45bc7c..f5c9392948b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1009,7 +1009,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("try_promote_type_test_subject(ty = {:?})", ty); - let ty = tcx.fold_regions(ty, &mut false, |r, _depth| { + let ty = tcx.fold_regions(ty, |r, _depth| { let region_vid = self.to_region_vid(r); // The challenge if this. We have some region variable `r` @@ -1289,7 +1289,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(value, &mut false, |r, _db| { + tcx.fold_regions(value, |r, _db| { let vid = self.to_region_vid(r); let scc = self.constraint_sccs.scc(vid); let repr = self.scc_representatives[scc]; diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 81073758791..d182c0cf4e8 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -59,7 +59,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!(?concrete_type, ?substs); let mut subst_regions = vec![self.universal_regions.fr_static]; - let universal_substs = infcx.tcx.fold_regions(substs, &mut false, |region, _| { + let universal_substs = infcx.tcx.fold_regions(substs, |region, _| { if let ty::RePlaceholder(..) = region.kind() { // Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs. return region; @@ -91,7 +91,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { subst_regions.dedup(); let universal_concrete_type = - infcx.tcx.fold_regions(concrete_type, &mut false, |region, _| match *region { + infcx.tcx.fold_regions(concrete_type, |region, _| match *region { ty::ReVar(vid) => subst_regions .iter() .find(|ur_vid| self.eval_equal(vid, **ur_vid)) @@ -146,7 +146,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(ty, &mut false, |region, _| match *region { + tcx.fold_regions(ty, |region, _| match *region { ty::ReVar(vid) => { // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 2876d60527f..7a8ce621c5d 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -31,7 +31,7 @@ pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'_, 'tcx>, value: T) -> T where T: TypeFoldable<'tcx>, { - infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { + infcx.tcx.fold_regions(value, |_region, _depth| { let origin = NllRegionVariableOrigin::Existential { from_forall: false }; infcx.next_nll_region_var(origin) }) diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 3c9e3870aea..5e33d9d25c2 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -23,7 +23,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { tcx: TyCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: Option<ty::Region<'tcx>>, + implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, locations: Locations, span: Span, @@ -36,7 +36,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - implicit_region_bound: Option<ty::Region<'tcx>>, + implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, locations: Locations, span: Span, @@ -108,7 +108,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // create new region variables, which can't be done later when // verifying these bounds. if t1.has_placeholders() { - t1 = tcx.fold_regions(t1, &mut false, |r, _| match *r { + t1 = tcx.fold_regions(t1, |r, _| match *r { ty::RePlaceholder(placeholder) => { self.constraints.placeholder_region(self.infcx, placeholder) } @@ -120,7 +120,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { &mut *self, tcx, region_bound_pairs, - implicit_region_bound, + Some(implicit_region_bound), param_env, ) .type_must_outlive(origin, t1, r2); 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 813307356c4..421ef5be812 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -61,7 +61,7 @@ pub(crate) struct CreateResult<'tcx> { pub(crate) fn create<'tcx>( infcx: &InferCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, - implicit_region_bound: Option<ty::Region<'tcx>>, + implicit_region_bound: ty::Region<'tcx>, universal_regions: &Rc<UniversalRegions<'tcx>>, constraints: &mut MirTypeckRegionConstraints<'tcx>, ) -> CreateResult<'tcx> { @@ -223,7 +223,7 @@ struct UniversalRegionRelationsBuilder<'this, 'tcx> { infcx: &'this InferCtxt<'this, 'tcx>, param_env: ty::ParamEnv<'tcx>, universal_regions: Rc<UniversalRegions<'tcx>>, - implicit_region_bound: Option<ty::Region<'tcx>>, + implicit_region_bound: ty::Region<'tcx>, constraints: &'this mut MirTypeckRegionConstraints<'tcx>, // outputs: diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 2259a59e195..2a6ca5246da 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx, &self.borrowck_context.universal_regions, &self.region_bound_pairs, - Some(self.implicit_region_bound), + self.implicit_region_bound, self.param_env, Locations::All(DUMMY_SP), DUMMY_SP, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 355254fe942..542fc6b0f48 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -157,7 +157,7 @@ pub(crate) fn type_check<'mir, 'tcx>( } = free_region_relations::create( infcx, param_env, - Some(implicit_region_bound), + implicit_region_bound, universal_regions, &mut constraints, ); @@ -1142,7 +1142,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.infcx, self.borrowck_context.universal_regions, self.region_bound_pairs, - Some(self.implicit_region_bound), + self.implicit_region_bound, self.param_env, locations, locations.span(self.body), diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index c2c093f9f2f..89d84fcf09c 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -725,7 +725,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { where T: TypeFoldable<'tcx>, { - self.tcx.fold_regions(value, &mut false, |_region, _depth| self.next_nll_region_var(origin)) + self.tcx.fold_regions(value, |_region, _depth| self.next_nll_region_var(origin)) } #[instrument(level = "debug", skip(self, indices))] @@ -817,9 +817,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(value, &mut false, |region, _| { - tcx.mk_region(ty::ReVar(self.to_region_vid(region))) - }) + tcx.fold_regions(value, |region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region)))) } } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index cad30181212..9e50d33486c 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,11 +1,10 @@ -use crate::assert::expr_if_not; use rustc_ast::{ attr, ptr::P, token, tokenstream::{DelimSpan, TokenStream, TokenTree}, - BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path, - PathSegment, Stmt, StructRest, UseTree, UseTreeKind, DUMMY_NODE_ID, + BinOpKind, BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, + Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -16,11 +15,19 @@ use rustc_span::{ }; pub(super) struct Context<'cx, 'a> { + // An optimization. + // + // Elements that aren't consumed (PartialEq, PartialOrd, ...) can be copied **after** the + // `assert!` expression fails rather than copied on-the-fly. + best_case_captures: Vec<Stmt>, // Top-level `let captureN = Capture::new()` statements capture_decls: Vec<Capture>, cx: &'cx ExtCtxt<'a>, // Formatting string used for debugging fmt_string: String, + // If the current expression being visited consumes itself. Used to construct + // `best_case_captures`. + is_consumed: bool, // Top-level `let __local_bindN = &expr` statements local_bind_decls: Vec<Stmt>, // Used to avoid capturing duplicated paths @@ -36,9 +43,11 @@ pub(super) struct Context<'cx, 'a> { impl<'cx, 'a> Context<'cx, 'a> { pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self { Self { + best_case_captures: <_>::default(), capture_decls: <_>::default(), cx, fmt_string: <_>::default(), + is_consumed: true, local_bind_decls: <_>::default(), paths: <_>::default(), span, @@ -69,14 +78,22 @@ impl<'cx, 'a> Context<'cx, 'a> { self.manage_cond_expr(&mut cond_expr); let initial_imports = self.build_initial_imports(); let panic = self.build_panic(&expr_str, panic_path); + let cond_expr_with_unlikely = self.build_unlikely(cond_expr); + + let Self { best_case_captures, capture_decls, cx, local_bind_decls, span, .. } = self; - let Self { capture_decls, cx, local_bind_decls, span, .. } = self; + let mut assert_then_stmts = Vec::with_capacity(2); + assert_then_stmts.extend(best_case_captures); + assert_then_stmts.push(self.cx.stmt_expr(panic)); + let assert_then = self.cx.block(span, assert_then_stmts); let mut stmts = Vec::with_capacity(4); stmts.push(initial_imports); stmts.extend(capture_decls.into_iter().map(|c| c.decl)); stmts.extend(local_bind_decls); - stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None))); + stmts.push( + cx.stmt_expr(cx.expr(span, ExprKind::If(cond_expr_with_unlikely, assert_then, None))), + ); cx.expr_block(cx.block(span, stmts)) } @@ -115,6 +132,16 @@ impl<'cx, 'a> Context<'cx, 'a> { ) } + /// Takes the conditional expression of `assert!` and then wraps it inside `unlikely` + fn build_unlikely(&self, cond_expr: P<Expr>) -> P<Expr> { + let unlikely_path = self.cx.std_path(&[sym::intrinsics, sym::unlikely]); + self.cx.expr_call( + self.span, + self.cx.expr_path(self.cx.path(self.span, unlikely_path)), + vec![self.cx.expr(self.span, ExprKind::Unary(UnOp::Not, cond_expr))], + ) + } + /// The necessary custom `panic!(...)` expression. /// /// panic!( @@ -167,17 +194,39 @@ impl<'cx, 'a> Context<'cx, 'a> { /// See [Self::manage_initial_capture] and [Self::manage_try_capture] fn manage_cond_expr(&mut self, expr: &mut P<Expr>) { match (*expr).kind { - ExprKind::AddrOf(_, _, ref mut local_expr) => { - self.manage_cond_expr(local_expr); + ExprKind::AddrOf(_, mutability, ref mut local_expr) => { + self.with_is_consumed_management( + matches!(mutability, Mutability::Mut), + |this| this.manage_cond_expr(local_expr) + ); } ExprKind::Array(ref mut local_exprs) => { for local_expr in local_exprs { self.manage_cond_expr(local_expr); } } - ExprKind::Binary(_, ref mut lhs, ref mut rhs) => { - self.manage_cond_expr(lhs); - self.manage_cond_expr(rhs); + ExprKind::Binary(ref op, ref mut lhs, ref mut rhs) => { + self.with_is_consumed_management( + matches!( + op.node, + BinOpKind::Add + | BinOpKind::And + | BinOpKind::BitAnd + | BinOpKind::BitOr + | BinOpKind::BitXor + | BinOpKind::Div + | BinOpKind::Mul + | BinOpKind::Or + | BinOpKind::Rem + | BinOpKind::Shl + | BinOpKind::Shr + | BinOpKind::Sub + ), + |this| { + this.manage_cond_expr(lhs); + this.manage_cond_expr(rhs); + } + ); } ExprKind::Call(_, ref mut local_exprs) => { for local_expr in local_exprs { @@ -228,8 +277,11 @@ impl<'cx, 'a> Context<'cx, 'a> { self.manage_cond_expr(local_expr); } } - ExprKind::Unary(_, ref mut local_expr) => { - self.manage_cond_expr(local_expr); + ExprKind::Unary(un_op, ref mut local_expr) => { + self.with_is_consumed_management( + matches!(un_op, UnOp::Neg | UnOp::Not), + |this| this.manage_cond_expr(local_expr) + ); } // Expressions that are not worth or can not be captured. // @@ -337,9 +389,23 @@ impl<'cx, 'a> Context<'cx, 'a> { )) .add_trailing_semicolon(); let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind)); - let ret = self.cx.stmt_expr(local_bind_path); - let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret])); - *expr = self.cx.expr_deref(self.span, block); + let rslt = if self.is_consumed { + let ret = self.cx.stmt_expr(local_bind_path); + self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret])) + } else { + self.best_case_captures.push(try_capture_call); + local_bind_path + }; + *expr = self.cx.expr_deref(self.span, rslt); + } + + // Calls `f` with the internal `is_consumed` set to `curr_is_consumed` and then + // sets the internal `is_consumed` back to its original value. + fn with_is_consumed_management(&mut self, curr_is_consumed: bool, f: impl FnOnce(&mut Self)) { + let prev_is_consumed = self.is_consumed; + self.is_consumed = curr_is_consumed; + f(self); + self.is_consumed = prev_is_consumed; } } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index c75d83bd0a0..aa355150b4f 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -36,7 +36,7 @@ pub fn expand_cfg( } #[derive(SessionDiagnostic)] -#[error(slug = "builtin-macros-requires-cfg-pattern")] +#[error(builtin_macros::requires_cfg_pattern)] struct RequiresCfgPattern { #[primary_span] #[label] @@ -44,7 +44,7 @@ struct RequiresCfgPattern { } #[derive(SessionDiagnostic)] -#[error(slug = "builtin-macros-expected-one-cfg-pattern")] +#[error(builtin_macros::expected_one_cfg_pattern)] struct OneCfgPattern { #[primary_span] span: Span, diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index c952fc0a866..83317106990 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -3,9 +3,9 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; +use rustc_ast::{self as ast, Expr, Generics, ItemKind, MetaItem, VariantData}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; pub fn expand_deriving_clone( @@ -107,44 +107,38 @@ fn cs_clone_shallow( substr: &Substructure<'_>, is_union: bool, ) -> P<Expr> { - fn assert_ty_bounds( - cx: &mut ExtCtxt<'_>, - stmts: &mut Vec<ast::Stmt>, - ty: P<ast::Ty>, - span: Span, - helper_name: &str, - ) { - // Generate statement `let _: helper_name<ty>;`, - // set the expn ID so we can use the unstable struct. - let span = cx.with_def_site_ctxt(span); - let assert_path = cx.path_all( - span, - true, - cx.std_path(&[sym::clone, Symbol::intern(helper_name)]), - vec![GenericArg::Type(ty)], - ); - stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); - } - fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) { + let mut stmts = Vec::new(); + let mut process_variant = |variant: &VariantData| { for field in variant.fields() { // let _: AssertParamIsClone<FieldTy>; - assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::clone, sym::AssertParamIsClone], + ); } - } + }; - let mut stmts = Vec::new(); if is_union { // let _: AssertParamIsCopy<Self>; let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); - assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); + super::assert_ty_bounds( + cx, + &mut stmts, + self_ty, + trait_span, + &[sym::clone, sym::AssertParamIsCopy], + ); } else { match *substr.fields { StaticStruct(vdata, ..) => { - process_variant(cx, &mut stmts, vdata); + process_variant(vdata); } StaticEnum(enum_def, ..) => { for variant in &enum_def.variants { - process_variant(cx, &mut stmts, &variant.data); + process_variant(&variant.data); } } _ => cx.span_bug( diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 54ab88dc3ff..f54ded3c87c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -3,9 +3,9 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, GenericArg, MetaItem}; +use rustc_ast::{self as ast, Expr, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; pub fn expand_deriving_eq( @@ -55,43 +55,27 @@ fn cs_total_eq_assert( trait_span: Span, substr: &Substructure<'_>, ) -> P<Expr> { - fn assert_ty_bounds( - cx: &mut ExtCtxt<'_>, - stmts: &mut Vec<ast::Stmt>, - ty: P<ast::Ty>, - span: Span, - helper_name: &str, - ) { - // Generate statement `let _: helper_name<ty>;`, - // set the expn ID so we can use the unstable struct. - let span = cx.with_def_site_ctxt(span); - let assert_path = cx.path_all( - span, - true, - cx.std_path(&[sym::cmp, Symbol::intern(helper_name)]), - vec![GenericArg::Type(ty)], - ); - stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); - } - fn process_variant( - cx: &mut ExtCtxt<'_>, - stmts: &mut Vec<ast::Stmt>, - variant: &ast::VariantData, - ) { + let mut stmts = Vec::new(); + let mut process_variant = |variant: &ast::VariantData| { for field in variant.fields() { // let _: AssertParamIsEq<FieldTy>; - assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::cmp, sym::AssertParamIsEq], + ); } - } + }; - let mut stmts = Vec::new(); match *substr.fields { StaticStruct(vdata, ..) => { - process_variant(cx, &mut stmts, vdata); + process_variant(vdata); } StaticEnum(enum_def, ..) => { for variant in &enum_def.variants { - process_variant(cx, &mut stmts, &variant.data); + process_variant(&variant.data); } } _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`"), diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index ecf70da6d96..1fffd6f9727 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -3,14 +3,10 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, LocalKind, MetaItem}; +use rustc_ast::{self as ast, Expr, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; - -fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> { - cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr)) -} +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; pub fn expand_deriving_debug( cx: &mut ExtCtxt<'_>, @@ -49,11 +45,7 @@ pub fn expand_deriving_debug( trait_def.expand(cx, mitem, item, push) } -/// We use the debug builders to do the heavy lifting here fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { - // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build() - // or fmt.debug_tuple(<name>).field(&<fieldval>)....build() - // based on the "shape". let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), @@ -67,93 +59,130 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); let fmt = substr.nonself_args[0].clone(); - // Special fast path for unit variants. In the common case of an enum that is entirely unit - // variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in - // favor of a lookup table. - if let ast::VariantData::Unit(..) = vdata { - let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); - let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); - let stmts = vec![cx.stmt_expr(expr)]; - let block = cx.block(span, stmts); - return cx.expr_block(block); - } - - let builder = Ident::new(sym::debug_trait_builder, span); - let builder_expr = cx.expr_ident(span, builder); - - let mut stmts = Vec::with_capacity(fields.len() + 2); - let fn_path_finish; - match vdata { + // Struct and tuples are similar enough that we use the same code for both, + // with some extra pieces for structs due to the field names. + let (is_struct, args_per_field) = match vdata { ast::VariantData::Unit(..) => { - cx.span_bug(span, "unit variants should have been handled above"); + // Special fast path for unit variants. + //let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + //return cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); + assert!(fields.is_empty()); + (false, 0) } - ast::VariantData::Tuple(..) => { - // tuple struct/"normal" variant - let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]); - let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]); - let expr = make_mut_borrow(cx, span, expr); - stmts.push(cx.stmt_let(span, false, builder, expr)); - - for field in fields { - // Use double indirection to make sure this works for unsized types - let field = cx.expr_addr_of(field.span, field.self_.clone()); - let field = cx.expr_addr_of(field.span, field); - - let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]); - let expr = - cx.expr_call_global(span, fn_path_field, vec![builder_expr.clone(), field]); - - // Use `let _ = expr;` to avoid triggering the - // unused_results lint. - stmts.push(stmt_let_underscore(cx, span, expr)); - } + ast::VariantData::Tuple(..) => (false, 1), + ast::VariantData::Struct(..) => (true, 2), + }; - fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]); - } - ast::VariantData::Struct(..) => { - // normal struct/struct variant - let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]); - let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]); - let expr = make_mut_borrow(cx, span, expr); - stmts.push(cx.stmt_let(DUMMY_SP, false, builder, expr)); - - for field in fields { + // The number of fields that can be handled without an array. + const CUTOFF: usize = 5; + + if fields.is_empty() { + // Special case for no fields. + let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]) + } else if fields.len() <= CUTOFF { + // Few enough fields that we can use a specific-length method. + let debug = if is_struct { + format!("debug_struct_field{}_finish", fields.len()) + } else { + format!("debug_tuple_field{}_finish", fields.len()) + }; + let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]); + + let mut args = Vec::with_capacity(2 + fields.len() * args_per_field); + args.extend([fmt, name]); + for i in 0..fields.len() { + let field = &fields[i]; + if is_struct { let name = cx.expr_lit( field.span, ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), ); - - // Use double indirection to make sure this works for unsized types - let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]); - let field = cx.expr_addr_of(field.span, field.self_.clone()); - let field = cx.expr_addr_of(field.span, field); - let expr = cx.expr_call_global( - span, - fn_path_field, - vec![builder_expr.clone(), name, field], - ); - stmts.push(stmt_let_underscore(cx, span, expr)); + args.push(name); } - fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]); + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + args.push(field); } - } + cx.expr_call_global(span, fn_path_debug, args) + } else { + // Enough fields that we must use the any-length method. + let mut name_exprs = Vec::with_capacity(fields.len()); + let mut value_exprs = Vec::with_capacity(fields.len()); + + for field in fields { + if is_struct { + name_exprs.push(cx.expr_lit( + field.span, + ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), + )); + } - let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_expr]); + // Use double indirection to make sure this works for unsized types + let value_ref = cx.expr_addr_of(field.span, field.self_.clone()); + value_exprs.push(cx.expr_addr_of(field.span, value_ref)); + } - stmts.push(cx.stmt_expr(expr)); - let block = cx.block(span, stmts); - cx.expr_block(block) -} + // `let names: &'static _ = &["field1", "field2"];` + let names_let = if is_struct { + let lt_static = Some(cx.lifetime_static(span)); + let ty_static_ref = + cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not); + Some(cx.stmt_let_ty( + span, + false, + Ident::new(sym::names, span), + Some(ty_static_ref), + cx.expr_array_ref(span, name_exprs), + )) + } else { + None + }; + + // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];` + let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug])); + let ty_dyn_debug = cx.ty( + span, + ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn), + ); + let ty_slice = cx.ty( + span, + ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)), + ); + let values_let = cx.stmt_let_ty( + span, + false, + Ident::new(sym::values, span), + Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)), + cx.expr_array_ref(span, value_exprs), + ); + + // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or + // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)` + let sym_debug = if is_struct { + sym::debug_struct_fields_finish + } else { + sym::debug_tuple_fields_finish + }; + let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]); + + let mut args = Vec::with_capacity(4); + args.push(fmt); + args.push(name); + if is_struct { + args.push(cx.expr_ident(span, Ident::new(sym::names, span))); + } + args.push(cx.expr_ident(span, Ident::new(sym::values, span))); + let expr = cx.expr_call_global(span, fn_path_debug_internal, args); -fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast::Stmt { - let local = P(ast::Local { - pat: cx.pat_wild(sp), - ty: None, - id: ast::DUMMY_NODE_ID, - kind: LocalKind::Init(expr), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + let mut stmts = Vec::with_capacity(3); + if is_struct { + stmts.push(names_let.unwrap()); + } + stmts.push(values_let); + stmts.push(cx.stmt_expr(expr)); + + cx.expr_block(cx.block(span, stmts)) + } } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 1d892b20729..b39f35a9d40 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -162,14 +162,13 @@ fn decodable_substructure( cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms), ); let lambda = cx.lambda(trait_span, vec![blkarg, variant], result); - let variant_vec = cx.expr_vec(trait_span, variants); - let variant_vec = cx.expr_addr_of(trait_span, variant_vec); + let variant_array_ref = cx.expr_array_ref(trait_span, variants); let fn_read_enum_variant_path: Vec<_> = cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]); let result = cx.expr_call_global( trait_span, fn_read_enum_variant_path, - vec![blkdecoder, variant_vec, lambda], + vec![blkdecoder, variant_array_ref, lambda], ); let fn_read_enum_path: Vec<_> = cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 53369afae27..8347cded2fe 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1126,75 +1126,43 @@ impl<'a> MethodDef<'a> { /// A1, /// A2(i32) /// } - /// - /// // is equivalent to - /// - /// impl PartialEq for A { + /// ``` + /// is equivalent to: + /// ``` + /// impl ::core::cmp::PartialEq for A { + /// #[inline] /// fn eq(&self, other: &A) -> bool { - /// use A::*; - /// match (&*self, &*other) { - /// (&A1, &A1) => true, - /// (&A2(ref self_0), - /// &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)), - /// _ => { - /// let __self_vi = match *self { A1 => 0, A2(..) => 1 }; - /// let __arg_1_vi = match *other { A1 => 0, A2(..) => 1 }; - /// false + /// { + /// 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 /// } /// } /// } /// } /// ``` - /// - /// (Of course `__self_vi` and `__arg_1_vi` are unused for - /// `PartialEq`, and those subcomputations will hopefully be removed - /// as their results are unused. The point of `__self_vi` and - /// `__arg_1_vi` is for `PartialOrd`; see #15503.) - fn expand_enum_method_body<'b>( - &self, - cx: &mut ExtCtxt<'_>, - trait_: &TraitDef<'b>, - enum_def: &'b EnumDef, - type_ident: Ident, - self_args: Vec<P<Expr>>, - nonself_args: &[P<Expr>], - ) -> P<Expr> { - self.build_enum_match_tuple(cx, trait_, enum_def, type_ident, self_args, nonself_args) - } - /// Creates a match for a tuple of all `self_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 self-args, carried in precomputed variables. - - /// ```{.text} - /// let __self0_vi = std::intrinsics::discriminant_value(&self); - /// let __self1_vi = std::intrinsics::discriminant_value(&arg1); - /// let __self2_vi = std::intrinsics::discriminant_value(&arg2); - /// - /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { - /// match (...) { - /// (Variant1, Variant1, ...) => Body1 - /// (Variant2, Variant2, ...) => Body2, - /// ... - /// _ => ::core::intrinsics::unreachable() - /// } - /// } - /// else { - /// ... // catch-all remainder can inspect above variant index values. - /// } - /// ``` - fn build_enum_match_tuple<'b>( + fn expand_enum_method_body<'b>( &self, cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, @@ -1392,37 +1360,32 @@ impl<'a> MethodDef<'a> { // // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving // with three Self args, builds three statements: - // // ``` - // let __self0_vi = std::intrinsics::discriminant_value(&self); - // let __self1_vi = std::intrinsics::discriminant_value(&arg1); - // let __self2_vi = std::intrinsics::discriminant_value(&arg2); + // 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 - // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... + // 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); - - let mut first_ident = None; - for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { + for (i, (&ident, self_arg)) in iter::zip(&vi_idents, &self_args).enumerate() { let self_addr = cx.expr_addr_of(span, self_arg.clone()); let variant_value = deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]); let let_stmt = cx.stmt_let(span, false, ident, variant_value); index_let_stmts.push(let_stmt); - match first_ident { - Some(first) => { - let first_expr = cx.expr_ident(span, first); - let id = cx.expr_ident(span, ident); - let test = cx.expr_binary(span, BinOpKind::Eq, first_expr, id); - discriminant_test = - cx.expr_binary(span, BinOpKind::And, discriminant_test, test) - } - None => { - first_ident = Some(ident); - } + 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) + }; } } @@ -1453,7 +1416,7 @@ impl<'a> MethodDef<'a> { // } // } // else { - // <delegated expression referring to __self0_vi, et al.> + // <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)); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index c678c8cbd15..c1ca089da22 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{Impl, ItemKind, MetaItem}; +use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -193,3 +193,16 @@ fn inject_impl_of_structural_trait( push(Annotatable::Item(newitem)); } + +fn assert_ty_bounds( + cx: &mut ExtCtxt<'_>, + stmts: &mut Vec<ast::Stmt>, + ty: P<ast::Ty>, + span: Span, + assert_path: &[Symbol], +) { + // Generate statement `let _: assert_path<ty>;`. + let span = cx.with_def_site_ctxt(span); + let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); +} diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 10348c4967c..6c2ac343544 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -776,7 +776,7 @@ impl<'a, 'b> Context<'a, 'b> { // First, build up the static array which will become our precompiled // format "string" - let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); + let pieces = self.ecx.expr_array_ref(self.fmtsp, self.str_pieces); // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments // constructor. In general the expressions in this slice might be @@ -849,7 +849,7 @@ impl<'a, 'b> Context<'a, 'b> { fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg)); } - let args_array = self.ecx.expr_vec(self.macsp, fmt_args); + let args_array = self.ecx.expr_array(self.macsp, fmt_args); let args_slice = self.ecx.expr_addr_of( self.macsp, if no_need_for_match { @@ -879,7 +879,7 @@ impl<'a, 'b> Context<'a, 'b> { } else { // Build up the static array which will store our precompiled // nonstandard placeholders, if there are any. - let fmt = self.ecx.expr_vec_slice(self.macsp, self.pieces); + let fmt = self.ecx.expr_array_ref(self.macsp, self.pieces); let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]); let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new()); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 03159d43950..5cfda33491d 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -317,7 +317,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> { proc_macro_ty_method_path(cx, custom_derive), vec![ cx.expr_str(span, cd.trait_name), - cx.expr_vec_slice( + cx.expr_array_ref( span, cd.attrs.iter().map(|&s| cx.expr_str(span, s)).collect::<Vec<_>>(), ), @@ -362,7 +362,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> { ast::Mutability::Not, ), ast::Mutability::Not, - cx.expr_vec_slice(span, decls), + cx.expr_array_ref(span, decls), ) .map(|mut i| { let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index a08495f7671..e2a6e77ab44 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -352,7 +352,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> { debug!("building test vector from {} tests", cx.test_cases.len()); let ecx = &cx.ext_cx; - ecx.expr_vec_slice( + ecx.expr_array_ref( sp, cx.test_cases .iter() diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index c41a41980eb..8c1e865762c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1064,11 +1064,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { dst: &'ll Value, cmp: &'ll Value, src: &'ll Value, - order: rustc_codegen_ssa::common::AtomicOrdering, + mut order: rustc_codegen_ssa::common::AtomicOrdering, failure_order: rustc_codegen_ssa::common::AtomicOrdering, weak: bool, ) -> &'ll Value { let weak = if weak { llvm::True } else { llvm::False }; + if llvm_util::get_version() < (13, 0, 0) { + use rustc_codegen_ssa::common::AtomicOrdering::*; + // Older llvm has the pre-C++17 restriction on + // success and failure memory ordering, + // requiring the former to be at least as strong as the latter. + // So, for llvm 12, we upgrade the success ordering to a stronger + // one if necessary. + match (order, failure_order) { + (Relaxed, Acquire) => order = Acquire, + (Release, Acquire) => order = AcquireRelease, + (_, SequentiallyConsistent) => order = SequentiallyConsistent, + _ => {} + } + } unsafe { llvm::LLVMRustBuildAtomicCmpXchg( self.llbuilder, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 6c30923bc3d..4be3ae11e4e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,6 +6,7 @@ #![feature(associated_type_bounds)] #![feature(strict_provenance)] #![feature(int_roundings)] +#![feature(if_let_guard)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 0ed4c3f1d94..7f14b95317b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -376,32 +376,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // This requires that atomic intrinsics follow a specific naming pattern: - // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst - name if name_str.starts_with("atomic_") => { + // "atomic_<operation>[_<ordering>]" + name if let Some(atomic) = name_str.strip_prefix("atomic_") => { use crate::common::AtomicOrdering::*; use crate::common::{AtomicRmwBinOp, SynchronizationScope}; - let split: Vec<_> = name_str.split('_').collect(); - - let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; - let (order, failorder) = match split.len() { - 2 => (SequentiallyConsistent, SequentiallyConsistent), - 3 => match split[2] { - "unordered" => (Unordered, Unordered), - "relaxed" => (Relaxed, Relaxed), - "acq" => (Acquire, Acquire), - "rel" => (Release, Relaxed), - "acqrel" => (AcquireRelease, Acquire), - "failrelaxed" if is_cxchg => (SequentiallyConsistent, Relaxed), - "failacq" if is_cxchg => (SequentiallyConsistent, Acquire), - _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), - }, - 4 => match (split[2], split[3]) { - ("acq", "failrelaxed") if is_cxchg => (Acquire, Relaxed), - ("acqrel", "failrelaxed") if is_cxchg => (AcquireRelease, Relaxed), - _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), - }, - _ => bx.sess().fatal("Atomic intrinsic not in correct format"), + let Some((instruction, ordering)) = atomic.split_once('_') else { + bx.sess().fatal("Atomic intrinsic missing memory ordering"); + }; + + let parse_ordering = |bx: &Bx, s| match s { + "unordered" => Unordered, + "relaxed" => Relaxed, + "acquire" => Acquire, + "release" => Release, + "acqrel" => AcquireRelease, + "seqcst" => SequentiallyConsistent, + _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), }; let invalid_monomorphization = |ty| { @@ -416,11 +407,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); }; - match split[1] { + match instruction { "cxchg" | "cxchgweak" => { + let Some((success, failure)) = ordering.split_once('_') else { + bx.sess().fatal("Atomic compare-exchange intrinsic missing failure memory ordering"); + }; let ty = substs.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { - let weak = split[1] == "cxchgweak"; + let weak = instruction == "cxchgweak"; let mut dst = args[0].immediate(); let mut cmp = args[1].immediate(); let mut src = args[2].immediate(); @@ -432,7 +426,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cmp = bx.ptrtoint(cmp, bx.type_isize()); src = bx.ptrtoint(src, bx.type_isize()); } - let pair = bx.atomic_cmpxchg(dst, cmp, src, order, failorder, weak); + let pair = bx.atomic_cmpxchg(dst, cmp, src, parse_ordering(bx, success), parse_ordering(bx, failure), weak); let val = bx.extract_value(pair, 0); let success = bx.extract_value(pair, 1); let val = bx.from_immediate(val); @@ -460,11 +454,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llty = bx.type_isize(); let ptr_llty = bx.type_ptr_to(llty); source = bx.pointercast(source, ptr_llty); - let result = bx.atomic_load(llty, source, order, size); + let result = bx.atomic_load(llty, source, parse_ordering(bx, ordering), size); // ... and then cast the result back to a pointer bx.inttoptr(result, bx.backend_type(layout)) } else { - bx.atomic_load(bx.backend_type(layout), source, order, size) + bx.atomic_load(bx.backend_type(layout), source, parse_ordering(bx, ordering), size) } } else { return invalid_monomorphization(ty); @@ -484,7 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ptr = bx.pointercast(ptr, ptr_llty); val = bx.ptrtoint(val, bx.type_isize()); } - bx.atomic_store(val, ptr, order, size); + bx.atomic_store(val, ptr, parse_ordering(bx, ordering), size); return; } else { return invalid_monomorphization(ty); @@ -492,12 +486,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } "fence" => { - bx.atomic_fence(order, SynchronizationScope::CrossThread); + bx.atomic_fence(parse_ordering(bx, ordering), SynchronizationScope::CrossThread); return; } "singlethreadfence" => { - bx.atomic_fence(order, SynchronizationScope::SingleThread); + bx.atomic_fence(parse_ordering(bx, ordering), SynchronizationScope::SingleThread); return; } @@ -531,7 +525,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ptr = bx.pointercast(ptr, ptr_llty); val = bx.ptrtoint(val, bx.type_isize()); } - bx.atomic_rmw(atom_op, ptr, val, order) + bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering)) } else { return invalid_monomorphization(ty); } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index d46f2f38d3a..d5e68dbd5b7 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -56,15 +56,14 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> { } } -/// Used by `get_size_and_align` to indicate whether the allocation needs to be live. -#[derive(Debug, Copy, Clone)] -pub enum AllocCheck { - /// Allocation must be live and not a function pointer. - Dereferenceable, - /// Allocations needs to be live, but may be a function pointer. - Live, - /// Allocation may be dead. - MaybeDead, +/// The return value of `get_alloc_info` indicates the "kind" of the allocation. +pub enum AllocKind { + /// A regular live data allocation. + LiveData, + /// A function allocation (that fn ptrs point to). + Function, + /// A dead allocation. + Dead, } /// The value of a function pointer. @@ -360,8 +359,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { align, CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, tag| { - let (size, align) = - self.get_alloc_size_and_align(alloc_id, AllocCheck::Dereferenceable)?; + let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; Ok((size, align, (alloc_id, offset, tag))) }, ) @@ -379,15 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| { - let check = match msg { - CheckInAllocMsg::DerefTest | CheckInAllocMsg::MemoryAccessTest => { - AllocCheck::Dereferenceable - } - CheckInAllocMsg::PointerArithmeticTest - | CheckInAllocMsg::OffsetFromTest - | CheckInAllocMsg::InboundsTest => AllocCheck::Live, - }; - let (size, align) = self.get_alloc_size_and_align(alloc_id, check)?; + let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; Ok((size, align, ())) })?; Ok(()) @@ -655,30 +645,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Obtain the size and alignment of an allocation, even if that allocation has /// been deallocated. - /// - /// If `liveness` is `AllocCheck::MaybeDead`, this function always returns `Ok`. - pub fn get_alloc_size_and_align( - &self, - id: AllocId, - liveness: AllocCheck, - ) -> InterpResult<'tcx, (Size, Align)> { + pub fn get_alloc_info(&self, id: AllocId) -> (Size, Align, AllocKind) { // # Regular allocations // Don't use `self.get_raw` here as that will // a) cause cycles in case `id` refers to a static // b) duplicate a global's allocation in miri if let Some((_, alloc)) = self.memory.alloc_map.get(id) { - return Ok((alloc.size(), alloc.align)); + return (alloc.size(), alloc.align, AllocKind::LiveData); } // # Function pointers // (both global from `alloc_map` and local from `extra_fn_ptr_map`) if self.get_fn_alloc(id).is_some() { - return if let AllocCheck::Dereferenceable = liveness { - // The caller requested no function pointers. - throw_ub!(DerefFunctionPointer(id)) - } else { - Ok((Size::ZERO, Align::ONE)) - }; + return (Size::ZERO, Align::ONE, AllocKind::Function); } // # Statics @@ -690,32 +669,38 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Use size and align of the type. let ty = self.tcx.type_of(did); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); - Ok((layout.size, layout.align.abi)) + (layout.size, layout.align.abi, AllocKind::LiveData) } Some(GlobalAlloc::Memory(alloc)) => { // Need to duplicate the logic here, because the global allocations have // different associated types than the interpreter-local ones. let alloc = alloc.inner(); - Ok((alloc.size(), alloc.align)) + (alloc.size(), alloc.align, AllocKind::LiveData) } Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"), // The rest must be dead. None => { - if let AllocCheck::MaybeDead = liveness { - // Deallocated pointers are allowed, we should be able to find - // them in the map. - Ok(*self - .memory - .dead_alloc_map - .get(&id) - .expect("deallocated pointers should all be recorded in `dead_alloc_map`")) - } else { - throw_ub!(PointerUseAfterFree(id)) - } + // Deallocated pointers are allowed, we should be able to find + // them in the map. + let (size, align) = *self + .memory + .dead_alloc_map + .get(&id) + .expect("deallocated pointers should all be recorded in `dead_alloc_map`"); + (size, align, AllocKind::Dead) } } } + /// Obtain the size and alignment of a live allocation. + pub fn get_live_alloc_size_and_align(&self, id: AllocId) -> InterpResult<'tcx, (Size, Align)> { + let (size, align, kind) = self.get_alloc_info(id); + if matches!(kind, AllocKind::Dead) { + throw_ub!(PointerUseAfterFree(id)) + } + Ok((size, align)) + } + fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> { if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) { Some(FnVal::Other(*extra)) @@ -1187,9 +1172,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr = self.scalar_to_ptr(scalar)?; match self.ptr_try_get_alloc_id(ptr) { Ok((alloc_id, offset, _)) => { - let (size, _align) = self - .get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead) - .expect("alloc info with MaybeDead cannot fail"); + let (size, _align, _kind) = self.get_alloc_info(alloc_id); // If the pointer is out-of-bounds, it may be null. // Note that one-past-the-end (offset == size) is still inbounds, and never null. offset > size diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2b73ad568e0..92f0a7498e3 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -23,7 +23,7 @@ pub use self::eval_context::{ }; pub use self::intern::{intern_const_alloc_recursive, InternKind}; pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; -pub use self::memory::{AllocCheck, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; +pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; pub use self::validity::{CtfeValidationMode, RefTracking}; diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 88ff33b4d09..d8b26f9840b 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -826,6 +826,24 @@ cfg_if! { } } } + } else if #[cfg(target_os = "macos")] { + pub fn get_resident_set_size() -> Option<usize> { + use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO}; + use std::mem; + const PROC_TASKINFO_SIZE: c_int = mem::size_of::<proc_taskinfo>() as c_int; + + unsafe { + let mut info: proc_taskinfo = mem::zeroed(); + let info_ptr = &mut info as *mut proc_taskinfo as *mut c_void; + let pid = getpid() as c_int; + let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info_ptr, PROC_TASKINFO_SIZE); + if ret == PROC_TASKINFO_SIZE { + Some(info.pti_resident_size as usize) + } else { + None + } + } + } } else if #[cfg(unix)] { pub fn get_resident_set_size() -> Option<usize> { let field = 1; diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index feb82cb0938..4437c0b1b69 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -21,6 +21,7 @@ use crate::owning_ref::{Erased, OwningRef}; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut}; +use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; pub use std::sync::atomic::Ordering; pub use std::sync::atomic::Ordering::SeqCst; @@ -41,7 +42,6 @@ cfg_if! { } use std::ops::Add; - use std::panic::{resume_unwind, catch_unwind, AssertUnwindSafe}; /// This is a single threaded variant of `AtomicU64`, `AtomicUsize`, etc. /// It has explicit ordering arguments and is only intended for use with @@ -339,7 +339,10 @@ cfg_if! { t: T, for_each: impl Fn(T::Item) + Sync + Send, ) { - t.into_par_iter().for_each(for_each) + let ps: Vec<_> = t.into_par_iter().map(|i| catch_unwind(AssertUnwindSafe(|| for_each(i)))).collect(); + ps.into_iter().for_each(|p| if let Err(panic) = p { + resume_unwind(panic) + }); } pub type MetadataRef = OwningRef<Box<dyn Erased + Send + Sync>, [u8]>; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index caa92e74808..3096af90d47 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -932,7 +932,7 @@ fn describe_codegen_flags() { print_flag_list("-C", config::CG_OPTIONS); } -fn print_flag_list<T>( +pub fn print_flag_list<T>( cmdline_opt: &str, flag_list: &[(&'static str, T, &'static str, &'static str)], ) { diff --git a/compiler/rustc_error_codes/src/error_codes/E0093.md b/compiler/rustc_error_codes/src/error_codes/E0093.md index 6d58e50ec88..b1683cf4fc4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0093.md +++ b/compiler/rustc_error_codes/src/error_codes/E0093.md @@ -24,12 +24,12 @@ functions are defined in `compiler/rustc_codegen_llvm/src/intrinsic.rs` and in #![feature(intrinsics)] extern "rust-intrinsic" { - fn atomic_fence(); // ok! + fn atomic_fence_seqcst(); // ok! } fn main() { unsafe { - atomic_fence(); + atomic_fence_seqcst(); } } ``` diff --git a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl index 1d3e33c8185..4a42d52f710 100644 --- a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl +++ b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl @@ -1,5 +1,5 @@ -builtin-macros-requires-cfg-pattern = +builtin_macros-requires-cfg-pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required -builtin-macros-expected-one-cfg-pattern = expected 1 cfg-pattern +builtin_macros-expected-one-cfg-pattern = expected 1 cfg-pattern diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl new file mode 100644 index 00000000000..2b0778f48ca --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl @@ -0,0 +1,12 @@ +privacy-field-is-private = field `{$field_name}` of {$variant_descr} `{$def_path_str}` is private +privacy-field-is-private-is-update-syntax-label = field `{$field_name}` is private +privacy-field-is-private-label = private field + +privacy-item-is-private = {$kind} `{$descr}` is private + .label = private {$kind} +privacy-unnamed-item-is-private = {$kind} is private + .label = private {$kind} + +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} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 7211c054326..90eb5ef5446 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -32,6 +32,7 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { parser => "../locales/en-US/parser.ftl", + privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", builtin_macros => "../locales/en-US/builtin_macros.ftl", } @@ -258,18 +259,6 @@ pub enum SubdiagnosticMessage { FluentAttr(FluentId), } -impl SubdiagnosticMessage { - /// Create a `SubdiagnosticMessage` for the provided Fluent attribute. - pub fn attr(id: impl Into<FluentId>) -> Self { - SubdiagnosticMessage::FluentAttr(id.into()) - } - - /// Create a `SubdiagnosticMessage` for the provided Fluent identifier. - pub fn message(id: impl Into<FluentId>) -> Self { - SubdiagnosticMessage::FluentIdentifier(id.into()) - } -} - /// `From` impl that enables existing diagnostic calls to functions which now take /// `impl Into<SubdiagnosticMessage>` to continue to work as before. impl<S: Into<String>> From<S> for SubdiagnosticMessage { @@ -332,11 +321,6 @@ impl DiagnosticMessage { _ => panic!("expected non-translatable diagnostic message"), } } - - /// Create a `DiagnosticMessage` for the provided Fluent identifier. - pub fn new(id: impl Into<FluentId>) -> Self { - DiagnosticMessage::FluentIdentifier(id.into(), None) - } } /// `From` impl that enables existing diagnostic calls to functions which now take @@ -347,6 +331,27 @@ impl<S: Into<String>> From<S> for DiagnosticMessage { } } +/// Translating *into* a subdiagnostic message from a diagnostic message is a little strange - but +/// the subdiagnostic functions (e.g. `span_label`) take a `SubdiagnosticMessage` and the +/// subdiagnostic derive refers to typed identifiers that are `DiagnosticMessage`s, so need to be +/// able to convert between these, as much as they'll be converted back into `DiagnosticMessage` +/// using `with_subdiagnostic_message` eventually. Don't use this other than for the derive. +impl Into<SubdiagnosticMessage> for DiagnosticMessage { + fn into(self) -> SubdiagnosticMessage { + match self { + DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s), + DiagnosticMessage::FluentIdentifier(id, None) => { + SubdiagnosticMessage::FluentIdentifier(id) + } + // There isn't really a sensible behaviour for this because it loses information but + // this is the most sensible of the behaviours. + DiagnosticMessage::FluentIdentifier(_, Some(attr)) => { + SubdiagnosticMessage::FluentAttr(attr) + } + } + } +} + /// A span together with some additional data. #[derive(Clone, Debug)] pub struct SpanLabel { diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a4cbc73978d..8b2a995f1c5 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -656,11 +656,6 @@ impl Emitter for SilentEmitter { } } -/// Maximum number of lines we will print for a multiline suggestion; arbitrary. -/// -/// This should be replaced with a more involved mechanism to output multiline suggestions that -/// more closely mimics the regular diagnostic output, where irrelevant code lines are elided. -pub const MAX_SUGGESTION_HIGHLIGHT_LINES: usize = 6; /// Maximum number of suggestions to be shown /// /// Arbitrary, but taken from trait import suggestion limit diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 245719bff12..1e57d66dd9f 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1077,6 +1077,7 @@ impl<'a> ExtCtxt<'a> { self.current_expansion.id.expansion_cause() } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -1101,9 +1102,11 @@ impl<'a> ExtCtxt<'a> { /// /// Compilation will be stopped in the near future (at the end of /// the macro expansion phase). + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_err(sp, msg); } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e73c31c98fe..1694a8865dd 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> { P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None }) } + pub fn ty_infer(&self, span: Span) -> P<ast::Ty> { + self.ty(span, ast::TyKind::Infer) + } + pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> { self.ty(path.span, ast::TyKind::Path(None, path)) } @@ -140,11 +144,26 @@ impl<'a> ExtCtxt<'a> { ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) } } + pub fn lifetime_static(&self, span: Span) -> ast::Lifetime { + self.lifetime(span, Ident::new(kw::StaticLifetime, span)) + } + pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } } pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt { + self.stmt_let_ty(sp, mutbl, ident, None, ex) + } + + pub fn stmt_let_ty( + &self, + sp: Span, + mutbl: bool, + ident: Ident, + ty: Option<P<ast::Ty>>, + ex: P<ast::Expr>, + ) -> ast::Stmt { let pat = if mutbl { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); self.pat_ident_binding_mode(sp, ident, binding_mode) @@ -153,7 +172,7 @@ impl<'a> ExtCtxt<'a> { }; let local = P(ast::Local { pat, - ty: None, + ty, id: ast::DUMMY_NODE_ID, kind: LocalKind::Init(ex), span: sp, @@ -315,12 +334,16 @@ impl<'a> ExtCtxt<'a> { self.expr_lit(sp, ast::LitKind::Bool(value)) } - pub fn expr_vec(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> { + /// `[expr1, expr2, ...]` + pub fn expr_array(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> { self.expr(sp, ast::ExprKind::Array(exprs)) } - pub fn expr_vec_slice(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> { - self.expr_addr_of(sp, self.expr_vec(sp, exprs)) + + /// `&[expr1, expr2, ...]` + pub fn expr_array_ref(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> { + self.expr_addr_of(sp, self.expr_array(sp, exprs)) } + pub fn expr_str(&self, sp: Span, s: Symbol) -> P<ast::Expr> { self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked)) } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 86ff110eec1..c18147592dc 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -9,6 +9,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] +#![feature(rustc_attrs)] #![feature(try_blocks)] #![recursion_limit = "256"] diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1e4193a5a16..0e909e2f982 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,7 +2,7 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; +use rustc_ast::tokenstream::{self, Spacing::*, TokenStream}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; @@ -14,8 +14,8 @@ use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, kw, sym, Symbol}; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; -use pm::bridge::{server, TokenTree}; -use pm::{Delimiter, Level, LineColumn, Spacing}; +use pm::bridge::{server, DelimSpan, ExpnGlobals, Group, Punct, TokenTree}; +use pm::{Delimiter, Level, LineColumn}; use std::ops::Bound; use std::{ascii, panic}; @@ -49,158 +49,170 @@ impl ToInternal<token::Delimiter> for Delimiter { } } -impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> - for TokenTree<Group, Punct, Ident, Literal> +impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> + for Vec<TokenTree<TokenStream, Span, Ident, Literal>> { - fn from_internal( - ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>), - ) -> Self { + fn from_internal((stream, rustc): (TokenStream, &mut Rustc<'_, '_>)) -> Self { use rustc_ast::token::*; - let joint = spacing == Joint; - let Token { kind, span } = match tree { - tokenstream::TokenTree::Delimited(span, delim, tts) => { - let delimiter = pm::Delimiter::from_internal(delim); - return TokenTree::Group(Group { delimiter, stream: tts, span, flatten: false }); - } - tokenstream::TokenTree::Token(token) => token, - }; + // Estimate the capacity as `stream.len()` rounded up to the next power + // of two to limit the number of required reallocations. + let mut trees = Vec::with_capacity(stream.len().next_power_of_two()); + let mut cursor = stream.into_trees(); - macro_rules! tt { - ($ty:ident { $($field:ident $(: $value:expr)*),+ $(,)? }) => ( - TokenTree::$ty(self::$ty { - $($field $(: $value)*,)+ - span, - }) - ); - ($ty:ident::$method:ident($($value:expr),*)) => ( - TokenTree::$ty(self::$ty::$method($($value,)* span)) - ); - } - macro_rules! op { - ($a:expr) => { - tt!(Punct::new($a, joint)) + while let Some((tree, spacing)) = cursor.next_with_spacing() { + let joint = spacing == Joint; + let Token { kind, span } = match tree { + tokenstream::TokenTree::Delimited(span, delim, tts) => { + let delimiter = pm::Delimiter::from_internal(delim); + trees.push(TokenTree::Group(Group { + delimiter, + stream: Some(tts), + span: DelimSpan { + open: span.open, + close: span.close, + entire: span.entire(), + }, + })); + continue; + } + tokenstream::TokenTree::Token(token) => token, }; - ($a:expr, $b:expr) => {{ - stack.push(tt!(Punct::new($b, joint))); - tt!(Punct::new($a, true)) - }}; - ($a:expr, $b:expr, $c:expr) => {{ - stack.push(tt!(Punct::new($c, joint))); - stack.push(tt!(Punct::new($b, true))); - tt!(Punct::new($a, true)) - }}; - } - match kind { - Eq => op!('='), - Lt => op!('<'), - Le => op!('<', '='), - EqEq => op!('=', '='), - Ne => op!('!', '='), - Ge => op!('>', '='), - Gt => op!('>'), - AndAnd => op!('&', '&'), - OrOr => op!('|', '|'), - Not => op!('!'), - Tilde => op!('~'), - BinOp(Plus) => op!('+'), - BinOp(Minus) => op!('-'), - BinOp(Star) => op!('*'), - BinOp(Slash) => op!('/'), - BinOp(Percent) => op!('%'), - BinOp(Caret) => op!('^'), - BinOp(And) => op!('&'), - BinOp(Or) => op!('|'), - BinOp(Shl) => op!('<', '<'), - BinOp(Shr) => op!('>', '>'), - BinOpEq(Plus) => op!('+', '='), - BinOpEq(Minus) => op!('-', '='), - BinOpEq(Star) => op!('*', '='), - BinOpEq(Slash) => op!('/', '='), - BinOpEq(Percent) => op!('%', '='), - BinOpEq(Caret) => op!('^', '='), - BinOpEq(And) => op!('&', '='), - BinOpEq(Or) => op!('|', '='), - BinOpEq(Shl) => op!('<', '<', '='), - BinOpEq(Shr) => op!('>', '>', '='), - At => op!('@'), - Dot => op!('.'), - DotDot => op!('.', '.'), - DotDotDot => op!('.', '.', '.'), - DotDotEq => op!('.', '.', '='), - Comma => op!(','), - Semi => op!(';'), - Colon => op!(':'), - ModSep => op!(':', ':'), - RArrow => op!('-', '>'), - LArrow => op!('<', '-'), - FatArrow => op!('=', '>'), - Pound => op!('#'), - Dollar => op!('$'), - Question => op!('?'), - SingleQuote => op!('\''), - - Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)), - Lifetime(name) => { - let ident = symbol::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(rustc.sess(), ident.name, false))); - tt!(Punct::new('\'', true)) - } - Literal(lit) => tt!(Literal { lit }), - DocComment(_, attr_style, data) => { - let mut escaped = String::new(); - for ch in data.as_str().chars() { - escaped.extend(ch.escape_debug()); - } - let stream = [ - Ident(sym::doc, false), - Eq, - TokenKind::lit(token::Str, Symbol::intern(&escaped), None), - ] - .into_iter() - .map(|kind| tokenstream::TokenTree::token(kind, span)) - .collect(); - stack.push(TokenTree::Group(Group { - delimiter: pm::Delimiter::Bracket, - stream, - span: DelimSpan::from_single(span), - flatten: false, + let mut op = |s: &str| { + assert!(s.is_ascii()); + trees.extend(s.as_bytes().iter().enumerate().map(|(idx, &ch)| { + TokenTree::Punct(Punct { ch, joint: joint || idx != s.len() - 1, span }) })); - if attr_style == ast::AttrStyle::Inner { - stack.push(tt!(Punct::new('!', false))); + }; + + match kind { + Eq => op("="), + Lt => op("<"), + Le => op("<="), + EqEq => op("=="), + Ne => op("!="), + Ge => op(">="), + Gt => op(">"), + AndAnd => op("&&"), + OrOr => op("||"), + Not => op("!"), + Tilde => op("~"), + BinOp(Plus) => op("+"), + BinOp(Minus) => op("-"), + BinOp(Star) => op("*"), + BinOp(Slash) => op("/"), + BinOp(Percent) => op("%"), + BinOp(Caret) => op("^"), + BinOp(And) => op("&"), + BinOp(Or) => op("|"), + BinOp(Shl) => op("<<"), + BinOp(Shr) => op(">>"), + BinOpEq(Plus) => op("+="), + BinOpEq(Minus) => op("-="), + BinOpEq(Star) => op("*="), + BinOpEq(Slash) => op("/="), + BinOpEq(Percent) => op("%="), + BinOpEq(Caret) => op("^="), + BinOpEq(And) => op("&="), + BinOpEq(Or) => op("|="), + BinOpEq(Shl) => op("<<="), + BinOpEq(Shr) => op(">>="), + At => op("@"), + Dot => op("."), + DotDot => op(".."), + DotDotDot => op("..."), + DotDotEq => op("..="), + Comma => op(","), + Semi => op(";"), + Colon => op(":"), + ModSep => op("::"), + RArrow => op("->"), + LArrow => op("<-"), + FatArrow => op("=>"), + Pound => op("#"), + Dollar => op("$"), + 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))), + 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)), + ]); + } + Literal(lit) => trees.push(TokenTree::Literal(self::Literal { lit, span })), + DocComment(_, attr_style, data) => { + let mut escaped = String::new(); + for ch in data.as_str().chars() { + escaped.extend(ch.escape_debug()); + } + let stream = [ + Ident(sym::doc, false), + Eq, + TokenKind::lit(token::Str, Symbol::intern(&escaped), None), + ] + .into_iter() + .map(|kind| tokenstream::TokenTree::token(kind, span)) + .collect(); + trees.push(TokenTree::Punct(Punct { ch: b'#', joint: false, span })); + if attr_style == ast::AttrStyle::Inner { + trees.push(TokenTree::Punct(Punct { ch: b'!', joint: false, span })); + } + trees.push(TokenTree::Group(Group { + delimiter: pm::Delimiter::Bracket, + stream: Some(stream), + span: DelimSpan::from_single(span), + })); } - tt!(Punct::new('#', false)) - } - Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => { - TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span)) - } - Interpolated(nt) => { - TokenTree::Group(Group { - delimiter: pm::Delimiter::None, - stream: TokenStream::from_nonterminal_ast(&nt), - span: DelimSpan::from_single(span), - flatten: crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()), - }) - } + Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => { + trees.push(TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span))) + } - OpenDelim(..) | CloseDelim(..) => unreachable!(), - Eof => unreachable!(), + Interpolated(nt) => { + let stream = TokenStream::from_nonterminal_ast(&nt); + // A hack used to pass AST fragments to attribute and derive + // macros as a single nonterminal token instead of a token + // stream. Such token needs to be "unwrapped" and not + // represented as a delimited group. + // FIXME: It needs to be removed, but there are some + // compatibility issues (see #73345). + if crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.sess()) { + trees.extend(Self::from_internal((stream, rustc))); + } else { + trees.push(TokenTree::Group(Group { + delimiter: pm::Delimiter::None, + stream: Some(stream), + span: DelimSpan::from_single(span), + })) + } + } + + OpenDelim(..) | CloseDelim(..) => unreachable!(), + Eof => unreachable!(), + } } + trees } } -impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { +impl ToInternal<TokenStream> for TokenTree<TokenStream, Span, Ident, Literal> { fn to_internal(self) -> TokenStream { use rustc_ast::token::*; let (ch, joint, span) = match self { TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), - TokenTree::Group(Group { delimiter, stream, span, .. }) => { - return tokenstream::TokenTree::Delimited(span, delimiter.to_internal(), stream) - .into(); + TokenTree::Group(Group { delimiter, stream, span: DelimSpan { open, close, .. } }) => { + return tokenstream::TokenTree::Delimited( + tokenstream::DelimSpan { open, close }, + delimiter.to_internal(), + stream.unwrap_or_default(), + ) + .into(); } TokenTree::Ident(self::Ident { sym, is_raw, span }) => { return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); @@ -233,28 +245,28 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { }; let kind = match ch { - '=' => Eq, - '<' => Lt, - '>' => Gt, - '!' => Not, - '~' => Tilde, - '+' => BinOp(Plus), - '-' => BinOp(Minus), - '*' => BinOp(Star), - '/' => BinOp(Slash), - '%' => BinOp(Percent), - '^' => BinOp(Caret), - '&' => BinOp(And), - '|' => BinOp(Or), - '@' => At, - '.' => Dot, - ',' => Comma, - ';' => Semi, - ':' => Colon, - '#' => Pound, - '$' => Dollar, - '?' => Question, - '\'' => SingleQuote, + b'=' => Eq, + b'<' => Lt, + b'>' => Gt, + b'!' => Not, + b'~' => Tilde, + b'+' => BinOp(Plus), + b'-' => BinOp(Minus), + b'*' => BinOp(Star), + b'/' => BinOp(Slash), + b'%' => BinOp(Percent), + b'^' => BinOp(Caret), + b'&' => BinOp(And), + b'|' => BinOp(Or), + b'@' => At, + b'.' => Dot, + b',' => Comma, + b';' => Semi, + b':' => Colon, + b'#' => Pound, + b'$' => Dollar, + b'?' => Question, + b'\'' => SingleQuote, _ => unreachable!(), }; @@ -277,38 +289,6 @@ impl ToInternal<rustc_errors::Level> for Level { pub struct FreeFunctions; -#[derive(Clone)] -pub struct Group { - delimiter: Delimiter, - stream: TokenStream, - span: DelimSpan, - /// A hack used to pass AST fragments to attribute and derive macros - /// as a single nonterminal token instead of a token stream. - /// FIXME: It needs to be removed, but there are some compatibility issues (see #73345). - flatten: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Punct { - ch: char, - // NB. not using `Spacing` here because it doesn't implement `Hash`. - joint: bool, - span: Span, -} - -impl Punct { - fn new(ch: char, joint: bool, span: Span) -> Punct { - const LEGAL_CHARS: &[char] = &[ - '=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', '&', '|', '@', '.', ',', ';', - ':', '#', '$', '?', '\'', - ]; - if !LEGAL_CHARS.contains(&ch) { - panic!("unsupported character `{:?}`", ch) - } - Punct { ch, joint, span } - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Ident { sym: Symbol, @@ -370,15 +350,13 @@ impl<'a, 'b> Rustc<'a, 'b> { } fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal { - Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) } + Literal { lit: token::Lit::new(kind, symbol, suffix), span: self.call_site } } } impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; - type Group = Group; - type Punct = Punct; type Ident = Ident; type Literal = Literal; type SourceFile = Lrc<SourceFile>; @@ -471,7 +449,7 @@ impl server::TokenStream for Rustc<'_, '_> { fn from_token_tree( &mut self, - tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>, + tree: TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>, ) -> Self::TokenStream { tree.to_internal() } @@ -479,7 +457,7 @@ impl server::TokenStream for Rustc<'_, '_> { fn concat_trees( &mut self, base: Option<Self::TokenStream>, - trees: Vec<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>>, + trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>>, ) -> Self::TokenStream { let mut builder = tokenstream::TokenStreamBuilder::new(); if let Some(base) = base { @@ -509,93 +487,8 @@ impl server::TokenStream for Rustc<'_, '_> { fn into_trees( &mut self, stream: Self::TokenStream, - ) -> Vec<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> { - // FIXME: This is a raw port of the previous approach (which had a - // `TokenStreamIter` server-side object with a single `next` method), - // and can probably be optimized (for bulk conversion). - let mut cursor = stream.into_trees(); - let mut stack = Vec::new(); - let mut tts = Vec::new(); - loop { - let next = stack.pop().or_else(|| { - let next = cursor.next_with_spacing()?; - Some(TokenTree::from_internal((next, &mut stack, self))) - }); - match next { - Some(TokenTree::Group(group)) => { - // A hack used to pass AST fragments to attribute and derive - // macros as a single nonterminal token instead of a token - // stream. Such token needs to be "unwrapped" and not - // represented as a delimited group. - // FIXME: It needs to be removed, but there are some - // compatibility issues (see #73345). - if group.flatten { - tts.append(&mut self.into_trees(group.stream)); - } else { - tts.push(TokenTree::Group(group)); - } - } - Some(tt) => tts.push(tt), - None => return tts, - } - } - } -} - -impl server::Group for Rustc<'_, '_> { - fn new(&mut self, delimiter: Delimiter, stream: Option<Self::TokenStream>) -> Self::Group { - Group { - delimiter, - stream: stream.unwrap_or_default(), - span: DelimSpan::from_single(server::Span::call_site(self)), - flatten: false, - } - } - - fn delimiter(&mut self, group: &Self::Group) -> Delimiter { - group.delimiter - } - - fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { - group.stream.clone() - } - - fn span(&mut self, group: &Self::Group) -> Self::Span { - group.span.entire() - } - - fn span_open(&mut self, group: &Self::Group) -> Self::Span { - group.span.open - } - - fn span_close(&mut self, group: &Self::Group) -> Self::Span { - group.span.close - } - - fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { - group.span = DelimSpan::from_single(span); - } -} - -impl server::Punct for Rustc<'_, '_> { - fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { - Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) - } - - fn as_char(&mut self, punct: Self::Punct) -> char { - punct.ch - } - - fn spacing(&mut self, punct: Self::Punct) -> Spacing { - if punct.joint { Spacing::Joint } else { Spacing::Alone } - } - - fn span(&mut self, punct: Self::Punct) -> Self::Span { - punct.span - } - - fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { - Punct { span, ..punct } + ) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>> { + FromInternal::from_internal((stream, self)) } } @@ -829,18 +722,6 @@ impl server::Span for Rustc<'_, '_> { } } - fn def_site(&mut self) -> Self::Span { - self.def_site - } - - fn call_site(&mut self) -> Self::Span { - self.call_site - } - - fn mixed_site(&mut self) -> Self::Span { - self.mixed_site - } - fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { self.sess().source_map().lookup_char_pos(span.lo()).file } @@ -926,3 +807,13 @@ impl server::Span for Rustc<'_, '_> { }) } } + +impl server::Server for Rustc<'_, '_> { + fn globals(&mut self) -> ExpnGlobals<Self::Span> { + ExpnGlobals { + def_site: self.def_site, + call_site: self.call_site, + mixed_site: self.mixed_site, + } + } +} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index e68274e2ad9..384a0f9c225 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -65,7 +65,6 @@ //! example generator inference, and possibly also HIR borrowck. use crate::hir::*; -use crate::itemlikevisit::ParItemLikeVisitor; use rustc_ast::walk_list; use rustc_ast::{Attribute, Label}; use rustc_span::symbol::{Ident, Symbol}; @@ -76,29 +75,6 @@ pub trait IntoVisitor<'hir> { fn into_visitor(&self) -> Self::Visitor; } -pub struct ParDeepVisitor<V>(pub V); - -impl<'hir, V> ParItemLikeVisitor<'hir> for ParDeepVisitor<V> -where - V: IntoVisitor<'hir>, -{ - fn visit_item(&self, item: &'hir Item<'hir>) { - self.0.into_visitor().visit_item(item); - } - - fn visit_trait_item(&self, trait_item: &'hir TraitItem<'hir>) { - self.0.into_visitor().visit_trait_item(trait_item); - } - - fn visit_impl_item(&self, impl_item: &'hir ImplItem<'hir>) { - self.0.into_visitor().visit_impl_item(impl_item); - } - - fn visit_foreign_item(&self, foreign_item: &'hir ForeignItem<'hir>) { - self.0.into_visitor().visit_foreign_item(foreign_item); - } -} - #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// `#[xxx] pub async/const/extern "Abi" fn foo()` diff --git a/compiler/rustc_hir/src/itemlikevisit.rs b/compiler/rustc_hir/src/itemlikevisit.rs deleted file mode 100644 index a490268dc9f..00000000000 --- a/compiler/rustc_hir/src/itemlikevisit.rs +++ /dev/null @@ -1,9 +0,0 @@ -use super::{ForeignItem, ImplItem, Item, TraitItem}; - -/// A parallel variant of `ItemLikeVisitor`. -pub trait ParItemLikeVisitor<'hir> { - fn visit_item(&self, item: &'hir Item<'hir>); - fn visit_trait_item(&self, trait_item: &'hir TraitItem<'hir>); - fn visit_impl_item(&self, impl_item: &'hir ImplItem<'hir>); - fn visit_foreign_item(&self, foreign_item: &'hir ForeignItem<'hir>); -} diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index d845c433d8c..9f32a7da159 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -27,7 +27,6 @@ pub use rustc_span::def_id; mod hir; pub mod hir_id; pub mod intravisit; -pub mod itemlikevisit; pub mod lang_items; pub mod pat_util; mod stable_hash_impls; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index b9596cd10ed..42d52446ab6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -79,7 +79,7 @@ pub fn find_param_with_region<'tcx>( // May return None; sometimes the tables are not yet populated. let ty = fn_sig.inputs()[index]; let mut found_anon_region = false; - let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| { + let new_param_ty = tcx.fold_regions(ty, |r, _| { if r == anon_region { found_anon_region = true; replace_region diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 455de47acef..68c709a2e24 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -868,7 +868,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(value, &mut false, |r, _db| match *r { + tcx.fold_regions(value, |r, _db| match *r { ty::ReVar(rid) => self.resolve_var(rid), _ => r, }) diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 7684d861f3c..ebb8d443421 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -39,21 +39,19 @@ pub struct OpaqueTypeDecl<'tcx> { } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - /// This is a backwards compatibility hack to prevent breaking changes from - /// lazy TAIT around RPIT handling. - pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<'tcx>>( + pub fn replace_opaque_types_with_inference_vars( &self, - value: T, + ty: Ty<'tcx>, body_id: HirId, span: Span, code: ObligationCauseCode<'tcx>, param_env: ty::ParamEnv<'tcx>, - ) -> InferOk<'tcx, T> { - if !value.has_opaque_types() { - return InferOk { value, obligations: vec![] }; + ) -> InferOk<'tcx, Ty<'tcx>> { + if !ty.has_opaque_types() { + return InferOk { value: ty, obligations: vec![] }; } let mut obligations = vec![]; - let value = value.fold_with(&mut ty::fold::BottomUpFolder { + let value = ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, ct_op: |ct| ct, diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a268493b28f..1c1906f3375 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -141,9 +141,6 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// `('a, K)` in this list tells us that the bounds in scope /// indicate that `K: 'a`, where `K` is either a generic /// parameter like `T` or a projection like `T::Item`. - /// - `implicit_region_bound`: if some, this is a region bound - /// that is considered to hold for all type parameters (the - /// function body). /// - `param_env` is the parameter environment for the enclosing function. /// - `body_id` is the body-id whose region obligations are being /// processed. @@ -151,7 +148,6 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn process_registered_region_obligations( &self, region_bound_pairs_map: &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, - implicit_region_bound: Option<ty::Region<'tcx>>, param_env: ty::ParamEnv<'tcx>, ) { assert!( @@ -170,13 +166,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let sup_type = self.resolve_vars_if_possible(sup_type); if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) { - let outlives = &mut TypeOutlives::new( - self, - self.tcx, - ®ion_bound_pairs, - implicit_region_bound, - param_env, - ); + let outlives = + &mut TypeOutlives::new(self, self.tcx, ®ion_bound_pairs, None, param_env); outlives.type_must_outlive(origin, sup_type, sub_region); } else { self.tcx.sess.delay_span_bug( diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 191f5f18ec2..86b025dce5e 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -16,6 +16,11 @@ use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; pub struct VerifyBoundCx<'cx, 'tcx> { tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + /// During borrowck, if there are no outlives bounds on a generic + /// parameter `T`, we assume that `T: 'in_fn_body` holds. + /// + /// Outside of borrowck the only way to prove `T: '?0` is by + /// setting `'?0` to `'empty`. implicit_region_bound: Option<ty::Region<'tcx>>, param_env: ty::ParamEnv<'tcx>, } @@ -263,8 +268,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // fn foo<'a, A>(x: &'a A) { x.bar() } // // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. + // 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 = {:?}", diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index d830000b65f..1f3cb401314 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { .borrow_mut() .unwrap_region_constraints() .opportunistic_resolve_var(rid); - self.tcx().reuse_or_mk_region(r, ty::ReVar(resolved)) + TypeFolder::tcx(self).reuse_or_mk_region(r, ty::ReVar(resolved)) } _ => r, } @@ -179,15 +179,13 @@ struct FullTypeResolver<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, } -impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { +impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { type Error = FixupError<'tcx>; fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.infcx.tcx } -} -impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { if !t.needs_infer() { Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects... diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 97f9588ae1e..3da6bc14622 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -238,7 +238,7 @@ fn scan_escape(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> { c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?; n_digits += 1; if n_digits > 6 { - // Stop updating value since we're sure that it's is incorrect already. + // Stop updating value since we're sure that it's incorrect already. continue; } let digit = digit as u32; diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index fadb1c87933..56c8635a189 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -406,9 +406,12 @@ impl LateLintPass<'_> for Diagnostics { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some((span, def_id, substs)) = typeck_results_of_method_fn(cx, expr) else { return }; debug!(?span, ?def_id, ?substs); - if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) && - !cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_diagnostics) - { + let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) + .ok() + .and_then(|inst| inst) + .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)) + .unwrap_or(false); + if !has_attr { return; } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 2a2dc6822ce..5579e4d19cf 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,6 @@ use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; @@ -1483,39 +1482,32 @@ impl InvalidAtomicOrdering { None } - fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool { + fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> { + let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None }; + let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?; let tcx = cx.tcx; let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering); - orderings.iter().any(|ordering| { - tcx.item_name(did) == *ordering && { - let parent = tcx.parent(did); - Some(parent) == atomic_ordering - // needed in case this is a ctor, not a variant - || tcx.opt_parent(parent) == atomic_ordering - } - }) - } - - fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> { - if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { - cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() - } else { - None - } + let name = tcx.item_name(did); + let parent = tcx.parent(did); + [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find( + |&ordering| { + name == ordering + && (Some(parent) == atomic_ordering + // needed in case this is a ctor, not a variant + || tcx.opt_parent(parent) == atomic_ordering) + }, + ) } fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { - use rustc_hir::def::{DefKind, Res}; - use rustc_hir::QPath; if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store]) && let Some((ordering_arg, invalid_ordering)) = match method { sym::load => Some((&args[1], sym::Release)), sym::store => Some((&args[2], sym::Acquire)), _ => None, } - && let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res - && Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel]) + && let Some(ordering) = Self::match_ordering(cx, ordering_arg) + && (ordering == invalid_ordering || ordering == sym::AcqRel) { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| { if method == sym::load { @@ -1537,9 +1529,7 @@ impl InvalidAtomicOrdering { && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) - && let ExprKind::Path(ref ordering_qpath) = &args[0].kind - && let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id() - && Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed]) + && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) { cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| { diag.build("memory fences cannot have `Relaxed` ordering") @@ -1550,62 +1540,56 @@ impl InvalidAtomicOrdering { } fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak]) - && let Some((success_order_arg, failure_order_arg)) = match method { - sym::fetch_update => Some((&args[1], &args[2])), - sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])), - _ => None, - } - && let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg) - { - // Helper type holding on to some checking and error reporting data. Has - // - (success ordering, - // - list of failure orderings forbidden by the success order, - // - suggestion message) - type OrdLintInfo = (Symbol, &'static [Symbol], &'static str); - const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`"); - const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`"); - const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); - const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2); - const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2); - const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL]; - - let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg) - .and_then(|success_ord_def_id| -> Option<OrdLintInfo> { - SEARCH - .iter() - .copied() - .find(|(ordering, ..)| { - Self::matches_ordering(cx, success_ord_def_id, &[*ordering]) - }) - }); - if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) { - // If we don't know the success order is, use what we'd suggest - // if it were maximally permissive. - let suggested = success_lint_info.unwrap_or(SEQ_CST).2; - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| { - let msg = format!( - "{}'s failure ordering may not be `Release` or `AcqRel`", - method, - ); - diag.build(&msg) - .help(&format!("consider using {} instead", suggested)) - .emit(); - }); - } else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info { - if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) { - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| { - let msg = format!( - "{}'s failure ordering may not be stronger than the success ordering of `{}`", - method, - success_ord, - ); - diag.build(&msg) - .help(&format!("consider using {} instead", suggested)) - .emit(); - }); - } - } + 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]), + _ => return, + }; + + let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; + + if matches!(fail_ordering, sym::Release | sym::AcqRel) { + cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| { + diag.build(&format!( + "`{method}`'s failure ordering may not be `Release` or `AcqRel`, \ + since a failed `{method}` does not result in a write", + )) + .span_label(fail_order_arg.span, "invalid failure ordering") + .help("consider using `Acquire` or `Relaxed` failure ordering instead") + .emit(); + }); + } + + 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(&format!( + "`{method}`'s success ordering must be at least as strong as its failure ordering" + )) + .span_label(fail_order_arg.span, format!("`{fail_ordering}` failure ordering")) + .span_label(success_order_arg.span, format!("`{success_ordering}` success ordering")) + .span_suggestion_short( + success_order_arg.span, + format!("consider using `{success_suggestion}` success ordering instead"), + 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 b1bfd612b90..d52455e2576 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -289,6 +289,7 @@ declare_lint! { "constant evaluation encountered erroneous expression", @future_incompatible = FutureIncompatibleInfo { reference: "issue #71800 <https://github.com/rust-lang/rust/issues/71800>", + reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow, }; report_in_external_macro } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 38fddbdba54..0f4973ebf71 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -985,7 +985,9 @@ LLVMRustOptimizeWithNewPassManager( if (SanitizerOptions->SanitizeAddress) { OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { +#if LLVM_VERSION_LT(15, 0) MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); +#endif #if LLVM_VERSION_GE(14, 0) AddressSanitizerOptions opts = AddressSanitizerOptions{ /*CompileKernel=*/false, diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 95ee0d4a060..d0c86527189 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -12,7 +12,9 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type}; +use syn::{ + parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, +}; use synstructure::{BindingInfo, Structure}; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. @@ -118,23 +120,23 @@ impl<'a> SessionDiagnosticDerive<'a> { return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some((kind, _)), None) => { - span_err(span, "`slug` not specified") - .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr())) + span_err(span, "diagnostic slug not specified") + .help(&format!( + "specify the slug as the first argument to the attribute, such as \ + `#[{}(typeck::example_error)]`", + kind.descr() + )) .emit(); return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => { quote! { - let mut #diag = #sess.struct_err( - rustc_errors::DiagnosticMessage::new(#slug), - ); + let mut #diag = #sess.struct_err(rustc_errors::fluent::#slug); } } (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => { quote! { - let mut #diag = #sess.struct_warn( - rustc_errors::DiagnosticMessage::new(#slug), - ); + let mut #diag = #sess.struct_warn(rustc_errors::fluent::#slug); } } }; @@ -226,7 +228,7 @@ struct SessionDiagnosticDeriveBuilder { kind: Option<(SessionDiagnosticKind, proc_macro::Span)>, /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - slug: Option<(String, proc_macro::Span)>, + slug: Option<(Path, proc_macro::Span)>, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. code: Option<(String, proc_macro::Span)>, @@ -240,50 +242,79 @@ impl HasFieldMap for SessionDiagnosticDeriveBuilder { impl SessionDiagnosticDeriveBuilder { /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct - /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates + /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates /// diagnostic builder calls for setting error code and creating note/help messages. fn generate_structure_code( &mut self, attr: &Attribute, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { + let diag = &self.diag; let span = attr.span().unwrap(); let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); let meta = attr.parse_meta()?; - if matches!(name, "help" | "note") && matches!(meta, Meta::Path(_) | Meta::NameValue(_)) { - let diag = &self.diag; - let id = match meta { - Meta::Path(..) => quote! { #name }, - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - quote! { #s } - } - _ => unreachable!(), - }; - let fn_name = proc_macro2::Ident::new(name, attr.span()); - - return Ok(quote! { - #diag.#fn_name(rustc_errors::SubdiagnosticMessage::attr(#id)); - }); - } + let is_help_or_note = matches!(name, "help" | "note"); let nested = match meta { + // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or + // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug. 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()); + return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); + } _ => throw_invalid_attr!(attr, &meta), }; - let kind = match name { - "error" => SessionDiagnosticKind::Error, - "warning" => SessionDiagnosticKind::Warn, + // Check the kind before doing any further processing so that there aren't misleading + // "no kind specified" errors if there are failures later. + match name { + "error" => self.kind.set_once((SessionDiagnosticKind::Error, span)), + "warning" => self.kind.set_once((SessionDiagnosticKind::Warn, span)), + "help" | "note" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error` and `warning` are valid attributes") + diag.help("only `error`, `warning`, `help` and `note` are valid attributes") }), - }; - self.kind.set_once((kind, span)); + } + + // First nested element should always be the path, e.g. `#[error(typeck::invalid)]` or + // `#[help(typeck::another_help)]`. + 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() { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("`help` and `note` struct attributes can only have one argument") + }); + } + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + let fn_name = proc_macro2::Ident::new(name, attr.span()); + return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); + } + NestedMeta::Meta(Meta::Path(path)) => { + 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" => + { + // don't error for valid follow-up attributes + } + nested_attr => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("first argument of the attribute should be the diagnostic slug") + }), + }; + } + + // Remaining attributes are optional, only `code = ".."` at the moment. let mut tokens = Vec::new(); - for nested_attr in nested { + for nested_attr in nested_iter { let meta = match nested_attr { syn::NestedMeta::Meta(meta) => meta, _ => throw_invalid_nested_attr!(attr, &nested_attr), @@ -291,28 +322,24 @@ impl SessionDiagnosticDeriveBuilder { let path = meta.path(); let nested_name = path.segments.last().unwrap().ident.to_string(); - match &meta { - // Struct attributes are only allowed to be applied once, and the diagnostic - // changes will be set in the initialisation code. - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let span = s.span().unwrap(); - match nested_name.as_str() { - "slug" => { - self.slug.set_once((s.value(), span)); - } - "code" => { - self.code.set_once((s.value(), span)); - let (diag, code) = (&self.diag, &self.code.as_ref().map(|(v, _)| v)); - tokens.push(quote! { - #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); - }); - } - _ => invalid_nested_attr(attr, &nested_attr) - .help("only `slug` and `code` are valid nested attributes") - .emit(), + // Struct attributes are only allowed to be applied once, and the diagnostic + // changes will be set in the initialisation code. + if let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) = &meta { + let span = s.span().unwrap(); + match nested_name.as_str() { + "code" => { + self.code.set_once((s.value(), span)); + let code = &self.code.as_ref().map(|(v, _)| v); + tokens.push(quote! { + #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); + }); } + _ => invalid_nested_attr(attr, &nested_attr) + .help("only `code` is a valid nested attributes following the slug") + .emit(), } - _ => invalid_nested_attr(attr, &nested_attr).emit(), + } else { + invalid_nested_attr(attr, &nested_attr).emit() } } @@ -382,142 +409,215 @@ impl SessionDiagnosticDeriveBuilder { info: FieldInfo<'_>, binding: TokenStream, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { + let meta = attr.parse_meta()?; + match meta { + Meta::Path(_) => self.generate_inner_field_code_path(attr, info, binding), + Meta::List(MetaList { .. }) => self.generate_inner_field_code_list(attr, info, binding), + _ => throw_invalid_attr!(attr, &meta), + } + } + + fn generate_inner_field_code_path( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + binding: TokenStream, + ) -> Result<TokenStream, SessionDiagnosticDeriveError> { + assert!(matches!(attr.parse_meta()?, Meta::Path(_))); let diag = &self.diag; + let meta = attr.parse_meta()?; + let ident = &attr.path.segments.last().unwrap().ident; let name = ident.to_string(); let name = name.as_str(); + match name { + "skip_arg" => { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + Ok(quote! {}) + } + "primary_span" => { + report_error_if_not_applied_to_span(attr, &info)?; + Ok(quote! { + #diag.set_span(#binding); + }) + } + "label" => { + 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 }, + _ => unreachable!(), + }; + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + Ok(self.add_spanned_subdiagnostic(binding, ident, path)) + } else if type_is_unit(&info.ty) { + Ok(self.add_subdiagnostic(ident, path)) + } else { + report_type_error(attr, "`Span` or `()`")?; + } + } + "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), + _ => throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` \ + are valid field attributes", + ) + }), + } + } + fn generate_inner_field_code_list( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + binding: TokenStream, + ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let meta = attr.parse_meta()?; - match meta { - Meta::Path(_) => match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - Ok(quote! {}) - } - "primary_span" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(quote! { - #diag.set_span(#binding); - }) - } - "label" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, name)) - } - "note" | "help" => { - if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - Ok(self.add_spanned_subdiagnostic(binding, ident, name)) - } else if type_is_unit(&info.ty) { - Ok(self.add_subdiagnostic(ident, name)) - } else { - report_type_error(attr, "`Span` or `()`")?; - } - } - "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag - .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes") - }), - }, - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name { - "label" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value())) - } - "note" | "help" => { - if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value())) - } else if type_is_unit(&info.ty) { - Ok(self.add_subdiagnostic(ident, &s.value())) - } else { - report_type_error(attr, "`Span` or `()`")?; - } - } - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `label`, `note` and `help` are valid field attributes") - }), - }, - Meta::List(MetaList { ref path, ref nested, .. }) => { - let name = path.segments.last().unwrap().ident.to_string(); - let name = name.as_ref(); - - match name { - "suggestion" | "suggestion_short" | "suggestion_hidden" - | "suggestion_verbose" => (), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag - .help("only `suggestion{,_short,_hidden,_verbose}` are valid field attributes") - }), - }; + let Meta::List(MetaList { ref path, ref nested, .. }) = meta else { unreachable!() }; - let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; + let ident = &attr.path.segments.last().unwrap().ident; + let name = path.segments.last().unwrap().ident.to_string(); + let name = name.as_ref(); + match name { + "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { + return self.generate_inner_field_code_suggestion(attr, info); + } + "label" | "help" | "note" => (), + _ => throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + valid field attributes", + ) + }), + } - let mut msg = None; - let mut code = None; + // For `#[label(..)]`, `#[note(..)]` and `#[help(..)]`, the first nested element must be a + // path, e.g. `#[label(typeck::label)]`. + let mut nested_iter = nested.into_iter(); + let msg = match nested_iter.next() { + Some(NestedMeta::Meta(Meta::Path(path))) => path.clone(), + Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr), + None => throw_invalid_attr!(attr, &meta), + }; - for nested_attr in nested { - let meta = match nested_attr { - syn::NestedMeta::Meta(ref meta) => meta, - syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr), - }; + // None of these attributes should have anything following the slug. + if nested_iter.next().is_some() { + throw_invalid_attr!(attr, &meta); + } - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let span = meta.span().unwrap(); - match nested_name { - "message" => { - msg = Some(s.value()); - } - "code" => { - let formatted_str = self.build_format(&s.value(), s.span()); - code = Some(formatted_str); + match name { + "label" => { + report_error_if_not_applied_to_span(attr, &info)?; + Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) + } + "note" | "help" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => { + 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 `()`")?; + } + _ => unreachable!(), + } + } + + fn generate_inner_field_code_suggestion( + &mut self, + attr: &Attribute, + info: FieldInfo<'_>, + ) -> Result<TokenStream, SessionDiagnosticDeriveError> { + let diag = &self.diag; + + let mut meta = attr.parse_meta()?; + let Meta::List(MetaList { ref path, ref mut nested, .. }) = meta else { unreachable!() }; + + let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; + + let mut msg = None; + let mut code = None; + + let mut nested_iter = nested.into_iter().peekable(); + if let Some(nested_attr) = nested_iter.peek() { + if let NestedMeta::Meta(Meta::Path(path)) = nested_attr { + msg = Some(path.clone()); + } + }; + // Move the iterator forward if a path was found (don't otherwise so that + // code/applicability can be found or an error emitted). + if msg.is_some() { + let _ = nested_iter.next(); + } + + for nested_attr in nested_iter { + let meta = match nested_attr { + syn::NestedMeta::Meta(ref meta) => meta, + syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let span = meta.span().unwrap(); + match nested_name { + "code" => { + let formatted_str = self.build_format(&s.value(), s.span()); + code = Some(formatted_str); + } + "applicability" => { + applicability = match applicability { + Some(v) => { + span_err( + span, + "applicability cannot be set in both the field and \ + attribute", + ) + .emit(); + Some(v) } - "applicability" => { - applicability = match applicability { - Some(v) => { - span_err( - span, - "applicability cannot be set in both the field and attribute" - ).emit(); - Some(v) - } - None => match Applicability::from_str(&s.value()) { - Ok(v) => Some(quote! { #v }), - Err(()) => { - span_err(span, "invalid applicability").emit(); - None - } - }, + None => match Applicability::from_str(&s.value()) { + Ok(v) => Some(quote! { #v }), + Err(()) => { + span_err(span, "invalid applicability").emit(); + None } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "only `message`, `code` and `applicability` are valid field attributes", - ) - }), + }, } } - _ => throw_invalid_nested_attr!(attr, &nested_attr), + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "only `message`, `code` and `applicability` are valid field \ + attributes", + ) + }), } } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + if matches!(meta, Meta::Path(_)) { + diag.help("a diagnostic slug must be the first argument to the attribute") + } else { + diag + } + }), + } + } - let applicability = applicability - .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + let applicability = + applicability.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); - let method = format_ident!("span_{}", name); + let name = path.segments.last().unwrap().ident.to_string(); + let method = format_ident!("span_{}", name); - let msg = msg.as_deref().unwrap_or("suggestion"); - let msg = quote! { rustc_errors::SubdiagnosticMessage::attr(#msg) }; - let code = code.unwrap_or_else(|| quote! { String::new() }); + let msg = msg.unwrap_or_else(|| parse_quote! { _subdiag::suggestion }); + let msg = quote! { rustc_errors::fluent::#msg }; + let code = code.unwrap_or_else(|| quote! { String::new() }); - Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) - } - _ => throw_invalid_attr!(attr, &meta), - } + Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) } /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug @@ -526,24 +626,24 @@ impl SessionDiagnosticDeriveBuilder { &self, field_binding: TokenStream, kind: &Ident, - fluent_attr_identifier: &str, + fluent_attr_identifier: Path, ) -> TokenStream { let diag = &self.diag; let fn_name = format_ident!("span_{}", kind); quote! { #diag.#fn_name( #field_binding, - rustc_errors::SubdiagnosticMessage::attr(#fluent_attr_identifier) + rustc_errors::fluent::#fluent_attr_identifier ); } } /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream { + fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { let diag = &self.diag; quote! { - #diag.#kind(rustc_errors::SubdiagnosticMessage::attr(#fluent_attr_identifier)); + #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier); } } @@ -569,7 +669,8 @@ impl SessionDiagnosticDeriveBuilder { } else { throw_span_err!( info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more than one `Span`" + "type of field annotated with `#[suggestion(...)]` contains more \ + than one `Span`" ); } } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) { @@ -578,7 +679,8 @@ impl SessionDiagnosticDeriveBuilder { } else { throw_span_err!( info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" + "type of field annotated with `#[suggestion(...)]` contains more \ + than one Applicability" ); } } @@ -595,12 +697,18 @@ impl SessionDiagnosticDeriveBuilder { } throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { - diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`") + diag.help( + "`#[suggestion(...)]` on a tuple field must be applied to fields of type \ + `(Span, Applicability)`", + ) }); } // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error. _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| { - diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`") + diag.help( + "`#[suggestion(...)]` should be applied to fields of type `Span` or \ + `(Span, Applicability)`", + ) }), } } diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs index fd1dc2f3073..d088402abc6 100644 --- a/compiler/rustc_macros/src/diagnostics/error.rs +++ b/compiler/rustc_macros/src/diagnostics/error.rs @@ -39,6 +39,19 @@ pub(crate) fn _throw_err( SessionDiagnosticDeriveError::ErrorHandled } +/// Helper function for printing `syn::Path` - doesn't handle arguments in paths and these are +/// unlikely to come up much in use of the macro. +fn path_to_string(path: &syn::Path) -> String { + let mut out = String::new(); + for (i, segment) in path.segments.iter().enumerate() { + if i > 0 || path.leading_colon.is_some() { + out.push_str("::"); + } + out.push_str(&segment.ident.to_string()); + } + out +} + /// Returns an error diagnostic on span `span` with msg `msg`. pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic { Diagnostic::spanned(span, Level::Error, msg) @@ -61,15 +74,13 @@ pub(crate) use throw_span_err; /// Returns an error diagnostic for an invalid attribute. pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { let span = attr.span().unwrap(); - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - + let path = path_to_string(&attr.path); match meta { - Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", name)), + Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", path)), Meta::NameValue(_) => { - span_err(span, &format!("`#[{} = ...]` is not a valid attribute", name)) + span_err(span, &format!("`#[{} = ...]` is not a valid attribute", path)) } - Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", name)), + Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", path)), } } @@ -101,18 +112,16 @@ pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diag }; let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); + let path = path_to_string(meta.path()); match meta { - Meta::NameValue(..) => span_err( - span, - &format!("`#[{}({} = ...)]` is not a valid attribute", name, nested_name), - ), + Meta::NameValue(..) => { + span_err(span, &format!("`#[{}({} = ...)]` is not a valid attribute", name, path)) + } Meta::Path(..) => { - span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, nested_name)) + span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, path)) } Meta::List(..) => { - span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, nested_name)) + span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, path)) } } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 42a9bf477a4..2317186e655 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -254,6 +254,17 @@ 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 label: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); + pub const suggestion: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("suggestion")); + } } } .into() diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 69573d904d4..2eee4bfb5dd 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -22,14 +22,14 @@ use synstructure::Structure; /// # extern crate rust_middle; /// # use rustc_middle::ty::Ty; /// #[derive(SessionDiagnostic)] -/// #[error(code = "E0505", slug = "borrowck-move-out-of-borrow")] +/// #[error(borrowck::move_out_of_borrow, code = "E0505")] /// pub struct MoveOutOfBorrowError<'tcx> { /// pub name: Ident, /// pub ty: Ty<'tcx>, /// #[primary_span] /// #[label] /// pub span: Span, -/// #[label = "first-borrow-label"] +/// #[label(borrowck::first_borrow_label)] /// pub first_borrow_span: Span, /// #[suggestion(code = "{name}.clone()")] /// pub clone_sugg: Option<(Span, Applicability)> @@ -72,12 +72,12 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// ```ignore (rust) /// #[derive(SessionSubdiagnostic)] /// pub enum ExpectedIdentifierLabel<'tcx> { -/// #[label(slug = "parser-expected-identifier")] +/// #[label(parser::expected_identifier)] /// WithoutFound { /// #[primary_span] /// span: Span, /// } -/// #[label(slug = "parser-expected-identifier-found")] +/// #[label(parser::expected_identifier_found)] /// WithFound { /// #[primary_span] /// span: Span, @@ -86,7 +86,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// } /// /// #[derive(SessionSubdiagnostic)] -/// #[suggestion_verbose(slug = "parser-raw-identifier")] +/// #[suggestion_verbose(parser::raw_identifier)] /// pub struct RawIdentifierSuggestion<'tcx> { /// #[primary_span] /// span: Span, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 9aeb484bfd5..eab954a9c1b 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -13,7 +13,7 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::fmt; use std::str::FromStr; -use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue}; +use syn::{parse_quote, spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// Which kind of suggestion is being created? @@ -194,8 +194,8 @@ struct SessionSubdiagnosticDeriveBuilder<'a> { kind: Option<(SubdiagnosticKind, proc_macro::Span)>, /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the - /// `#[kind(slug = "...")]` attribute on the type or variant. - slug: Option<(String, proc_macro::Span)>, + /// `#[kind(slug)]` attribute on the type or variant. + slug: Option<(Path, proc_macro::Span)>, /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]` /// attribute on the type or variant. code: Option<(TokenStream, proc_macro::Span)>, @@ -224,9 +224,34 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let meta = attr.parse_meta()?; let kind = match meta { Meta::List(MetaList { ref nested, .. }) => { - for nested_attr in nested { + let mut nested_iter = nested.into_iter(); + if let Some(nested_attr) = nested_iter.next() { + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) => { + self.slug.set_once((path.clone(), span)); + } + NestedMeta::Meta(meta @ Meta::NameValue(_)) + if matches!( + meta.path().segments.last().unwrap().ident.to_string().as_str(), + "code" | "applicability" + ) => + { + // don't error for valid follow-up attributes + } + nested_attr => { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "first argument of the attribute should be the diagnostic \ + slug", + ) + }) + } + }; + } + + for nested_attr in nested_iter { let meta = match nested_attr { - syn::NestedMeta::Meta(ref meta) => meta, + NestedMeta::Meta(ref meta) => meta, _ => throw_invalid_nested_attr!(attr, &nested_attr), }; @@ -241,7 +266,6 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let formatted_str = self.build_format(&s.value(), s.span()); self.code.set_once((formatted_str, span)); } - "slug" => self.slug.set_once((s.value(), span)), "applicability" => { let value = match Applicability::from_str(&s.value()) { Ok(v) => v, @@ -253,11 +277,23 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { self.applicability.set_once((quote! { #value }, span)); } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("only `code`, `slug` and `applicability` are valid nested attributes") + diag.help( + "only `code` and `applicability` are valid nested \ + attributes", + ) }), } } - _ => throw_invalid_nested_attr!(attr, &nested_attr), + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + if matches!(meta, Meta::Path(_)) { + diag.help( + "a diagnostic slug must be the first argument to the \ + attribute", + ) + } else { + diag + } + }), } } @@ -281,10 +317,27 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { ); } + if matches!( + kind, + SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note + ) && self.applicability.is_some() + { + throw_span_err!( + span, + &format!( + "`applicability` is not a valid nested attribute of a `{}` attribute", + name + ) + ); + } + if self.slug.is_none() { throw_span_err!( span, - &format!("`slug` must be set in a `#[{}(...)]` attribute", name) + &format!( + "diagnostic slug must be first argument of a `#[{}(...)]` attribute", + name + ) ); } @@ -335,7 +388,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { return Ok(quote! {}); } _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `primary_span`, `applicability` and `skip_arg` are valid field attributes") + diag.help( + "only `primary_span`, `applicability` and `skip_arg` are valid field \ + attributes", + ) }), }, _ => throw_invalid_attr!(attr, &meta), @@ -375,7 +431,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { } // Missing slug errors will already have been reported. - let slug = self.slug.as_ref().map(|(slug, _)| &**slug).unwrap_or("missing-slug"); + let slug = self + .slug + .as_ref() + .map(|(slug, _)| slug.clone()) + .unwrap_or_else(|| parse_quote! { you::need::to::specify::a::slug }); let code = match self.code.as_ref() { Some((code, _)) => Some(quote! { #code }), None if is_suggestion => { @@ -397,7 +457,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { let diag = &self.diag; let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); - let message = quote! { rustc_errors::SubdiagnosticMessage::message(#slug) }; + let message = quote! { rustc_errors::fluent::#slug }; let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) { if let Some(span) = span_field { quote! { #diag.#name(#span, #message, #code, #applicability); } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c1ded99a25a..3280fd5c310 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -42,7 +42,7 @@ use std::io; use std::iter::TrustedLen; use std::mem; use std::num::NonZeroUsize; -use std::path::Path; +use std::path::PathBuf; use tracing::debug; pub(super) use cstore_impl::provide; @@ -1473,20 +1473,26 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // // 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(Path::new) - .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 - }) - .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) - }); + 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<_>>(); + let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { debug!( "try_to_translate_virtual_to_real(name={:?}): \ @@ -1494,7 +1500,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { name, virtual_rust_source_base_dir, sess.opts.real_rust_source_base_dir, ); - if let Some(virtual_dir) = virtual_rust_source_base_dir { + for virtual_dir in &virtual_rust_source_base_dir { 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 } = diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 738d47ba296..a27b8470e95 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -612,23 +612,6 @@ impl<'hir> Map<'hir> { } } - /// A parallel version of `visit_all_item_likes`. - pub fn par_visit_all_item_likes<V>(self, visitor: &V) - where - V: itemlikevisit::ParItemLikeVisitor<'hir> + Sync + Send, - { - let krate = self.krate(); - par_for_each_in(&krate.owners.raw, |owner| match owner.map(OwnerInfo::node) { - MaybeOwner::Owner(OwnerNode::Item(item)) => visitor.visit_item(item), - MaybeOwner::Owner(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item), - MaybeOwner::Owner(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item), - MaybeOwner::Owner(OwnerNode::TraitItem(item)) => visitor.visit_trait_item(item), - MaybeOwner::Owner(OwnerNode::Crate(_)) - | MaybeOwner::NonOwner(_) - | MaybeOwner::Phantom => {} - }) - } - /// If you don't care about nesting, you should use the /// `tcx.hir_module_items()` query or `module_items()` instead. /// Please see notes in `deep_visit_all_item_likes`. @@ -867,6 +850,10 @@ impl<'hir> Map<'hir> { ) } + pub fn expect_owner(self, id: LocalDefId) -> OwnerNode<'hir> { + self.tcx.hir_owner(id).unwrap_or_else(|| bug!("expected owner for {:?}", id)).node + } + pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> { match self.tcx.hir_owner(id) { Some(Owner { node: OwnerNode::Item(item), .. }) => item, diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 09b142e0c41..8622a620721 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -10,6 +10,7 @@ use crate::ty::query::Providers; use crate::ty::{DefIdTree, ImplSubject, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::*; use rustc_query_system::ich::StableHashingContext; @@ -61,6 +62,22 @@ impl ModuleItems { pub fn foreign_items(&self) -> impl Iterator<Item = ForeignItemId> + '_ { self.foreign_items.iter().copied() } + + pub fn par_items(&self, f: impl Fn(ItemId) + Send + Sync) { + par_for_each_in(&self.items[..], |&id| f(id)) + } + + pub fn par_trait_items(&self, f: impl Fn(TraitItemId) + Send + Sync) { + par_for_each_in(&self.trait_items[..], |&id| f(id)) + } + + pub fn par_impl_items(&self, f: impl Fn(ImplItemId) + Send + Sync) { + par_for_each_in(&self.impl_items[..], |&id| f(id)) + } + + pub fn par_foreign_items(&self, f: impl Fn(ForeignItemId) + Send + Sync) { + par_for_each_in(&self.foreign_items[..], |&id| f(id)) + } } impl<'tcx> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index ca2c03cb614..2e68fc8a7c0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -826,6 +826,10 @@ rustc_queries! { desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } } + query check_mod_type_wf(key: LocalDefId) -> () { + desc { |tcx| "checking that types are well-formed in {}", describe_as_module(key, tcx) } + } + query collect_mod_item_types(key: LocalDefId) -> () { desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } } @@ -906,9 +910,10 @@ rustc_queries! { /// Checks whether all impls in the crate pass the overlap check, returning /// which impls fail it. If all impls are correct, the returned slice is empty. - query orphan_check_crate(_: ()) -> &'tcx [LocalDefId] { - desc { - "checking whether the immpl in the this crate follow the orphan rules", + query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| + "checking whether impl `{}` follows the orphan rules", + tcx.def_path_str(key.to_def_id()), } } @@ -1398,13 +1403,7 @@ rustc_queries! { separate_provide_extern } - query check_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_trait_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_impl_item_well_formed(key: LocalDefId) -> () { + query check_well_formed(key: LocalDefId) -> () { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 5258d37a14c..7f913faf860 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -253,7 +253,7 @@ pub enum ObligationCauseCode<'tcx> { ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), /// Obligation incurred due to an object cast. - ObjectCastObligation(/* Object type */ Ty<'tcx>), + ObjectCastObligation(/* Concrete type */ Ty<'tcx>, /* Object type */ Ty<'tcx>), /// Obligation incurred due to a coercion. Coercion { @@ -406,7 +406,7 @@ pub enum ObligationCauseCode<'tcx> { QuestionMark, /// Well-formed checking. If a `WellFormedLoc` is provided, - /// then it will be used to eprform HIR-based wf checking + /// then it will be used to perform HIR-based wf checking /// after an error occurs, in order to generate a more precise error span. /// This is purely for diagnostic purposes - it is always /// correct to use `MiscObligation` instead, or to specify @@ -863,7 +863,7 @@ pub enum ObjectSafetyViolation { impl ObjectSafetyViolation { pub fn error_msg(&self) -> Cow<'static, str> { - match *self { + match self { ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), ObjectSafetyViolation::SupertraitSelf(ref spans) => { if spans.iter().any(|sp| *sp != DUMMY_SP) { @@ -873,7 +873,7 @@ impl ObjectSafetyViolation { .into() } } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_, _, _), _) => { + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { format!("associated function `{}` has no `self` parameter", name).into() } ObjectSafetyViolation::Method( @@ -897,9 +897,11 @@ impl ObjectSafetyViolation { ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { format!("method `{}` has generic type parameters", name).into() } - ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { - format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() - } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::UndispatchableReceiver(_), + _, + ) => format!("method `{}`'s `self` parameter cannot be dispatched on", name).into(), ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { format!("it contains associated `const` `{}`", name).into() } @@ -911,51 +913,40 @@ impl ObjectSafetyViolation { } pub fn solution(&self, err: &mut Diagnostic) { - match *self { + match self { ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {} ObjectSafetyViolation::Method( name, - MethodViolationCode::StaticMethod(sugg, self_span, has_args), + MethodViolationCode::StaticMethod(Some((add_self_sugg, make_sized_sugg))), _, ) => { err.span_suggestion( - self_span, - &format!( + add_self_sugg.1, + format!( "consider turning `{}` into a method by giving it a `&self` argument", name ), - format!("&self{}", if has_args { ", " } else { "" }), + add_self_sugg.0.to_string(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion( + make_sized_sugg.1, + format!( + "alternatively, consider constraining `{}` so it does not apply to \ + trait objects", + name + ), + make_sized_sugg.0.to_string(), Applicability::MaybeIncorrect, ); - match sugg { - Some((sugg, span)) => { - err.span_suggestion( - span, - &format!( - "alternatively, consider constraining `{}` so it does not apply to \ - trait objects", - name - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - None => { - err.help(&format!( - "consider turning `{}` into a method by giving it a `&self` \ - argument or constraining it so it does not apply to trait objects", - name - )); - } - } } ObjectSafetyViolation::Method( name, - MethodViolationCode::UndispatchableReceiver, - span, + MethodViolationCode::UndispatchableReceiver(Some(span)), + _, ) => { err.span_suggestion( - span, + *span, &format!( "consider changing method `{}`'s `self` parameter to be `&self`", name @@ -991,13 +982,13 @@ impl ObjectSafetyViolation { } /// Reasons a method might not be object-safe. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum MethodViolationCode { /// e.g., `fn foo()` - StaticMethod(Option<(&'static str, Span)>, Span, bool /* has args */), + StaticMethod(Option<(/* add &self */ (String, Span), /* add Self: Sized */ (String, Span))>), /// e.g., `fn foo(&self, x: Self)` - ReferencesSelfInput(usize), + ReferencesSelfInput(Option<Span>), /// e.g., `fn foo(&self) -> Self` ReferencesSelfOutput, @@ -1009,7 +1000,7 @@ pub enum MethodViolationCode { Generic, /// the method's receiver (`self` argument) can't be dispatched on - UndispatchableReceiver, + UndispatchableReceiver(Option<Span>), } /// These are the error cases for `codegen_fulfill_obligation`. diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 31b8fa1ce95..a6310ae5e66 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -86,7 +86,7 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { /// A convenient alternative to `try_fold_with` for use with infallible /// folders. Do not override this method, to ensure coherence with /// `try_fold_with`. - fn fold_with<F: TypeFolder<'tcx, Error = !>>(self, folder: &mut F) -> Self { + fn fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { self.try_fold_with(folder).into_ok() } @@ -216,7 +216,7 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> { /// A convenient alternative to `try_super_fold_with` for use with /// infallible folders. Do not override this method, to ensure coherence /// with `try_super_fold_with`. - fn super_fold_with<F: TypeFolder<'tcx, Error = !>>(self, folder: &mut F) -> Self { + fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { self.try_super_fold_with(folder).into_ok() } @@ -229,70 +229,46 @@ pub trait TypeSuperFoldable<'tcx>: TypeFoldable<'tcx> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>; } -/// This trait is implemented for every folding traversal. There is a fold -/// method defined for every type of interest. Each such method has a default -/// that does an "identity" fold. Implementations of these methods often fall -/// back to a `super_fold_with` method if the primary argument doesn't -/// satisfy a particular condition. +/// This trait is implemented for every infallible folding traversal. There is +/// a fold method defined for every type of interest. Each such method has a +/// default that does an "identity" fold. Implementations of these methods +/// often fall back to a `super_fold_with` method if the primary argument +/// doesn't satisfy a particular condition. /// -/// If this folder is fallible (and therefore its [`Error`][`TypeFolder::Error`] -/// associated type is something other than the default `!`) then -/// [`FallibleTypeFolder`] should be implemented manually. Otherwise, -/// a blanket implementation of [`FallibleTypeFolder`] will defer to +/// A blanket implementation of [`FallibleTypeFolder`] will defer to /// the infallible methods of this trait to ensure that the two APIs /// are coherent. -pub trait TypeFolder<'tcx>: Sized { - type Error = !; - +pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T> where T: TypeFoldable<'tcx>, - Self: TypeFolder<'tcx, Error = !>, { t.super_fold_with(self) } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { t.super_fold_with(self) } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { r.super_fold_with(self) } - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { c.super_fold_with(self) } - fn fold_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ty::Unevaluated<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ty::Unevaluated<'tcx> { uv.super_fold_with(self) } - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { p.super_fold_with(self) } - fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> - where - Self: TypeFolder<'tcx, Error = !>, - { + fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { bug!("most type folders should not be folding MIR datastructures: {:?}", c) } } @@ -304,7 +280,11 @@ pub trait TypeFolder<'tcx>: Sized { /// A blanket implementation of this trait (that defers to the relevant /// method of [`TypeFolder`]) is provided for all infallible folders in /// order to ensure the two APIs are coherent. -pub trait FallibleTypeFolder<'tcx>: TypeFolder<'tcx> { +pub trait FallibleTypeFolder<'tcx>: Sized { + type Error; + + fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + fn try_fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Result<Binder<'tcx, T>, Self::Error> where T: TypeFoldable<'tcx>, @@ -350,45 +330,48 @@ pub trait FallibleTypeFolder<'tcx>: TypeFolder<'tcx> { // delegates to infallible methods to ensure coherence. impl<'tcx, F> FallibleTypeFolder<'tcx> for F where - F: TypeFolder<'tcx, Error = !>, + F: TypeFolder<'tcx>, { - fn try_fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Result<Binder<'tcx, T>, Self::Error> + type Error = !; + + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + TypeFolder::tcx(self) + } + + fn try_fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Result<Binder<'tcx, T>, !> where T: TypeFoldable<'tcx>, { Ok(self.fold_binder(t)) } - fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { + fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, !> { Ok(self.fold_ty(t)) } - fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> { + fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, !> { Ok(self.fold_region(r)) } - fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> { + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, !> { Ok(self.fold_const(c)) } fn try_fold_unevaluated( &mut self, c: ty::Unevaluated<'tcx>, - ) -> Result<ty::Unevaluated<'tcx>, Self::Error> { + ) -> Result<ty::Unevaluated<'tcx>, !> { Ok(self.fold_unevaluated(c)) } - fn try_fold_predicate( - &mut self, - p: ty::Predicate<'tcx>, - ) -> Result<ty::Predicate<'tcx>, Self::Error> { + fn try_fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> Result<ty::Predicate<'tcx>, !> { Ok(self.fold_predicate(p)) } fn try_fold_mir_const( &mut self, c: mir::ConstantKind<'tcx>, - ) -> Result<mir::ConstantKind<'tcx>, Self::Error> { + ) -> Result<mir::ConstantKind<'tcx>, !> { Ok(self.fold_mir_const(c)) } } @@ -482,13 +465,12 @@ impl<'tcx> TyCtxt<'tcx> { pub fn fold_regions<T>( self, value: T, - skipped_regions: &mut bool, mut f: impl FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>, ) -> T where T: TypeFoldable<'tcx>, { - value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f)) + value.fold_with(&mut RegionFolder::new(self, &mut f)) } /// Invoke `callback` on every region appearing free in `value`. @@ -596,7 +578,6 @@ impl<'tcx> TyCtxt<'tcx> { pub struct RegionFolder<'a, 'tcx> { tcx: TyCtxt<'tcx>, - skipped_regions: &'a mut bool, /// Stores the index of a binder *just outside* the stuff we have /// visited. So this begins as INNERMOST; when we pass through a @@ -614,10 +595,9 @@ impl<'a, 'tcx> RegionFolder<'a, 'tcx> { #[inline] pub fn new( tcx: TyCtxt<'tcx>, - skipped_regions: &'a mut bool, fold_region_fn: &'a mut dyn FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>, ) -> RegionFolder<'a, 'tcx> { - RegionFolder { tcx, skipped_regions, current_index: ty::INNERMOST, fold_region_fn } + RegionFolder { tcx, current_index: ty::INNERMOST, fold_region_fn } } } @@ -641,7 +621,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { match *r { ty::ReLateBound(debruijn, _) if debruijn < self.current_index => { debug!(?self.current_index, "skipped bound region"); - *self.skipped_regions = true; r } _ => { diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 9bbbd7e2f7c..66a0a192a87 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -228,15 +228,13 @@ impl<'tcx> TryNormalizeAfterErasingRegionsFolder<'tcx> { } } -impl<'tcx> TypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'tcx> { +impl<'tcx> FallibleTypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'tcx> { type Error = NormalizationError<'tcx>; fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } -} -impl<'tcx> FallibleTypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'tcx> { fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) { Ok(t) => Ok(t.expect_ty()), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 200253d5755..c56909ba18b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -226,7 +226,7 @@ pub trait PrettyPrinter<'tcx>: value.as_ref().skip_binder().print(self) } - fn wrap_binder<T, F: Fn(&T, Self) -> Result<Self, fmt::Error>>( + fn wrap_binder<T, F: FnOnce(&T, Self) -> Result<Self, fmt::Error>>( self, value: &ty::Binder<'tcx, T>, f: F, @@ -773,18 +773,18 @@ pub trait PrettyPrinter<'tcx>: def_id: DefId, substs: &'tcx ty::List<ty::GenericArg<'tcx>>, ) -> Result<Self::Type, Self::Error> { - define_scoped_cx!(self); + let tcx = self.tcx(); // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. - let bounds = self.tcx().bound_explicit_item_bounds(def_id); + let bounds = tcx.bound_explicit_item_bounds(def_id); let mut traits = FxIndexMap::default(); let mut fn_traits = FxIndexMap::default(); let mut is_sized = false; for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { - let predicate = predicate.subst(self.tcx(), substs); + let predicate = predicate.subst(tcx, substs); let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { @@ -792,7 +792,7 @@ pub trait PrettyPrinter<'tcx>: let trait_ref = bound_predicate.rebind(pred.trait_ref); // Don't print + Sized, but rather + ?Sized if absent. - if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { + if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { is_sized = true; continue; } @@ -801,7 +801,7 @@ pub trait PrettyPrinter<'tcx>: } ty::PredicateKind::Projection(pred) => { let proj_ref = bound_predicate.rebind(pred); - let trait_ref = proj_ref.required_poly_trait_ref(self.tcx()); + let trait_ref = proj_ref.required_poly_trait_ref(tcx); // Projection type entry -- the def-id for naming, and the ty. let proj_ty = (proj_ref.projection_def_id(), proj_ref.term()); @@ -817,148 +817,155 @@ pub trait PrettyPrinter<'tcx>: } } + write!(self, "impl ")?; + let mut first = true; // Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized; - p!("impl"); - for (fn_once_trait_ref, entry) in fn_traits { - // Get the (single) generic ty (the args) of this FnOnce trait ref. - let generics = self.tcx().generics_of(fn_once_trait_ref.def_id()); - let args = - generics.own_substs_no_defaults(self.tcx(), fn_once_trait_ref.skip_binder().substs); - - match (entry.return_ty, args[0].expect_ty()) { - // We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded - // a return type. - (Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => { - let name = if entry.fn_trait_ref.is_some() { - "Fn" - } else if entry.fn_mut_trait_ref.is_some() { - "FnMut" - } else { - "FnOnce" - }; + write!(self, "{}", if first { "" } else { " + " })?; + write!(self, "{}", if paren_needed { "(" } else { "" })?; - p!( - write("{}", if first { " " } else { " + " }), - write("{}{}(", if paren_needed { "(" } else { "" }, name) - ); + self = self.wrap_binder(&fn_once_trait_ref, |trait_ref, mut cx| { + define_scoped_cx!(cx); + // Get the (single) generic ty (the args) of this FnOnce trait ref. + let generics = tcx.generics_of(trait_ref.def_id); + let args = generics.own_substs_no_defaults(tcx, trait_ref.substs); + + match (entry.return_ty, args[0].expect_ty()) { + // We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded + // a return type. + (Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => { + let name = if entry.fn_trait_ref.is_some() { + "Fn" + } else if entry.fn_mut_trait_ref.is_some() { + "FnMut" + } else { + "FnOnce" + }; - for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() { - if idx > 0 { - p!(", "); + p!(write("{}(", name)); + + for (idx, ty) in arg_tys.tuple_fields().iter().enumerate() { + if idx > 0 { + p!(", "); + } + p!(print(ty)); } - p!(print(ty)); - } - p!(")"); - if let Term::Ty(ty) = return_ty.skip_binder() { - if !ty.is_unit() { - p!(" -> ", print(return_ty)); + p!(")"); + if let Term::Ty(ty) = return_ty.skip_binder() { + if !ty.is_unit() { + p!(" -> ", print(return_ty)); + } } - } - p!(write("{}", if paren_needed { ")" } else { "" })); + p!(write("{}", if paren_needed { ")" } else { "" })); - first = false; - } - // If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the - // trait_refs we collected in the OpaqueFnEntry as normal trait refs. - _ => { - if entry.has_fn_once { - traits.entry(fn_once_trait_ref).or_default().extend( - // Group the return ty with its def id, if we had one. - entry - .return_ty - .map(|ty| (self.tcx().lang_items().fn_once_output().unwrap(), ty)), - ); - } - if let Some(trait_ref) = entry.fn_mut_trait_ref { - traits.entry(trait_ref).or_default(); + first = false; } - if let Some(trait_ref) = entry.fn_trait_ref { - traits.entry(trait_ref).or_default(); + // If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the + // trait_refs we collected in the OpaqueFnEntry as normal trait refs. + _ => { + if entry.has_fn_once { + traits.entry(fn_once_trait_ref).or_default().extend( + // Group the return ty with its def id, if we had one. + entry + .return_ty + .map(|ty| (tcx.lang_items().fn_once_output().unwrap(), ty)), + ); + } + if let Some(trait_ref) = entry.fn_mut_trait_ref { + traits.entry(trait_ref).or_default(); + } + if let Some(trait_ref) = entry.fn_trait_ref { + traits.entry(trait_ref).or_default(); + } } } - } + + Ok(cx) + })?; } // Print the rest of the trait types (that aren't Fn* family of traits) for (trait_ref, assoc_items) in traits { - p!( - write("{}", if first { " " } else { " + " }), - print(trait_ref.skip_binder().print_only_trait_name()) - ); + write!(self, "{}", if first { "" } else { " + " })?; + + self = self.wrap_binder(&trait_ref, |trait_ref, mut cx| { + define_scoped_cx!(cx); + p!(print(trait_ref.print_only_trait_name())); - let generics = self.tcx().generics_of(trait_ref.def_id()); - let args = generics.own_substs_no_defaults(self.tcx(), trait_ref.skip_binder().substs); + let generics = tcx.generics_of(trait_ref.def_id); + let args = generics.own_substs_no_defaults(tcx, trait_ref.substs); - if !args.is_empty() || !assoc_items.is_empty() { - let mut first = true; + if !args.is_empty() || !assoc_items.is_empty() { + let mut first = true; - for ty in args { - if first { - p!("<"); - first = false; - } else { - p!(", "); + for ty in args { + if first { + p!("<"); + first = false; + } else { + p!(", "); + } + p!(print(ty)); } - p!(print(trait_ref.rebind(*ty))); - } - for (assoc_item_def_id, term) in assoc_items { - // Skip printing `<[generator@] as Generator<_>>::Return` from async blocks, - // unless we can find out what generator return type it comes from. - let term = if let Some(ty) = term.skip_binder().ty() - && let ty::Projection(ty::ProjectionTy { item_def_id, substs }) = ty.kind() - && Some(*item_def_id) == self.tcx().lang_items().generator_return() - { - if let ty::Generator(_, substs, _) = substs.type_at(0).kind() { - let return_ty = substs.as_generator().return_ty(); - if !return_ty.is_ty_infer() { - return_ty.into() + for (assoc_item_def_id, term) in assoc_items { + // Skip printing `<[generator@] as Generator<_>>::Return` from async blocks, + // unless we can find out what generator return type it comes from. + let term = if let Some(ty) = term.skip_binder().ty() + && let ty::Projection(ty::ProjectionTy { item_def_id, substs }) = ty.kind() + && Some(*item_def_id) == tcx.lang_items().generator_return() + { + if let ty::Generator(_, substs, _) = substs.type_at(0).kind() { + let return_ty = substs.as_generator().return_ty(); + if !return_ty.is_ty_infer() { + return_ty.into() + } else { + continue; + } } else { continue; } } else { - continue; - } - } else { - term.skip_binder() - }; + term.skip_binder() + }; - if first { - p!("<"); - first = false; - } else { - p!(", "); - } + if first { + p!("<"); + first = false; + } else { + p!(", "); + } - p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).name)); + p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name)); - match term { - Term::Ty(ty) => { - p!(print(ty)) - } - Term::Const(c) => { - p!(print(c)); - } - }; - } + match term { + Term::Ty(ty) => { + p!(print(ty)) + } + Term::Const(c) => { + p!(print(c)); + } + }; + } - if !first { - p!(">"); + if !first { + p!(">"); + } } - } - first = false; + first = false; + Ok(cx) + })?; } if !is_sized { - p!(write("{}?Sized", if first { " " } else { " + " })); + write!(self, "{}?Sized", if first { "" } else { " + " })?; } else if first { - p!(" Sized"); + write!(self, "Sized")?; } Ok(self) @@ -1869,7 +1876,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { self.pretty_in_binder(value) } - fn wrap_binder<T, C: Fn(&T, Self) -> Result<Self, Self::Error>>( + fn wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, Self::Error>>( self, value: &ty::Binder<'tcx, T>, f: C, @@ -2256,7 +2263,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { Ok(inner) } - pub fn pretty_wrap_binder<T, C: Fn(&T, Self) -> Result<Self, fmt::Error>>( + pub fn pretty_wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, fmt::Error>>( self, value: &ty::Binder<'tcx, T>, f: C, diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index ad2898ccd67..1417c8a511c 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -705,7 +705,7 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { return val; } - let result = ty::fold::shift_vars(self.tcx(), val, self.binders_passed); + let result = ty::fold::shift_vars(TypeFolder::tcx(self), val, self.binders_passed); debug!("shift_vars: shifted result = {:?}", result); result diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b5f3cd828a0..08b66d0abc7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,3 +1,4 @@ +use crate::thir::cx::region::Scope; use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -158,6 +159,98 @@ impl<'tcx> Cx<'tcx> { Expr { temp_lifetime, ty: adjustment.target, span, kind } } + /// Lowers a cast expression. + /// + /// Dealing with user type annotations is left to the caller. + fn mirror_expr_cast( + &mut self, + source: &'tcx hir::Expr<'tcx>, + temp_lifetime: Option<Scope>, + span: Span, + ) -> ExprKind<'tcx> { + let tcx = self.tcx; + + // Check to see if this cast is a "coercion cast", where the cast is actually done + // using a coercion (or is a no-op). + if self.typeck_results().is_coercion_cast(source.hir_id) { + // Convert the lexpr to a vexpr. + ExprKind::Use { source: self.mirror_expr(source) } + } else if self.typeck_results().expr_ty(source).is_region_ptr() { + // Special cased so that we can type check that the element + // type of the source matches the pointed to type of the + // destination. + ExprKind::Pointer { + source: self.mirror_expr(source), + cast: PointerCast::ArrayToPointer, + } + } else { + // check whether this is casting an enum variant discriminant + // to prevent cycles, we refer to the discriminant initializer + // which is always an integer and thus doesn't need to know the + // enum's layout (or its tag type) to compute it during const eval + // Example: + // enum Foo { + // A, + // B = A as isize + 4, + // } + // The correct solution would be to add symbolic computations to miri, + // so we wouldn't have to compute and store the actual value + + let hir::ExprKind::Path(ref qpath) = source.kind else { + return ExprKind::Cast { source: self.mirror_expr(source)}; + }; + + let res = self.typeck_results().qpath_res(qpath, source.hir_id); + let ty = self.typeck_results().node_type(source.hir_id); + let ty::Adt(adt_def, substs) = ty.kind() else { + return ExprKind::Cast { source: self.mirror_expr(source)}; + }; + + let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res else { + return ExprKind::Cast { source: self.mirror_expr(source)}; + }; + + let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); + let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx); + + use rustc_middle::ty::util::IntTypeExt; + let ty = adt_def.repr().discr_type(); + let discr_ty = ty.to_ty(tcx); + + let param_env_ty = self.param_env.and(discr_ty); + let size = tcx + .layout_of(param_env_ty) + .unwrap_or_else(|e| { + panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) + }) + .size; + + let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap(); + let kind = ExprKind::NonHirLiteral { lit, user_ty: None }; + let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind }); + + let source = match discr_did { + // in case we are offsetting from a computed discriminant + // and not the beginning of discriminants (which is always `0`) + Some(did) => { + let kind = ExprKind::NamedConst { def_id: did, substs, user_ty: None }; + let lhs = + self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind }); + let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; + self.thir.exprs.push(Expr { + temp_lifetime, + ty: discr_ty, + span: span, + kind: bin, + }) + } + None => offset, + }; + + ExprKind::Cast { source } + } + } + fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; let expr_ty = self.typeck_results().expr_ty(expr); @@ -604,98 +697,7 @@ impl<'tcx> Cx<'tcx> { expr, cast_ty.hir_id, user_ty, ); - // Check to see if this cast is a "coercion cast", where the cast is actually done - // using a coercion (or is a no-op). - let cast = if self.typeck_results().is_coercion_cast(source.hir_id) { - // Convert the lexpr to a vexpr. - ExprKind::Use { source: self.mirror_expr(source) } - } else if self.typeck_results().expr_ty(source).is_region_ptr() { - // Special cased so that we can type check that the element - // type of the source matches the pointed to type of the - // destination. - ExprKind::Pointer { - source: self.mirror_expr(source), - cast: PointerCast::ArrayToPointer, - } - } else { - // check whether this is casting an enum variant discriminant - // to prevent cycles, we refer to the discriminant initializer - // which is always an integer and thus doesn't need to know the - // enum's layout (or its tag type) to compute it during const eval - // Example: - // enum Foo { - // A, - // B = A as isize + 4, - // } - // The correct solution would be to add symbolic computations to miri, - // so we wouldn't have to compute and store the actual value - let var = if let hir::ExprKind::Path(ref qpath) = source.kind { - let res = self.typeck_results().qpath_res(qpath, source.hir_id); - self.typeck_results().node_type(source.hir_id).ty_adt_def().and_then( - |adt_def| match res { - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const), - variant_ctor_id, - ) => { - let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); - let (d, o) = adt_def.discriminant_def_for_variant(idx); - use rustc_middle::ty::util::IntTypeExt; - let ty = adt_def.repr().discr_type(); - let ty = ty.to_ty(tcx); - Some((d, o, ty)) - } - _ => None, - }, - ) - } else { - None - }; - - let source = if let Some((did, offset, var_ty)) = var { - let param_env_ty = self.param_env.and(var_ty); - let size = tcx - .layout_of(param_env_ty) - .unwrap_or_else(|e| { - panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) - }) - .size; - let lit = ScalarInt::try_from_uint(offset as u128, size).unwrap(); - let kind = ExprKind::NonHirLiteral { lit, user_ty: None }; - let offset = self.thir.exprs.push(Expr { - temp_lifetime, - ty: var_ty, - span: expr.span, - kind, - }); - match did { - Some(did) => { - // in case we are offsetting from a computed discriminant - // and not the beginning of discriminants (which is always `0`) - let substs = InternalSubsts::identity_for_item(tcx, did); - let kind = - ExprKind::NamedConst { def_id: did, substs, user_ty: None }; - let lhs = self.thir.exprs.push(Expr { - temp_lifetime, - ty: var_ty, - span: expr.span, - kind, - }); - let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; - self.thir.exprs.push(Expr { - temp_lifetime, - ty: var_ty, - span: expr.span, - kind: bin, - }) - } - None => offset, - } - } else { - self.mirror_expr(source) - }; - - ExprKind::Cast { source: source } - }; + let cast = self.mirror_expr_cast(*source, temp_lifetime, expr.span); if let Some(user_ty) = user_ty { // NOTE: Creating a new Expr and wrapping a Cast inside of it may be diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f3bdd63ee6d..113af328a91 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -6,6 +6,7 @@ #![feature(let_chains)] #![feature(let_else)] #![feature(never_type)] +#![feature(rustc_attrs)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ba325d70422..0869ed65ad2 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -244,7 +244,7 @@ impl MultiSugg { } #[derive(SessionDiagnostic)] -#[error(slug = "parser-maybe-report-ambiguous-plus")] +#[error(parser::maybe_report_ambiguous_plus)] struct AmbiguousPlus { pub sum_ty: String, #[primary_span] @@ -253,7 +253,7 @@ struct AmbiguousPlus { } #[derive(SessionDiagnostic)] -#[error(code = "E0178", slug = "parser-maybe-recover-from-bad-type-plus")] +#[error(parser::maybe_recover_from_bad_type_plus, code = "E0178")] struct BadTypePlus { pub ty: String, #[primary_span] @@ -265,7 +265,7 @@ struct BadTypePlus { #[derive(SessionSubdiagnostic)] pub enum BadTypePlusSub { #[suggestion( - slug = "parser-add-paren", + parser::add_paren, code = "{sum_with_parens}", applicability = "machine-applicable" )] @@ -274,12 +274,12 @@ pub enum BadTypePlusSub { #[primary_span] span: Span, }, - #[label(slug = "parser-forgot-paren")] + #[label(parser::forgot_paren)] ForgotParen { #[primary_span] span: Span, }, - #[label(slug = "parser-expect-path")] + #[label(parser::expect_path)] ExpectPath { #[primary_span] span: Span, @@ -287,7 +287,7 @@ pub enum BadTypePlusSub { } #[derive(SessionDiagnostic)] -#[error(slug = "parser-maybe-recover-from-bad-qpath-stage-2")] +#[error(parser::maybe_recover_from_bad_qpath_stage_2)] struct BadQPathStage2 { #[primary_span] #[suggestion(applicability = "maybe-incorrect")] @@ -296,7 +296,7 @@ struct BadQPathStage2 { } #[derive(SessionDiagnostic)] -#[error(slug = "parser-incorrect-semicolon")] +#[error(parser::incorrect_semicolon)] struct IncorrectSemicolon<'a> { #[primary_span] #[suggestion_short(applicability = "machine-applicable")] @@ -307,26 +307,26 @@ struct IncorrectSemicolon<'a> { } #[derive(SessionDiagnostic)] -#[error(slug = "parser-incorrect-use-of-await")] +#[error(parser::incorrect_use_of_await)] struct IncorrectUseOfAwait { #[primary_span] - #[suggestion(message = "parentheses-suggestion", applicability = "machine-applicable")] + #[suggestion(parser::parentheses_suggestion, applicability = "machine-applicable")] span: Span, } #[derive(SessionDiagnostic)] -#[error(slug = "parser-incorrect-use-of-await")] +#[error(parser::incorrect_use_of_await)] struct IncorrectAwait { #[primary_span] span: Span, - #[suggestion(message = "postfix-suggestion", code = "{expr}.await{question_mark}")] + #[suggestion(parser::postfix_suggestion, code = "{expr}.await{question_mark}")] sugg_span: (Span, Applicability), expr: String, question_mark: &'static str, } #[derive(SessionDiagnostic)] -#[error(slug = "parser-in-in-typo")] +#[error(parser::in_in_typo)] struct InInTypo { #[primary_span] span: Span, @@ -357,6 +357,7 @@ impl<'a> DerefMut for SnapshotParser<'a> { } impl<'a> Parser<'a> { + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub(super) fn span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -365,6 +366,7 @@ impl<'a> Parser<'a> { err.span_err(sp, self.diagnostic()) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 81bab0e3513..2c43563b104 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -7,6 +7,7 @@ use super::{ }; use crate::maybe_recover_from_interpolated_ty_qpath; +use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::Spacing; @@ -26,7 +27,6 @@ use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; -use std::mem; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -2343,7 +2343,9 @@ 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.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let cond = 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. @@ -2356,6 +2358,13 @@ impl<'a> Parser<'a> { /// 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, @@ -2672,6 +2681,8 @@ 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) { match expr.kind { ExprKind::Binary(_, ref lhs, ref rhs) => { @@ -2694,7 +2705,7 @@ impl<'a> Parser<'a> { )?; let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; - let cond = this.parse_expr()?; + 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); if has_let_expr { if does_not_have_bin_op { @@ -3256,4 +3267,17 @@ impl<'a> Parser<'a> { Ok((res, trailing)) }) } + + // Calls `f` with the internal `let_expr_allowed` set to `let_expr_allowed` and then + // sets the internal `let_expr_allowed` back to its original value. + fn with_let_management<T>( + &mut self, + let_expr_allowed: bool, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + let last_let_expr_allowed = mem::replace(&mut self.let_expr_allowed, let_expr_allowed); + let rslt = f(self); + self.let_expr_allowed = last_let_expr_allowed; + rslt + } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 6d6667717f0..acdf121522a 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -147,12 +147,15 @@ pub struct Parser<'a> { /// This allows us to recover when the user forget to add braces around /// multiple statements in the closure body. pub current_closure: Option<ClosureSpans>, + /// Used to track where `let`s are allowed. For example, `if true && let 1 = 1` is valid + /// but `[1, 2, 3][let _ = ()]` is not. + let_expr_allowed: bool, } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 328); +rustc_data_structures::static_assert_size!(Parser<'_>, 336); /// Stores span information about a closure. #[derive(Clone)] @@ -455,6 +458,7 @@ impl<'a> Parser<'a> { inner_attr_ranges: Default::default(), }, current_closure: None, + let_expr_allowed: false, }; // Make parser point to the first token. diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index d952e288a64..5785921fb1e 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -4,14 +4,15 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_middle = { path = "../rustc_middle" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } +rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } -rustc_typeck = { path = "../rustc_typeck" } +rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_data_structures = { path = "../rustc_data_structures" } rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_typeck = { path = "../rustc_typeck" } tracing = "0.1" diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs new file mode 100644 index 00000000000..482721d373a --- /dev/null +++ b/compiler/rustc_privacy/src/errors.rs @@ -0,0 +1,75 @@ +use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error(privacy::field_is_private, code = "E0451")] +pub struct FieldIsPrivate { + #[primary_span] + pub span: Span, + pub field_name: Symbol, + pub variant_descr: &'static str, + pub def_path_str: String, + #[subdiagnostic] + pub label: FieldIsPrivateLabel, +} + +#[derive(SessionSubdiagnostic)] +pub enum FieldIsPrivateLabel { + #[label(privacy::field_is_private_is_update_syntax_label)] + IsUpdateSyntax { + #[primary_span] + span: Span, + field_name: Symbol, + }, + #[label(privacy::field_is_private_label)] + Other { + #[primary_span] + span: Span, + }, +} + +#[derive(SessionDiagnostic)] +#[error(privacy::item_is_private)] +pub struct ItemIsPrivate<'a> { + #[primary_span] + #[label] + pub span: Span, + pub kind: &'a str, + pub descr: String, +} + +#[derive(SessionDiagnostic)] +#[error(privacy::unnamed_item_is_private)] +pub struct UnnamedItemIsPrivate { + #[primary_span] + pub span: Span, + pub kind: &'static str, +} + +// Duplicate of `InPublicInterface` but with a different error code, shares the same slug. +#[derive(SessionDiagnostic)] +#[error(privacy::in_public_interface, code = "E0445")] +pub struct InPublicInterfaceTraits<'a> { + #[primary_span] + #[label] + pub span: Span, + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, + #[label(privacy::visibility_label)] + pub vis_span: Span, +} + +// Duplicate of `InPublicInterfaceTraits` but with a different error code, shares the same slug. +#[derive(SessionDiagnostic)] +#[error(privacy::in_public_interface, code = "E0446")] +pub struct InPublicInterface<'a> { + #[primary_span] + #[label] + pub span: Span, + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, + #[label(privacy::visibility_label)] + pub vis_span: Span, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index b27c986d0f9..238c917bbc3 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,15 +1,19 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(associated_type_defaults)] #![feature(control_flow_enum)] +#![feature(rustc_private)] #![feature(try_blocks)] -#![feature(associated_type_defaults)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![cfg_attr(not(bootstrap), deny(rustc::untranslatable_diagnostic))] +#![cfg_attr(not(bootstrap), deny(rustc::diagnostic_outside_of_impl))] + +mod errors; use rustc_ast::MacroDef; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; @@ -34,6 +38,11 @@ use std::marker::PhantomData; use std::ops::ControlFlow; use std::{cmp, fmt, mem}; +use errors::{ + FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, + UnnamedItemIsPrivate, +}; + //////////////////////////////////////////////////////////////////////////////// /// Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// @@ -935,23 +944,17 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.current_item); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1; if !field.vis.is_accessible_from(def_id, self.tcx) { - let label = if in_update_syntax { - format!("field `{}` is private", field.name) - } else { - "private field".to_string() - }; - - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(FieldIsPrivate { span, - E0451, - "field `{}` of {} `{}` is private", - field.name, - def.variant_descr(), - self.tcx.def_path_str(def.did()) - ) - .span_label(span, label) - .emit(); + field_name: field.name, + variant_descr: def.variant_descr(), + def_path_str: self.tcx.def_path_str(def.did()), + label: if in_update_syntax { + FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name } + } else { + FieldIsPrivateLabel::Other { span } + }, + }); } } } @@ -1075,11 +1078,11 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { let is_error = !self.item_is_accessible(def_id); if is_error { - self.tcx - .sess - .struct_span_err(self.span, &format!("{} `{}` is private", kind, descr)) - .span_label(self.span, &format!("private {}", kind)) - .emit(); + self.tcx.sess.emit_err(ItemIsPrivate { + span: self.span, + kind, + descr: descr.to_string(), + }); } is_error } @@ -1250,13 +1253,10 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()), }; let kind = kind.descr(def_id); - let msg = match name { - Some(name) => format!("{} `{}` is private", kind, name), - None => format!("{} is private", kind), + let _ = match name { + Some(name) => sess.emit_err(ItemIsPrivate { span, kind, descr: name }), + None => sess.emit_err(UnnamedItemIsPrivate { span, kind }), }; - sess.struct_span_err(span, &msg) - .span_label(span, &format!("private {}", kind)) - .emit(); return; } } @@ -1753,22 +1753,31 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { } } }; - let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr); let span = self.tcx.def_span(self.item_def_id.to_def_id()); if self.has_old_errors || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { - let mut err = if kind == "trait" { - struct_span_err!(self.tcx.sess, span, E0445, "{}", make_msg()) - } else { - struct_span_err!(self.tcx.sess, span, E0446, "{}", make_msg()) - }; + let descr = descr.to_string(); let vis_span = self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); - err.span_label(span, format!("can't leak {} {}", vis_descr, kind)); - err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); - err.emit(); + if kind == "trait" { + self.tcx.sess.emit_err(InPublicInterfaceTraits { + span, + vis_descr, + kind, + descr, + vis_span, + }); + } else { + self.tcx.sess.emit_err(InPublicInterface { + span, + vis_descr, + kind, + descr, + vis_span, + }); + } } else { let err_code = if kind == "trait" { "E0445" } else { "E0446" }; self.tcx.struct_span_lint_hir( @@ -1776,7 +1785,12 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { hir_id, span, |lint| { - lint.build(&format!("{} (error {})", make_msg(), err_code)).emit(); + lint.build(&format!( + "{} (error {})", + format!("{} {} `{}` in public interface", vis_descr, kind, descr), + err_code + )) + .emit(); }, ); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f1814eebfa6..b5058fd699a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -280,6 +280,7 @@ impl Session { self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn<S: Into<MultiSpan>>( &self, sp: S, @@ -287,6 +288,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>( &self, sp: S, @@ -295,6 +297,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_expectation(sp, msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -303,9 +306,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn_with_expectation( &self, msg: impl Into<DiagnosticMessage>, @@ -313,6 +318,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_expectation(msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_allow<S: Into<MultiSpan>>( &self, sp: S, @@ -320,9 +326,11 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_allow(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_expect( &self, msg: impl Into<DiagnosticMessage>, @@ -330,6 +338,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -337,6 +346,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -346,12 +356,14 @@ impl Session { self.diagnostic().struct_span_err_with_code(sp, msg, code) } // FIXME: This method should be removed (every error should have an associated error code). + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_err( &self, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.parse_sess.struct_err(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_err_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -359,6 +371,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err_with_code(msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_warn_with_code( &self, msg: impl Into<DiagnosticMessage>, @@ -366,6 +379,7 @@ impl Session { ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn_with_code(msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_fatal<S: Into<MultiSpan>>( &self, sp: S, @@ -373,6 +387,7 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -381,13 +396,16 @@ impl Session { ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_fatal(msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().span_fatal(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -396,9 +414,11 @@ impl Session { ) -> ! { self.diagnostic().span_fatal_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().fatal(msg).raise() } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err_or_warn<S: Into<MultiSpan>>( &self, is_warning: bool, @@ -411,6 +431,7 @@ impl Session { self.span_err(sp, msg); } } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err<S: Into<MultiSpan>>( &self, sp: S, @@ -418,6 +439,7 @@ impl Session { ) -> ErrorGuaranteed { self.diagnostic().span_err(sp, msg) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, @@ -426,6 +448,7 @@ impl Session { ) { self.diagnostic().span_err_with_code(sp, msg, code) } + #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 95ea7029617..227127aed50 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -956,7 +956,7 @@ impl SourceMap { } pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> { - let prev_span = self.span_extend_to_prev_str(span, "fn", true, true).unwrap_or(span); + let prev_span = self.span_extend_to_prev_str(span, "fn", true, true)?; if let Ok(snippet) = self.span_to_snippet(prev_span) { debug!( "generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}", diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8a6941a4516..4e28d2b6001 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -135,6 +135,9 @@ symbols! { Arguments, AsMut, AsRef, + AssertParamIsClone, + AssertParamIsCopy, + AssertParamIsEq, AtomicBool, AtomicI128, AtomicI16, @@ -567,8 +570,10 @@ symbols! { debug_assert_ne_macro, debug_assertions, debug_struct, + debug_struct_fields_finish, debug_trait_builder, debug_tuple, + debug_tuple_fields_finish, debugger_visualizer, decl_macro, declare_lint_pass, diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index a8ddcc9bfac..59dbea70534 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -8,6 +8,7 @@ //! LLVM. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] #![feature(let_else)] diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index 86f76fdb6a7..9d36e37d7b8 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -8,7 +8,7 @@ pub fn target() -> Target { // FIXME: The leak sanitizer currently fails the tests, see #88132. base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD; - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), "arm64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-arch", "arm64"]); base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove()); // Clang automatically chooses a more specific target based on diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs index 965b254c289..162b091b269 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs @@ -2,20 +2,13 @@ // uefi-base module for generic UEFI options. use super::uefi_msvc_base; -use crate::spec::{LinkerFlavor, LldFlavor, Target}; +use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = uefi_msvc_base::opts(); base.max_atomic_width = Some(64); - - let pre_link_args_msvc = vec!["/machine:arm64".into()]; - - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); - base.pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) - .unwrap() - .extend(pre_link_args_msvc); + base.add_pre_link_args(LinkerFlavor::Msvc, &["/machine:arm64"]); Target { llvm_target: "aarch64-unknown-windows".into(), diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs index 67df73fa935..8c2a9bcfde6 100644 --- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs @@ -1,19 +1,13 @@ -use crate::spec::{cvs, LinkArgs, LinkerFlavor, RelocModel, Target, TargetOptions}; +use crate::spec::{cvs, LinkerFlavor, RelocModel, Target, TargetOptions}; /// A base target for Nintendo 3DS devices using the devkitARM toolchain. /// /// Requires the devkitARM toolchain for 3DS targets on the host system. pub fn target() -> Target { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( + let pre_link_args = TargetOptions::link_args( LinkerFlavor::Gcc, - vec![ - "-specs=3dsx.specs".into(), - "-mtune=mpcore".into(), - "-mfloat-abi=hard".into(), - "-mtp=soft".into(), - ], + &["-specs=3dsx.specs", "-mtune=mpcore", "-mfloat-abi=hard", "-mtp=soft"], ); Target { diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs index 2afd93fcad8..38c117a495e 100644 --- a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs @@ -10,7 +10,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::android_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]); Target { llvm_target: "armv7-none-linux-android".into(), pointer_width: 32, diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs index 269bf8b8bcd..b4cf2c5ee22 100644 --- a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs @@ -2,10 +2,6 @@ use super::{wasm32_unknown_emscripten, LinkerFlavor, Target}; pub fn target() -> Target { let mut target = wasm32_unknown_emscripten::target(); - target.post_link_args.entry(LinkerFlavor::Em).or_default().extend(vec![ - "-sWASM=0".into(), - "--memory-init-file".into(), - "0".into(), - ]); + target.add_post_link_args(LinkerFlavor::Em, &["-sWASM=0", "--memory-init-file", "0"]); target } diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index c288e8b0e9e..4fd6c06394d 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -3,7 +3,8 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; /// A base target for AVR devices using the GNU toolchain. /// /// Requires GNU avr-gcc and avr-binutils on the host system. -pub fn target(target_cpu: &'static str) -> Target { +/// FIXME: Remove the second parameter when const string concatenation is possible. +pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { Target { arch: "avr".into(), data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".into(), @@ -17,10 +18,8 @@ pub fn target(target_cpu: &'static str) -> Target { linker: Some("avr-gcc".into()), executables: true, eh_frame_header: false, - pre_link_args: [(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu).into()])] - .into_iter() - .collect(), - late_link_args: [(LinkerFlavor::Gcc, vec!["-lgcc".into()])].into_iter().collect(), + pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]), + late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]), max_atomic_width: Some(0), atomic_cas: false, ..TargetOptions::default() diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs index 6871ca0f789..6c16b03cc28 100644 --- a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs +++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs @@ -1,5 +1,5 @@ use crate::spec::Target; pub fn target() -> Target { - super::avr_gnu_base::target("atmega328") + super::avr_gnu_base::target("atmega328", "-mmcu=atmega328") } diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index b64875e32bd..b02b70f76ee 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -1,23 +1,20 @@ -use crate::spec::{ - crt_objects, cvs, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions, -}; +use crate::spec::{crt_objects, cvs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Lld(LldFlavor::Ld), - vec![ - "--build-id".into(), - "--hash-style=gnu".into(), - "-z".into(), - "max-page-size=4096".into(), - "-z".into(), - "now".into(), - "-z".into(), - "rodynamic".into(), - "-z".into(), - "separate-loadable-segments".into(), - "--pack-dyn-relocs=relr".into(), + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Ld, + &[ + "--build-id", + "--hash-style=gnu", + "-z", + "max-page-size=4096", + "-z", + "now", + "-z", + "rodynamic", + "-z", + "separate-loadable-segments", + "--pack-dyn-relocs=relr", ], ); diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index 7cbd42417e6..e43153177f0 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -1,10 +1,9 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Lld(LldFlavor::Ld), - vec!["--build-id".into(), "--hash-style=gnu".into(), "--Bstatic".into()], + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Ld, + &["--build-id", "--hash-style=gnu", "--Bstatic"], ); TargetOptions { diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs index ad716a6cd5a..1718bd77b86 100644 --- a/compiler/rustc_target/src/spec/i686_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); base.cpu = "yonah".into(); base.max_atomic_width = Some(64); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs index 554b0f34499..6318654399c 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs @@ -1,19 +1,16 @@ -use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target}; +use crate::spec::{FramePointer, LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_gnu_base::opts(); base.cpu = "pentium4".into(); - base.pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pe".into()]); base.max_atomic_width = Some(64); base.frame_pointer = FramePointer::Always; // Required for backtraces base.linker = Some("i686-w64-mingw32-gcc".into()); // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - base.pre_link_args - .entry(LinkerFlavor::Gcc) - .or_default() - .push("-Wl,--large-address-aware".into()); + base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]); Target { llvm_target: "i686-pc-windows-gnu".into(), diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs index fb0cb6a6943..f4ceaa1ca4b 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs @@ -1,24 +1,22 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target}; +use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - let pre_link_args_msvc = vec![ - // Mark all dynamic libraries and executables as compatible with the larger 4GiB address - // space available to x86 Windows binaries on x86_64. - "/LARGEADDRESSAWARE".into(), - // Ensure the linker will only produce an image if it can also produce a table of - // the image's safe exception handlers. - // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers - "/SAFESEH".into(), - ]; - base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); - base.pre_link_args - .entry(LinkerFlavor::Lld(LldFlavor::Link)) - .or_default() - .extend(pre_link_args_msvc); + base.add_pre_link_args( + LinkerFlavor::Msvc, + &[ + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + "/LARGEADDRESSAWARE", + // Ensure the linker will only produce an image if it can also produce a table of + // the image's safe exception handlers. + // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers + "/SAFESEH", + ], + ); // Workaround for #95429 base.has_thread_local = false; diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs index 9f0cb04c65d..aff284bf2bc 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs @@ -4,9 +4,7 @@ pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - let pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - pre_link_args.push("-m32".into()); - pre_link_args.push("-Wl,-znotext".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-znotext"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs index d1af163f1cf..87aa74e406c 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs index 0998c618f31..765803d1692 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs index a697f292da0..d9492804349 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs @@ -4,8 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-Wl,-melf_i386".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-melf_i386"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs index 2807d328205..8de698b51f0 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs index 78462eb63b8..7f25a1a16c1 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs @@ -4,8 +4,7 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-fuse-ld=lld".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-fuse-ld=lld"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs index 75f7a2209c8..d52810d2fb0 100644 --- a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs @@ -1,18 +1,15 @@ -use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target}; +use crate::spec::{FramePointer, LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "pentium4".into(); - base.pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pe".into()]); base.max_atomic_width = Some(64); base.frame_pointer = FramePointer::Always; // Required for backtraces // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - base.pre_link_args - .entry(LinkerFlavor::Gcc) - .or_default() - .push("-Wl,--large-address-aware".into()); + base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]); Target { llvm_target: "i686-pc-windows-gnu".into(), diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs index d51ed7c1f7a..f62404e8279 100644 --- a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs index ef8f90a4da8..b0e1b109be1 100644 --- a/compiler/rustc_target/src/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -1,10 +1,9 @@ -use crate::spec::{cvs, FramePointer, LinkArgs, LinkerFlavor, TargetOptions}; +use crate::spec::{cvs, FramePointer, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let mut late_link_args = LinkArgs::new(); - late_link_args.insert( + let late_link_args = TargetOptions::link_args( LinkerFlavor::Gcc, - vec![ + &[ // The illumos libc contains a stack unwinding implementation, as // does libgcc_s. The latter implementation includes several // additional symbols that are not always in base libc. To force @@ -15,13 +14,13 @@ pub fn opts() -> TargetOptions { // FIXME: This should be replaced by a more complete and generic // mechanism for controlling the order of library arguments passed // to the linker. - "-lc".into(), + "-lc", // LLVM will insert calls to the stack protector functions // "__stack_chk_fail" and "__stack_chk_guard" into code in native // object files. Some platforms include these symbols directly in // libc, but at least historically these have been provided in // libssp.so on illumos and Solaris systems. - "-lssp".into(), + "-lssp", ], ); diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs index 03e0934ea5e..e3522de6de0 100644 --- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -1,13 +1,11 @@ use crate::spec::{cvs, Target, TargetOptions}; -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, RelocModel}; +use crate::spec::{LinkerFlavor, LldFlavor, RelocModel}; // The PSP has custom linker requirements. const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld"); pub fn target() -> Target { - let mut pre_link_args = LinkArgs::new(); - pre_link_args - .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["--emit-relocs".into(), "--nmagic".into()]); + let pre_link_args = TargetOptions::link_args(LinkerFlavor::Ld, &["--emit-relocs", "--nmagic"]); Target { llvm_target: "mipsel-sony-psp".into(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index da0589cdd20..a08603da040 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1459,6 +1459,44 @@ pub struct TargetOptions { pub supports_stack_protector: bool, } +/// Add arguments for the given flavor and also for its "twin" flavors +/// that have a compatible command line interface. +fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'static str]) { + let mut insert = |flavor| { + link_args.entry(flavor).or_default().extend(args.iter().copied().map(Cow::Borrowed)) + }; + insert(flavor); + match flavor { + LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)), + LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)), + LinkerFlavor::Lld(LldFlavor::Wasm) => {} + LinkerFlavor::Lld(lld_flavor) => { + panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor) + } + LinkerFlavor::Gcc + | LinkerFlavor::Em + | LinkerFlavor::L4Bender + | LinkerFlavor::BpfLinker + | LinkerFlavor::PtxLinker => {} + } +} + +impl TargetOptions { + fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs { + let mut link_args = LinkArgs::new(); + add_link_args(&mut link_args, flavor, args); + link_args + } + + fn add_pre_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) { + add_link_args(&mut self.pre_link_args, flavor, args); + } + + fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) { + add_link_args(&mut self.post_link_args, flavor, args); + } +} + impl Default for TargetOptions { /// Creates a set of "sane defaults" for any target. This is still /// incomplete, and if used for compilation, will certainly not work. diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index 00cc9620243..c4df4b546e3 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -1,14 +1,9 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions}; +use crate::spec::{LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions}; pub fn opts() -> TargetOptions { - let pre_link_args_msvc = vec![ - // Suppress the verbose logo and authorship debugging output, which would needlessly - // clog any log files. - "/NOLOGO".into(), - ]; - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone()); - pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc); + // Suppress the verbose logo and authorship debugging output, which would needlessly + // clog any log files. + let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc, &["/NOLOGO"]); TargetOptions { linker_flavor: LinkerFlavor::Msvc, diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs index 595769c4bfa..803453c4ac4 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "ppc64".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 24d5d187e1a..5413c4f33ff 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index 0f465ccfe77..159335eb607 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index 491d344aedb..b7420d232ca 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "ppc64".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs index b198e667ccc..a3d18004371 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "ppc64le".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs index 09e3936db26..e18ff3be448 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64le".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs index 8a947b091cb..b84943d23a9 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64le".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs index c27b84775df..516b2de37ea 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs @@ -3,12 +3,8 @@ use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); // Extra hint to linker that we are generating secure-PLT code. - base.pre_link_args - .entry(LinkerFlavor::Gcc) - .or_default() - .push("--target=powerpc-unknown-freebsd13.0".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--target=powerpc-unknown-freebsd13.0"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs index 88f61500e3c..6686a0bbf04 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs index 3ee548750b9..6a250f4b51c 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs index ce33c787f33..34200c67906 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs index 998225f4dae..60661ef9b5d 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs index 76709cec591..3f24966e06e 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into()); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--secure-plt"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs index 7b5d1242c52..0f04f41f9e5 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".into()); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe", "--secure-plt"]); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs index 718303a4b4d..836ab0e3728 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "v9".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs index 2aaa0ca6df8..4a192df392f 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.endian = Endian::Big; base.cpu = "v9".into(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs index 71d3de0bfd1..ea4fafa4b06 100644 --- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { base.endian = Endian::Big; base.cpu = "v9".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mv8plus".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-mv8plus"]); Target { llvm_target: "sparc-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs index 79ae54aa666..aac09181a74 100644 --- a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); base.endian = Endian::Big; - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // llvm calls this "v9" base.cpu = "v9".into(); base.vendor = "sun".into(); diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 0865ca7ea7d..c7c5a231901 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -1,4 +1,5 @@ use super::super::*; +use std::assert_matches::assert_matches; // Test target self-consistency and JSON encoding/decoding roundtrip. pub(super) fn test_target(target: Target) { @@ -14,35 +15,105 @@ impl Target { assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64"); assert!(self.is_like_windows || !self.is_like_msvc); - // Check that LLD with the given flavor is treated identically to the linker it emulates. - // If your target really needs to deviate from the rules below, except it and document the - // reasons. - assert_eq!( - self.linker_flavor == LinkerFlavor::Msvc - || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), - self.lld_flavor == LldFlavor::Link, - ); - assert_eq!(self.is_like_msvc, self.lld_flavor == LldFlavor::Link); - for args in &[ + // Check that default linker flavor and lld flavor are compatible + // with some other key properties. + assert_eq!(self.is_like_osx, matches!(self.lld_flavor, LldFlavor::Ld64)); + assert_eq!(self.is_like_msvc, matches!(self.lld_flavor, LldFlavor::Link)); + assert_eq!(self.is_like_wasm, matches!(self.lld_flavor, LldFlavor::Wasm)); + assert_eq!(self.os == "l4re", matches!(self.linker_flavor, LinkerFlavor::L4Bender)); + assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::Em)); + assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::BpfLinker)); + assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::PtxLinker)); + + for args in [ &self.pre_link_args, &self.late_link_args, &self.late_link_args_dynamic, &self.late_link_args_static, &self.post_link_args, ] { + for (&flavor, flavor_args) in args { + assert!(!flavor_args.is_empty()); + // Check that flavors mentioned in link args are compatible with the default flavor. + match (self.linker_flavor, self.lld_flavor) { + ( + LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc, + LldFlavor::Ld, + ) => { + assert_matches!( + flavor, + LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc + ) + } + (LinkerFlavor::Gcc, LldFlavor::Ld64) => { + assert_matches!(flavor, LinkerFlavor::Gcc) + } + (LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link), LldFlavor::Link) => { + assert_matches!( + flavor, + LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link) + ) + } + (LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc, LldFlavor::Wasm) => { + assert_matches!( + flavor, + LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc + ) + } + (LinkerFlavor::L4Bender, LldFlavor::Ld) => { + assert_matches!(flavor, LinkerFlavor::L4Bender) + } + (LinkerFlavor::Em, LldFlavor::Wasm) => { + assert_matches!(flavor, LinkerFlavor::Em) + } + (LinkerFlavor::BpfLinker, LldFlavor::Ld) => { + assert_matches!(flavor, LinkerFlavor::BpfLinker) + } + (LinkerFlavor::PtxLinker, LldFlavor::Ld) => { + assert_matches!(flavor, LinkerFlavor::PtxLinker) + } + flavors => unreachable!("unexpected flavor combination: {:?}", flavors), + } + + // Check that link args for cc and non-cc versions of flavors are consistent. + let check_noncc = |noncc_flavor| { + if let Some(noncc_args) = args.get(&noncc_flavor) { + for arg in flavor_args { + if let Some(suffix) = arg.strip_prefix("-Wl,") { + assert!(noncc_args.iter().any(|a| a == suffix)); + } + } + } + }; + match self.linker_flavor { + LinkerFlavor::Gcc => match self.lld_flavor { + LldFlavor::Ld => { + check_noncc(LinkerFlavor::Ld); + check_noncc(LinkerFlavor::Lld(LldFlavor::Ld)); + } + LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)), + LldFlavor::Ld64 | LldFlavor::Link => {} + }, + _ => {} + } + } + + // Check that link args for lld and non-lld versions of flavors are consistent. + assert_eq!(args.get(&LinkerFlavor::Ld), args.get(&LinkerFlavor::Lld(LldFlavor::Ld))); assert_eq!( args.get(&LinkerFlavor::Msvc), args.get(&LinkerFlavor::Lld(LldFlavor::Link)), ); - if args.contains_key(&LinkerFlavor::Msvc) { - assert_eq!(self.lld_flavor, LldFlavor::Link); - } } + assert!( (self.pre_link_objects_fallback.is_empty() && self.post_link_objects_fallback.is_empty()) || self.crt_objects_fallback.is_some() ); + + // If your target really needs to deviate from the rules below, + // except it and document the reasons. // Keep the default "unknown" vendor instead. assert_ne!(self.vendor, ""); if !self.can_use_os_unknown() { diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs index f6cbbd38cf4..4d09d3a4d10 100644 --- a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); @@ -9,12 +9,7 @@ pub fn target() -> Target { // should be smart enough to insert branch islands only // where necessary, but this is not the observed behavior. // Disabling the LBR optimization works around the issue. - let pre_link_args_msvc = "/OPT:NOLBR"; - base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().push(pre_link_args_msvc.into()); - base.pre_link_args - .entry(LinkerFlavor::Lld(LldFlavor::Link)) - .or_default() - .push(pre_link_args_msvc.into()); + base.add_pre_link_args(LinkerFlavor::Msvc, &["/OPT:NOLBR"]); Target { llvm_target: "thumbv7a-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs index 9a3e8b5c5f8..4cad9e18370 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs @@ -10,7 +10,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::android_base::opts(); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]); Target { llvm_target: "armv7-none-linux-android".into(), pointer_width: 32, diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index bc7244b3a45..aee8eb2e31c 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -14,27 +14,25 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, Target pub fn opts() -> TargetOptions { let mut base = super::msvc_base::opts(); - let pre_link_args_msvc = vec![ - // Non-standard subsystems have no default entry-point in PE+ files. We have to define - // one. "efi_main" seems to be a common choice amongst other implementations and the - // spec. - "/entry:efi_main".into(), - // COFF images have a "Subsystem" field in their header, which defines what kind of - // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, - // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, - // which is very likely the most common option. Individual projects can override this - // with custom linker flags. - // The subsystem-type only has minor effects on the application. It defines the memory - // regions the application is loaded into (runtime-drivers need to be put into - // reserved areas), as well as whether a return from the entry-point is treated as - // exit (default for applications). - "/subsystem:efi_application".into(), - ]; - base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); - base.pre_link_args - .entry(LinkerFlavor::Lld(LldFlavor::Link)) - .or_default() - .extend(pre_link_args_msvc); + base.add_pre_link_args( + LinkerFlavor::Msvc, + &[ + // Non-standard subsystems have no default entry-point in PE+ files. We have to define + // one. "efi_main" seems to be a common choice amongst other implementations and the + // spec. + "/entry:efi_main", + // COFF images have a "Subsystem" field in their header, which defines what kind of + // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, + // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, + // which is very likely the most common option. Individual projects can override this + // with custom linker flags. + // The subsystem-type only has minor effects on the application. It defines the memory + // regions the application is loaded into (runtime-drivers need to be put into + // reserved areas), as well as whether a return from the entry-point is treated as + // exit (default for applications). + "/subsystem:efi_application", + ], + ); TargetOptions { os: "uefi".into(), diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index f1087db09d1..c7e7d221086 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -1,22 +1,12 @@ use super::{cvs, wasm_base}; -use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; pub fn target() -> Target { - let mut options = wasm_base::options(); - - let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - - // Rust really needs a way for users to specify exports and imports in - // the source code. --export-dynamic isn't the right tool for this job, - // however it does have the side effect of automatically exporting a lot - // of symbols, which approximates what people want when compiling for - // wasm32-unknown-unknown expect, so use it for now. - clang_args.push("--export-dynamic".into()); - - let mut post_link_args = LinkArgs::new(); - post_link_args.insert( + // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests. + let pre_link_args = LinkArgs::new(); + let post_link_args = TargetOptions::link_args( LinkerFlavor::Em, - vec!["-sABORTING_MALLOC=0".into(), "-Wl,--fatal-warnings".into()], + &["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"], ); let opts = TargetOptions { @@ -26,11 +16,13 @@ pub fn target() -> Target { // functionality, and a .wasm file. exe_suffix: ".js".into(), linker: None, + pre_link_args, + post_link_args, + relocation_model: RelocModel::Pic, panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, - post_link_args, families: cvs!["unix", "wasm"], - ..options + ..wasm_base::options() }; Target { llvm_target: "wasm32-unknown-emscripten".into(), diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 214b5fce5a6..4e2927dd913 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -29,27 +29,30 @@ pub fn target() -> Target { // code on this target due to this ABI mismatch. options.default_adjusted_cabi = Some(Abi::Wasm); - let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - - // Make sure clang uses LLD as its linker and is configured appropriately - // otherwise - clang_args.push("--target=wasm32-unknown-unknown".into()); - - // For now this target just never has an entry symbol no matter the output - // type, so unconditionally pass this. - clang_args.push("-Wl,--no-entry".into()); - - // Rust really needs a way for users to specify exports and imports in - // the source code. --export-dynamic isn't the right tool for this job, - // however it does have the side effect of automatically exporting a lot - // of symbols, which approximates what people want when compiling for - // wasm32-unknown-unknown expect, so use it for now. - clang_args.push("-Wl,--export-dynamic".into()); - - // Add the flags to wasm-ld's args too. - let lld_args = options.pre_link_args.entry(LinkerFlavor::Lld(LldFlavor::Wasm)).or_default(); - lld_args.push("--no-entry".into()); - lld_args.push("--export-dynamic".into()); + options.add_pre_link_args( + LinkerFlavor::Lld(LldFlavor::Wasm), + &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + // Rust really needs a way for users to specify exports and imports in + // the source code. --export-dynamic isn't the right tool for this job, + // however it does have the side effect of automatically exporting a lot + // of symbols, which approximates what people want when compiling for + // wasm32-unknown-unknown expect, so use it for now. + "--export-dynamic", + ], + ); + options.add_pre_link_args( + LinkerFlavor::Gcc, + &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm32-unknown-unknown", + "-Wl,--no-entry", + "-Wl,--export-dynamic", + ], + ); Target { llvm_target: "wasm32-unknown-unknown".into(), diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 10eb78e4e25..280457d68b9 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -80,11 +80,7 @@ pub fn target() -> Target { options.os = "wasi".into(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); - options - .pre_link_args - .entry(LinkerFlavor::Gcc) - .or_insert(Vec::new()) - .push("--target=wasm32-wasi".into()); + options.add_pre_link_args(LinkerFlavor::Gcc, &["--target=wasm32-wasi"]); options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs index 6826c0ac62b..5211f7707fb 100644 --- a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs @@ -14,19 +14,25 @@ pub fn target() -> Target { let mut options = wasm_base::options(); options.os = "unknown".into(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); - let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); - // Make sure clang uses LLD as its linker and is configured appropriately - // otherwise - clang_args.push("--target=wasm64-unknown-unknown".into()); - - // For now this target just never has an entry symbol no matter the output - // type, so unconditionally pass this. - clang_args.push("-Wl,--no-entry".into()); - - let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap(); - lld_args.push("--no-entry".into()); - lld_args.push("-mwasm64".into()); + options.add_pre_link_args( + LinkerFlavor::Lld(LldFlavor::Wasm), + &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + "-mwasm64", + ], + ); + options.add_pre_link_args( + LinkerFlavor::Gcc, + &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm64-unknown-unknown", + "-Wl,--no-entry", + ], + ); // Any engine that implements wasm64 will surely implement the rest of these // features since they were all merged into the official spec by the time diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index de7b7374af3..5736402ae14 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,63 +1,56 @@ use super::crt_objects::CrtObjectsFallback; use super::{cvs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; -use std::collections::BTreeMap; pub fn options() -> TargetOptions { - let mut lld_args = Vec::new(); - let mut clang_args = Vec::new(); - let mut arg = |arg: &'static str| { - lld_args.push(arg.into()); - clang_args.push(format!("-Wl,{}", arg).into()); - }; - - // By default LLD only gives us one page of stack (64k) which is a - // little small. Default to a larger stack closer to other PC platforms - // (1MB) and users can always inject their own link-args to override this. - arg("-z"); - arg("stack-size=1048576"); - - // By default LLD's memory layout is: - // - // 1. First, a blank page - // 2. Next, all static data - // 3. Finally, the main stack (which grows down) - // - // This has the unfortunate consequence that on stack overflows you - // corrupt static data and can cause some exceedingly weird bugs. To - // help detect this a little sooner we instead request that the stack is - // placed before static data. - // - // This means that we'll generate slightly larger binaries as references - // to static data will take more bytes in the ULEB128 encoding, but - // stack overflow will be guaranteed to trap as it underflows instead of - // corrupting static data. - arg("--stack-first"); - - // FIXME we probably shouldn't pass this but instead pass an explicit list - // of symbols we'll allow to be undefined. We don't currently have a - // mechanism of knowing, however, which symbols are intended to be imported - // from the environment and which are intended to be imported from other - // objects linked elsewhere. This is a coarse approximation but is sure to - // hide some bugs and frustrate someone at some point, so we should ideally - // work towards a world where we can explicitly list symbols that are - // supposed to be imported and have all other symbols generate errors if - // they remain undefined. - arg("--allow-undefined"); - - // Rust code should never have warnings, and warnings are often - // indicative of bugs, let's prevent them. - arg("--fatal-warnings"); - - // LLD only implements C++-like demangling, which doesn't match our own - // mangling scheme. Tell LLD to not demangle anything and leave it up to - // us to demangle these symbols later. Currently rustc does not perform - // further demangling, but tools like twiggy and wasm-bindgen are intended - // to do so. - arg("--no-demangle"); - - let mut pre_link_args = BTreeMap::new(); - pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); - pre_link_args.insert(LinkerFlavor::Gcc, clang_args); + macro_rules! args { + ($prefix:literal) => { + &[ + // By default LLD only gives us one page of stack (64k) which is a + // little small. Default to a larger stack closer to other PC platforms + // (1MB) and users can always inject their own link-args to override this. + concat!($prefix, "-z"), + concat!($prefix, "stack-size=1048576"), + // By default LLD's memory layout is: + // + // 1. First, a blank page + // 2. Next, all static data + // 3. Finally, the main stack (which grows down) + // + // This has the unfortunate consequence that on stack overflows you + // corrupt static data and can cause some exceedingly weird bugs. To + // help detect this a little sooner we instead request that the stack is + // placed before static data. + // + // This means that we'll generate slightly larger binaries as references + // to static data will take more bytes in the ULEB128 encoding, but + // stack overflow will be guaranteed to trap as it underflows instead of + // corrupting static data. + concat!($prefix, "--stack-first"), + // FIXME we probably shouldn't pass this but instead pass an explicit list + // of symbols we'll allow to be undefined. We don't currently have a + // mechanism of knowing, however, which symbols are intended to be imported + // from the environment and which are intended to be imported from other + // objects linked elsewhere. This is a coarse approximation but is sure to + // hide some bugs and frustrate someone at some point, so we should ideally + // work towards a world where we can explicitly list symbols that are + // supposed to be imported and have all other symbols generate errors if + // they remain undefined. + concat!($prefix, "--allow-undefined"), + // Rust code should never have warnings, and warnings are often + // indicative of bugs, let's prevent them. + concat!($prefix, "--fatal-warnings"), + // LLD only implements C++-like demangling, which doesn't match our own + // mangling scheme. Tell LLD to not demangle anything and leave it up to + // us to demangle these symbols later. Currently rustc does not perform + // further demangling, but tools like twiggy and wasm-bindgen are intended + // to do so. + concat!($prefix, "--no-demangle"), + ] + }; + } + + let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::Lld(LldFlavor::Wasm), args!("")); + super::add_link_args(&mut pre_link_args, LinkerFlavor::Gcc, args!("-Wl,")); TargetOptions { is_like_wasm: true, diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index d11f1f7d3f8..a0480f386f7 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,31 +1,35 @@ use crate::spec::crt_objects::{self, CrtObjectsFallback}; -use crate::spec::{cvs, LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; +use crate::spec::{cvs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( + let mut pre_link_args = TargetOptions::link_args( + LinkerFlavor::Ld, + &[ + // Enable ASLR + "--dynamicbase", + // ASLR will rebase it anyway so leaving that option enabled only leads to confusion + "--disable-auto-image-base", + ], + ); + super::add_link_args( + &mut pre_link_args, LinkerFlavor::Gcc, - vec![ + &[ // Tell GCC to avoid linker plugins, because we are not bundling // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin".into(), - // Enable ASLR - "-Wl,--dynamicbase".into(), - // ASLR will rebase it anyway so leaving that option enabled only leads to confusion - "-Wl,--disable-auto-image-base".into(), + "-fno-use-linker-plugin", + "-Wl,--dynamicbase", + "-Wl,--disable-auto-image-base", ], ); - let mut late_link_args = LinkArgs::new(); - let mut late_link_args_dynamic = LinkArgs::new(); - let mut late_link_args_static = LinkArgs::new(); // Order of `late_link_args*` was found through trial and error to work with various // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. - let mingw_libs = vec![ - "-lmsvcrt".into(), - "-lmingwex".into(), - "-lmingw32".into(), - "-lgcc".into(), // alas, mingw* libraries above depend on libgcc + let mingw_libs = &[ + "-lmsvcrt", + "-lmingwex", + "-lmingw32", + "-lgcc", // alas, mingw* libraries above depend on libgcc // mingw's msvcrt is a weird hybrid import library and static library. // And it seems that the linker fails to use import symbols from msvcrt // that are required from functions in msvcrt in certain cases. For example @@ -33,31 +37,27 @@ pub fn opts() -> TargetOptions { // The library is purposely listed twice to fix that. // // See https://github.com/rust-lang/rust/pull/47483 for some more details. - "-lmsvcrt".into(), - "-luser32".into(), - "-lkernel32".into(), - ]; - late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); - late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); - let dynamic_unwind_libs = vec![ - // If any of our crates are dynamically linked then we need to use - // the shared libgcc_s-dw2-1.dll. This is required to support - // unwinding across DLL boundaries. - "-lgcc_s".into(), - ]; - late_link_args_dynamic.insert(LinkerFlavor::Gcc, dynamic_unwind_libs.clone()); - late_link_args_dynamic.insert(LinkerFlavor::Lld(LldFlavor::Ld), dynamic_unwind_libs); - let static_unwind_libs = vec![ - // If all of our crates are statically linked then we can get away - // with statically linking the libgcc unwinding code. This allows - // binaries to be redistributed without the libgcc_s-dw2-1.dll - // dependency, but unfortunately break unwinding across DLL - // boundaries when unwinding across FFI boundaries. - "-lgcc_eh".into(), - "-l:libpthread.a".into(), + "-lmsvcrt", + "-luser32", + "-lkernel32", ]; - late_link_args_static.insert(LinkerFlavor::Gcc, static_unwind_libs.clone()); - late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs); + let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs); + super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs); + // If any of our crates are dynamically linked then we need to use + // the shared libgcc_s-dw2-1.dll. This is required to support + // unwinding across DLL boundaries. + let dynamic_unwind_libs = &["-lgcc_s"]; + let mut late_link_args_dynamic = + TargetOptions::link_args(LinkerFlavor::Ld, dynamic_unwind_libs); + super::add_link_args(&mut late_link_args_dynamic, LinkerFlavor::Gcc, dynamic_unwind_libs); + // If all of our crates are statically linked then we can get away + // with statically linking the libgcc unwinding code. This allows + // binaries to be redistributed without the libgcc_s-dw2-1.dll + // dependency, but unfortunately break unwinding across DLL + // boundaries when unwinding across FFI boundaries. + let static_unwind_libs = &["-lgcc_eh", "-l:libpthread.a"]; + let mut late_link_args_static = TargetOptions::link_args(LinkerFlavor::Ld, static_unwind_libs); + super::add_link_args(&mut late_link_args_static, LinkerFlavor::Gcc, static_unwind_libs); TargetOptions { os: "windows".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index 9f9f8be8718..30f995007a9 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -1,28 +1,17 @@ -use crate::spec::{cvs, LinkArgs, LinkerFlavor, TargetOptions}; +use crate::spec::{cvs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let pre_link_args = LinkArgs::from([( + // We cannot use `-nodefaultlibs` because compiler-rt has to be passed + // as a path since it's not added to linker search path by the default. + // There were attemts to make it behave like libgcc (so one can just use -l<name>) + // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440 + let pre_link_args = + TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]); + // Order of `late_link_args*` does not matter with LLD. + let late_link_args = TargetOptions::link_args( LinkerFlavor::Gcc, - vec![ - // We cannot use `-nodefaultlibs` because compiler-rt has to be passed - // as a path since it's not added to linker search path by the default. - // There were attemts to make it behave like libgcc (so one can just use -l<name>) - // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440 - "-nolibc".into(), - "--unwindlib=none".into(), - ], - )]); - let late_link_args = LinkArgs::from([( - LinkerFlavor::Gcc, - // Order of `late_link_args*` does not matter with LLD. - vec![ - "-lmingw32".into(), - "-lmingwex".into(), - "-lmsvcrt".into(), - "-lkernel32".into(), - "-luser32".into(), - ], - )]); + &["-lmingw32", "-lmingwex", "-lmsvcrt", "-lkernel32", "-luser32"], + ); TargetOptions { os: "windows".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 11968391776..334dec43ef7 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -1,28 +1,25 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let base = super::windows_gnu_base::opts(); // FIXME: This should be updated for the exception machinery changes from #67502 // and inherit from `windows_gnu_base`, at least partially. - let mut late_link_args = LinkArgs::new(); + let mingw_libs = &[ + "-lwinstorecompat", + "-lruntimeobject", + "-lsynchronization", + "-lvcruntime140_app", + "-lucrt", + "-lwindowsapp", + "-lmingwex", + "-lmingw32", + ]; + let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs); + super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs); + // Reset the flags back to empty until the FIXME above is addressed. let late_link_args_dynamic = LinkArgs::new(); let late_link_args_static = LinkArgs::new(); - let mingw_libs = vec![ - //"-lwinstorecompat".into(), - //"-lmingwex".into(), - //"-lwinstorecompat".into(), - "-lwinstorecompat".into(), - "-lruntimeobject".into(), - "-lsynchronization".into(), - "-lvcruntime140_app".into(), - "-lucrt".into(), - "-lwindowsapp".into(), - "-lmingwex".into(), - "-lmingw32".into(), - ]; - late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); - late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); TargetOptions { abi: "uwp".into(), diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs index d6b065b529a..f2573fc2d21 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs @@ -1,16 +1,11 @@ -use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions}; +use crate::spec::{LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut opts = super::windows_msvc_base::opts(); opts.abi = "uwp".into(); opts.vendor = "uwp".into(); - let pre_link_args_msvc = vec!["/APPCONTAINER".into(), "mincore.lib".into()]; - opts.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); - opts.pre_link_args - .entry(LinkerFlavor::Lld(LldFlavor::Link)) - .or_default() - .extend(pre_link_args_msvc); + opts.add_pre_link_args(LinkerFlavor::Msvc, &["/APPCONTAINER", "mincore.lib"]); opts } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index 51d14f0403a..dbd26899c18 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -6,8 +6,7 @@ pub fn target() -> Target { base.cpu = "core2".into(); base.max_atomic_width = Some(128); // core2 support cmpxchg16b base.frame_pointer = FramePointer::Always; - base.pre_link_args - .insert(LinkerFlavor::Gcc, vec!["-m64".into(), "-arch".into(), "x86_64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-arch", "x86_64"]); base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; 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 47c70513faf..4348d924579 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 @@ -1,41 +1,44 @@ -use std::{borrow::Cow, iter}; +use std::borrow::Cow; use crate::spec::cvs; use super::{LinkerFlavor, LldFlavor, Target, TargetOptions}; pub fn target() -> Target { - const PRE_LINK_ARGS: &[&str] = &[ - "-e", - "elf_entry", - "-Bstatic", - "--gc-sections", - "-z", - "text", - "-z", - "norelro", - "--no-undefined", - "--error-unresolved-symbols", - "--no-undefined-version", - "-Bsymbolic", - "--export-dynamic", - // The following symbols are needed by libunwind, which is linked after - // libstd. Make sure they're included in the link. - "-u", - "__rust_abort", - "-u", - "__rust_c_alloc", - "-u", - "__rust_c_dealloc", - "-u", - "__rust_print_err", - "-u", - "__rust_rwlock_rdlock", - "-u", - "__rust_rwlock_unlock", - "-u", - "__rust_rwlock_wrlock", - ]; + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Ld, + &[ + "-e", + "elf_entry", + "-Bstatic", + "--gc-sections", + "-z", + "text", + "-z", + "norelro", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-Bsymbolic", + "--export-dynamic", + // The following symbols are needed by libunwind, which is linked after + // libstd. Make sure they're included in the link. + "-u", + "__rust_abort", + "-u", + "__rust_c_alloc", + "-u", + "__rust_c_dealloc", + "-u", + "__rust_print_err", + "-u", + "__rust_rwlock_rdlock", + "-u", + "__rust_rwlock_unlock", + "-u", + "__rust_rwlock_wrlock", + ], + ); const EXPORT_SYMBOLS: &[&str] = &[ "sgx_entry", @@ -66,11 +69,7 @@ pub fn target() -> Target { features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(), llvm_args: cvs!["--x86-experimental-lvi-inline-asm-hardening"], position_independent_executables: true, - pre_link_args: iter::once(( - LinkerFlavor::Lld(LldFlavor::Ld), - PRE_LINK_ARGS.iter().cloned().map(Cow::from).collect(), - )) - .collect(), + pre_link_args, override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(Cow::from).collect()), relax_elf_relocations: true, ..Default::default() diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 049cab0d554..6d19cf26574 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { // https://developer.android.com/ndk/guides/abis.html#86-64 base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs index 2a697daeb28..0550b221fd9 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs @@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.cpu = "x86-64".into(); base.vendor = "pc".into(); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs index 0fa43481c9b..59a8cffca48 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs @@ -1,14 +1,11 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target}; +use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".into(); - let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - gcc_pre_link_args.push("-m64".into()); // Use high-entropy 64 bit address space for ASLR - gcc_pre_link_args.push("-Wl,--high-entropy-va".into()); - base.pre_link_args - .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pep".into()]); + base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]); base.max_atomic_width = Some(64); base.linker = Some("x86_64-w64-mingw32-gcc".into()); diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs index b5ff63e0532..d3909b3895e 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs @@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_gnullvm_base::opts(); base.cpu = "x86-64".into(); - let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - gcc_pre_link_args.push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.max_atomic_width = Some(64); base.linker = Some("x86_64-w64-mingw32-clang".into()); diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs index a02018266fb..cbe87589a70 100644 --- a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs @@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); base.cpu = "x86-64".into(); base.vendor = "sun".into(); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs index 1f2b998a7ba..746f6478178 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index c9aedd6ea82..b30784ed692 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.supported_sanitizers = diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs index aebbd18c66a..d6d03362982 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; // This option is required to build executables on Haiku x86_64 diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs index 9529fa9640d..9f19c3a2b2a 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs @@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, Target}; pub fn target() -> Target { let mut base = super::illumos_base::opts(); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into(), "-std=c99".into()]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-std=c99"]); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index e525cfdde14..956be0353fa 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.static_position_independent_executables = true; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs index 863b41633e2..140882747c2 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { base.cpu = "x86-64".into(); base.abi = "x32".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mx32".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-mx32"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.has_thread_local = false; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 8678f06e2cb..87e7784d1f9 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.static_position_independent_executables = true; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index a7115dace1c..d3a67619aa8 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.supported_sanitizers = SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs index 0db88d64ac0..593345a5f1d 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { base.features = "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float".into(); base.code_model = Some(CodeModel::Kernel); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); Target { // FIXME: Some dispute, the linux-on-clang folks think this should use diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index 11e9cc4abc0..f50c6bceec9 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs index af8b9673c30..668ae905417 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::redox_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs index a94bbbf6ede..76d2013cf7f 100644 --- a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs @@ -1,14 +1,11 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target}; +use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".into(); - let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); - gcc_pre_link_args.push("-m64".into()); // Use high-entropy 64 bit address space for ASLR - gcc_pre_link_args.push("-Wl,--high-entropy-va".into()); - base.pre_link_args - .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pep".into()]); + base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs index 16d29753e7d..1298974952f 100644 --- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into()); + base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.disable_redzone = true; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index a63790b594d..90ff07cba02 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -220,7 +220,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { .map(|&(id, _)| (id, vec![])) .collect(); - infcx.process_registered_region_obligations(&body_id_map, None, full_env); + infcx.process_registered_region_obligations(&body_id_map, full_env); let region_data = infcx .inner diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b37db4b9e18..2b26b916d32 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -407,11 +407,7 @@ fn resolve_negative_obligation<'cx, 'tcx>( // function bodies with closures). outlives_env.save_implied_bounds(CRATE_HIR_ID); - infcx.process_registered_region_obligations( - outlives_env.region_bound_pairs_map(), - Some(tcx.lifetimes.re_root_empty), - param_env, - ); + infcx.process_registered_region_obligations(outlives_env.region_bound_pairs_map(), param_env); let errors = infcx.resolve_regions(region_context, &outlives_env); 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 af0803fbd54..debb9e82951 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -484,10 +484,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_label(span, explanation); } - if let ObligationCauseCode::ObjectCastObligation(obj_ty) = obligation.cause.code().peel_derives() && - let Some(self_ty) = trait_predicate.self_ty().no_bound_vars() && + if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() && Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { - self.suggest_borrowing_for_object_cast(&mut err, &obligation, self_ty, *obj_ty); + self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty); } if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { @@ -1560,7 +1559,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) + | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( 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 fbe66d7dcdd..12858172ee5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2217,9 +2217,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_note(tcx.def_span(item_def_id), &descr); } } - ObligationCauseCode::ObjectCastObligation(object_ty) => { + ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => { err.note(&format!( - "required for the cast to the object type `{}`", + "required for the cast from `{}` to the object type `{}`", + self.ty_to_string(concrete_ty), self.ty_to_string(object_ty) )); } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 132c335a7e6..8d344591915 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -366,15 +366,9 @@ fn object_safety_violation_for_method( // Get an accurate span depending on the violation. violation.map(|v| { let node = tcx.hir().get_if_local(method.def_id); - let span = match (v, node) { - (MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node - .fn_decl() - .and_then(|decl| decl.inputs.get(arg + 1)) - .map_or(method.ident(tcx).span, |arg| arg.span), - (MethodViolationCode::UndispatchableReceiver, Some(node)) => node - .fn_decl() - .and_then(|decl| decl.inputs.get(0)) - .map_or(method.ident(tcx).span, |arg| arg.span), + let span = match (&v, node) { + (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span()) } @@ -397,32 +391,41 @@ fn virtual_call_violation_for_method<'tcx>( // The method's first parameter must be named `self` if !method.fn_has_self_parameter { - // We'll attempt to provide a structured suggestion for `Self: Sized`. - let sugg = - tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map( - |generics| match generics.predicates { - [] => (" where Self: Sized", generics.where_clause_span), - [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()), - }, - ); - // Get the span pointing at where the `self` receiver should be. - let sm = tcx.sess.source_map(); - let self_span = method.ident(tcx).span.to(tcx - .hir() - .span_if_local(method.def_id) - .unwrap_or_else(|| sm.next_point(method.ident(tcx).span)) - .shrink_to_hi()); - let self_span = sm.span_through_char(self_span, '(').shrink_to_hi(); - return Some(MethodViolationCode::StaticMethod( - sugg, - self_span, - !sig.inputs().skip_binder().is_empty(), - )); + let sugg = if let Some(hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + let sm = tcx.sess.source_map(); + Some(( + ( + format!("&self{}", if sig.decl.inputs.is_empty() { "" } else { ", " }), + sm.span_through_char(sig.span, '(').shrink_to_hi(), + ), + ( + format!("{} Self: Sized", generics.add_where_or_trailing_comma()), + generics.tail_span_for_predicate_suggestion(), + ), + )) + } else { + None + }; + return Some(MethodViolationCode::StaticMethod(sugg)); } - for (i, &input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { + for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { - return Some(MethodViolationCode::ReferencesSelfInput(i)); + let span = if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + Some(sig.decl.inputs[i].span) + } else { + None + }; + return Some(MethodViolationCode::ReferencesSelfInput(span)); } } if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { @@ -456,7 +459,16 @@ fn virtual_call_violation_for_method<'tcx>( // `Receiver: Unsize<Receiver[Self => dyn Trait]>`. if receiver_ty != tcx.types.self_param { if !receiver_is_dispatchable(tcx, method, receiver_ty) { - return Some(MethodViolationCode::UndispatchableReceiver); + let span = if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + Some(sig.decl.inputs[0].span) + } else { + None + }; + return Some(MethodViolationCode::UndispatchableReceiver(span)); } else { // Do sanity check to make sure the receiver actually has the layout of a pointer. diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 82c54291a5d..aba4f144d4b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -28,7 +28,6 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; -use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{MaxUniverse, TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::subst::Subst; @@ -252,22 +251,10 @@ fn project_and_unify_type<'cx, 'tcx>( Err(InProgress) => return ProjectAndUnifyResult::Recursive, }; debug!(?normalized, ?obligations, "project_and_unify_type result"); - let actual = obligation.predicate.term; - // HACK: lazy TAIT would regress src/test/ui/impl-trait/nested-return-type2.rs, so we add - // a back-compat hack hat converts the RPITs into inference vars, just like they were before - // lazy TAIT. - // This does not affect TAITs in general, as tested in the nested-return-type-tait* tests. - let InferOk { value: actual, obligations: new } = - selcx.infcx().replace_opaque_types_with_inference_vars( - actual, - obligation.cause.body_id, - obligation.cause.span, - ObligationCauseCode::MiscObligation, - obligation.param_env, - ); - obligations.extend(new); - - match infcx.at(&obligation.cause, obligation.param_env).eq(normalized, actual) { + match infcx + .at(&obligation.cause, obligation.param_env) + .eq(normalized, obligation.predicate.term) + { Ok(InferOk { obligations: inferred_obligations, value: () }) => { obligations.extend(inferred_obligations); ProjectAndUnifyResult::Holds(obligations) diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index b80a27eb07d..7f15b683fda 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -12,7 +12,7 @@ use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; use rustc_middle::mir; -use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; @@ -162,15 +162,13 @@ struct QueryNormalizer<'cx, 'tcx> { universes: Vec<Option<ty::UniverseIndex>>, } -impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { +impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { type Error = NoSolution; fn tcx<'c>(&'c self) -> TyCtxt<'tcx> { self.infcx.tcx } -} -impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { fn try_fold_binder<T: TypeFoldable<'tcx>>( &mut self, t: ty::Binder<'tcx, T>, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 5942bb79d69..e1131140c39 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -813,7 +813,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( @@ -910,7 +910,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( @@ -931,7 +931,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cause = ObligationCause::new( obligation.cause.span, obligation.cause.body_id, - ObjectCastObligation(target), + ObjectCastObligation(source, target), ); let predicate_to_obligation = |predicate| { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 95f1e224a4c..a1861529b59 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -452,7 +452,7 @@ fn report_conflicting_impls( match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() - || !tcx.orphan_check_crate(()).contains(&impl_def_id) + || tcx.orphan_check_impl(impl_def_id).is_ok() { let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); Some(decorate( diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index cb1602816ae..f9708d6d919 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(fmt_helpers_for_derive)] #![feature(min_specialization)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 5cd2901324a..74737e30bb4 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -554,137 +554,37 @@ impl<I: Interner> hash::Hash for TyKind<I> { // This is manually implemented because a derive would require `I: Debug` impl<I: Interner> fmt::Debug for TyKind<I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match (&*self,) { - (&Bool,) => fmt::Formatter::write_str(f, "Bool"), - (&Char,) => fmt::Formatter::write_str(f, "Char"), - (&Int(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Int"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Uint(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Uint"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Float(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Float"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Adt(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Adt"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Foreign(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Foreign"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Str,) => fmt::Formatter::write_str(f, "Str"), - (&Array(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Array"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Slice(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Slice"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&RawPtr(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "RawPtr"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Ref(ref __self_0, ref __self_1, ref __self_2),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Ref"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_2); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&FnDef(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "FnDef"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&FnPtr(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "FnPtr"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Dynamic(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Dynamic"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Closure(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Closure"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Generator(ref __self_0, ref __self_1, ref __self_2),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Generator"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_2); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&GeneratorWitness(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "GeneratorWitness"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Never,) => fmt::Formatter::write_str(f, "Never"), - (&Tuple(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Tuple"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Projection(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Projection"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Opaque(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Opaque"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Param(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Param"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Bound(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Bound"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Placeholder(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Placeholder"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Infer(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Infer"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Error(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Error"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } + use std::fmt::*; + match self { + Bool => Formatter::write_str(f, "Bool"), + Char => Formatter::write_str(f, "Char"), + Int(f0) => Formatter::debug_tuple_field1_finish(f, "Int", f0), + Uint(f0) => Formatter::debug_tuple_field1_finish(f, "Uint", f0), + Float(f0) => Formatter::debug_tuple_field1_finish(f, "Float", f0), + Adt(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Adt", f0, f1), + Foreign(f0) => Formatter::debug_tuple_field1_finish(f, "Foreign", f0), + Str => Formatter::write_str(f, "Str"), + Array(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Array", f0, f1), + Slice(f0) => Formatter::debug_tuple_field1_finish(f, "Slice", f0), + RawPtr(f0) => Formatter::debug_tuple_field1_finish(f, "RawPtr", f0), + Ref(f0, f1, f2) => Formatter::debug_tuple_field3_finish(f, "Ref", f0, f1, f2), + FnDef(f0, f1) => Formatter::debug_tuple_field2_finish(f, "FnDef", f0, f1), + FnPtr(f0) => Formatter::debug_tuple_field1_finish(f, "FnPtr", f0), + Dynamic(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Dynamic", f0, f1), + Closure(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Closure", f0, f1), + Generator(f0, f1, f2) => { + Formatter::debug_tuple_field3_finish(f, "Generator", f0, f1, f2) + } + GeneratorWitness(f0) => Formatter::debug_tuple_field1_finish(f, "GeneratorWitness", f0), + Never => Formatter::write_str(f, "Never"), + Tuple(f0) => Formatter::debug_tuple_field1_finish(f, "Tuple", f0), + Projection(f0) => Formatter::debug_tuple_field1_finish(f, "Projection", f0), + Opaque(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Opaque", f0, f1), + Param(f0) => Formatter::debug_tuple_field1_finish(f, "Param", f0), + Bound(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Bound", f0, f1), + Placeholder(f0) => Formatter::debug_tuple_field1_finish(f, "Placeholder", f0), + Infer(f0) => Formatter::debug_tuple_field1_finish(f, "Infer", f0), + TyKind::Error(f0) => Formatter::debug_tuple_field1_finish(f, "Error", f0), } } } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 45c011b78e3..8ffffbed93c 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -32,11 +32,6 @@ use rustc_ty_utils::representability::{self, Representability}; use std::iter; use std::ops::ControlFlow; -pub fn check_wf_new(tcx: TyCtxt<'_>) { - let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); - tcx.hir().par_visit_all_item_likes(&visit); -} - pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { match tcx.sess.target.is_abi_supported(abi) { Some(true) => (), @@ -754,7 +749,7 @@ fn check_opaque_meets_bounds<'tcx>( }); } -pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { +fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { debug!( "check_item_type(it.def_id={:?}, it.name={})", id.def_id, @@ -1543,12 +1538,6 @@ pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { } } -pub(super) use wfcheck::check_item_well_formed; - -pub(super) use wfcheck::check_trait_item as check_trait_item_well_formed; - -pub(super) use wfcheck::check_impl_item as check_impl_item_well_formed; - fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed { struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") .span_label(span, "recursive `async fn`") diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs index b7ba9d97878..7602f2550e8 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs @@ -1,7 +1,26 @@ use std::cmp; +use rustc_index::vec::IndexVec; use rustc_middle::ty::error::TypeError; +rustc_index::newtype_index! { + pub(crate) struct ExpectedIdx { + DEBUG_FORMAT = "ExpectedIdx({})", + } +} + +rustc_index::newtype_index! { + pub(crate) struct ProvidedIdx { + DEBUG_FORMAT = "ProvidedIdx({})", + } +} + +impl ExpectedIdx { + pub fn to_provided_idx(self) -> ProvidedIdx { + ProvidedIdx::from_usize(self.as_usize()) + } +} + // An issue that might be found in the compatibility matrix #[derive(Debug)] enum Issue { @@ -27,24 +46,24 @@ pub(crate) enum Compatibility<'tcx> { #[derive(Debug)] pub(crate) enum Error<'tcx> { /// The provided argument is the invalid type for the expected input - Invalid(usize, usize, Compatibility<'tcx>), // provided, expected + Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>), /// There is a missing input - Missing(usize), + Missing(ExpectedIdx), /// There's a superfluous argument - Extra(usize), + Extra(ProvidedIdx), /// Two arguments should be swapped - Swap(usize, usize, usize, usize), + Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx), /// Several arguments should be reordered - Permutation(Vec<(usize, usize)>), // dest_arg, dest_input + Permutation(Vec<(ExpectedIdx, ProvidedIdx)>), } pub(crate) struct ArgMatrix<'tcx> { /// Maps the indices in the `compatibility_matrix` rows to the indices of /// the *user provided* inputs - input_indexes: Vec<usize>, + provided_indices: Vec<ProvidedIdx>, /// Maps the indices in the `compatibility_matrix` columns to the indices /// of the *expected* args - arg_indexes: Vec<usize>, + expected_indices: Vec<ExpectedIdx>, /// The first dimension (rows) are the remaining user provided inputs to /// match and the second dimension (cols) are the remaining expected args /// to match @@ -52,62 +71,64 @@ pub(crate) struct ArgMatrix<'tcx> { } impl<'tcx> ArgMatrix<'tcx> { - pub(crate) fn new<F: FnMut(usize, usize) -> Compatibility<'tcx>>( - minimum_input_count: usize, - provided_arg_count: usize, + pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>( + provided_count: usize, + expected_input_count: usize, mut is_compatible: F, ) -> Self { - let compatibility_matrix = (0..provided_arg_count) - .map(|i| (0..minimum_input_count).map(|j| is_compatible(i, j)).collect()) + let compatibility_matrix = (0..provided_count) + .map(|i| { + (0..expected_input_count) + .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j))) + .collect() + }) .collect(); ArgMatrix { - input_indexes: (0..provided_arg_count).collect(), - arg_indexes: (0..minimum_input_count).collect(), + provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(), + expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(), compatibility_matrix, } } /// Remove a given input from consideration - fn eliminate_input(&mut self, idx: usize) { - self.input_indexes.remove(idx); + fn eliminate_provided(&mut self, idx: usize) { + self.provided_indices.remove(idx); self.compatibility_matrix.remove(idx); } /// Remove a given argument from consideration - fn eliminate_arg(&mut self, idx: usize) { - self.arg_indexes.remove(idx); + fn eliminate_expected(&mut self, idx: usize) { + self.expected_indices.remove(idx); for row in &mut self.compatibility_matrix { row.remove(idx); } } /// "satisfy" an input with a given arg, removing both from consideration - fn satisfy_input(&mut self, input_idx: usize, arg_idx: usize) { - self.eliminate_input(input_idx); - self.eliminate_arg(arg_idx); + fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) { + self.eliminate_provided(provided_idx); + self.eliminate_expected(expected_idx); } // Returns a `Vec` of (user input, expected arg) of matched arguments. These // are inputs on the remaining diagonal that match. - fn eliminate_satisfied(&mut self) -> Vec<(usize, usize)> { - let mut i = cmp::min(self.input_indexes.len(), self.arg_indexes.len()); + fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> { + let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len()); let mut eliminated = vec![]; - while i > 0 { - let idx = i - 1; - if matches!(self.compatibility_matrix[idx][idx], Compatibility::Compatible) { - eliminated.push((self.input_indexes[idx], self.arg_indexes[idx])); - self.satisfy_input(idx, idx); + for i in (0..num_args).rev() { + if matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) { + eliminated.push((self.provided_indices[i], self.expected_indices[i])); + self.satisfy_input(i, i); } - i -= 1; } - return eliminated; + eliminated } // Find some issue in the compatibility matrix fn find_issue(&self) -> Option<Issue> { let mat = &self.compatibility_matrix; - let ai = &self.arg_indexes; - let ii = &self.input_indexes; + let ai = &self.expected_indices; + let ii = &self.provided_indices; for i in 0..cmp::max(ai.len(), ii.len()) { // If we eliminate the last row, any left-over inputs are considered missing @@ -264,12 +285,15 @@ impl<'tcx> ArgMatrix<'tcx> { // // We'll want to know which arguments and inputs these rows and columns correspond to // even after we delete them. - pub(crate) fn find_errors(mut self) -> (Vec<Error<'tcx>>, Vec<Option<usize>>) { - let provided_arg_count = self.input_indexes.len(); + pub(crate) fn find_errors( + mut self, + ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) { + let provided_arg_count = self.provided_indices.len(); let mut errors: Vec<Error<'tcx>> = vec![]; // For each expected argument, the matched *actual* input - let mut matched_inputs: Vec<Option<usize>> = vec![None; self.arg_indexes.len()]; + let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> = + IndexVec::from_elem_n(None, self.expected_indices.len()); // Before we start looking for issues, eliminate any arguments that are already satisfied, // so that an argument which is already spoken for by the input it's in doesn't @@ -280,34 +304,34 @@ impl<'tcx> ArgMatrix<'tcx> { // Without this elimination, the first argument causes the second argument // to show up as both a missing input and extra argument, rather than // just an invalid type. - for (inp, arg) in self.eliminate_satisfied() { - matched_inputs[arg] = Some(inp); + for (provided, expected) in self.eliminate_satisfied() { + matched_inputs[expected] = Some(provided); } - while self.input_indexes.len() > 0 || self.arg_indexes.len() > 0 { + while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { match self.find_issue() { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); - let input_idx = self.input_indexes[idx]; - let arg_idx = self.arg_indexes[idx]; + let input_idx = self.provided_indices[idx]; + let arg_idx = self.expected_indices[idx]; self.satisfy_input(idx, idx); errors.push(Error::Invalid(input_idx, arg_idx, compatibility)); } Some(Issue::Extra(idx)) => { - let input_idx = self.input_indexes[idx]; - self.eliminate_input(idx); + let input_idx = self.provided_indices[idx]; + self.eliminate_provided(idx); errors.push(Error::Extra(input_idx)); } Some(Issue::Missing(idx)) => { - let arg_idx = self.arg_indexes[idx]; - self.eliminate_arg(idx); + let arg_idx = self.expected_indices[idx]; + self.eliminate_expected(idx); errors.push(Error::Missing(arg_idx)); } Some(Issue::Swap(idx, other)) => { - let input_idx = self.input_indexes[idx]; - let other_input_idx = self.input_indexes[other]; - let arg_idx = self.arg_indexes[idx]; - let other_arg_idx = self.arg_indexes[other]; + let input_idx = self.provided_indices[idx]; + let other_input_idx = self.provided_indices[other]; + let arg_idx = self.expected_indices[idx]; + let other_arg_idx = self.expected_indices[other]; let (min, max) = (cmp::min(idx, other), cmp::max(idx, other)); self.satisfy_input(min, max); // Subtract 1 because we already removed the "min" row @@ -319,13 +343,14 @@ impl<'tcx> ArgMatrix<'tcx> { Some(Issue::Permutation(args)) => { let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect(); - let mut real_idxs = vec![None; provided_arg_count]; + let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> = + IndexVec::from_elem_n(None, provided_arg_count); for (src, dst) in args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst))) { - let src_input_idx = self.input_indexes[src]; - let dst_input_idx = self.input_indexes[dst]; - let dest_arg_idx = self.arg_indexes[dst]; + let src_input_idx = self.provided_indices[src]; + let dst_input_idx = self.provided_indices[dst]; + let dest_arg_idx = self.expected_indices[dst]; real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx)); matched_inputs[dest_arg_idx] = Some(src_input_idx); } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 2326c4069e4..08df01c0c1a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -1,6 +1,8 @@ use crate::astconv::AstConv; use crate::check::coercion::CoerceMany; -use crate::check::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error}; +use crate::check::fn_ctxt::arg_matrix::{ + ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx, +}; use crate::check::gather_locals::Declaration; use crate::check::method::MethodCallee; use crate::check::Expectation::*; @@ -17,13 +19,14 @@ 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::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; @@ -214,6 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let provided_arg_count = provided_args.len(); // We'll also want to keep track of the fully coerced argument types, for an awkward hack near the end + // FIXME(compiler-errors): Get rid of this, actually. let mut final_arg_types: Vec<Option<(Ty<'_>, Ty<'_>)>> = vec![None; provided_arg_count]; // We introduce a helper function to demand that a given argument satisfy a given input @@ -287,54 +291,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - // A "softer" version of the helper above, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |input_idx, arg_idx| { - let formal_input_ty: Ty<'tcx> = formal_input_tys[arg_idx]; - let expected_input_ty: Ty<'tcx> = expected_input_tys[arg_idx]; - - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if formal_input_ty.references_error() || expected_input_ty.references_error() { - return Compatibility::Incompatible(None); - } - - let provided_arg: &hir::Expr<'tcx> = &provided_args[input_idx]; - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - // FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure. - // - // I had another method of "soft" type checking before, - // but it was failing to find the type of some expressions (like "") - // so I prodded this method and made it pub(super) so I could call it, and it seems to work well. - let checked_ty = self.check_expr_kind(provided_arg, expectation); - - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.can_coerce(checked_ty, coerced_ty); - - if !can_coerce { - return Compatibility::Incompatible(None); - } - - let subtyping_result = self - .at(&self.misc(provided_arg.span), self.param_env) - .sup(formal_input_ty, coerced_ty); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let coercible = - !coerced_ty.references_error() && !checked_ty.references_error() && can_coerce; - - match (coercible, &subtyping_result) { - (true, Ok(_)) => Compatibility::Compatible, - _ => Compatibility::Incompatible(subtyping_result.err()), - } - }; - // To start, we only care "along the diagonal", where we expect every // provided arg to be in the right spot - let mut compatibility = vec![Compatibility::Incompatible(None); provided_args.len()]; + let mut compatibility_diagonal = + vec![Compatibility::Incompatible(None); provided_args.len()]; // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path // if the wrong number of arguments were supplied, we CAN'T be satisfied, @@ -394,7 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let compatible = demand_compatible(idx, &mut final_arg_types); let is_compatible = matches!(compatible, Compatibility::Compatible); - compatibility[idx] = compatible; + compatibility_diagonal[idx] = compatible; if !is_compatible { call_appears_satisfied = false; @@ -402,70 +362,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Logic here is a bit hairy - 'errors: { - // If something above didn't typecheck, we've fallen off the happy path - // and we should make some effort to provide better error messages - if call_appears_satisfied { - break 'errors; - } - - self.set_tainted_by_errors(); + if c_variadic && provided_arg_count < minimum_input_count { + err_code = "E0060"; + } - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid + for arg in provided_args.iter().skip(minimum_input_count) { + // Make sure we've checked this expr at least once. + let arg_ty = self.check_expr(&arg); - // First, let's find the errors - let mut compatibility: Vec<_> = compatibility.into_iter().map(Some).collect(); - let (mut errors, matched_inputs) = - ArgMatrix::new(minimum_input_count, provided_arg_count, |i, j| { - if i == j { compatibility[i].take().unwrap() } else { check_compatible(i, j) } - }) - .find_errors(); + // If the function is c-style variadic, we skipped a bunch of arguments + // so we need to check those, and write out the types + // Ideally this would be folded into the above, for uniform style + // but c-variadic is already a corner case + if c_variadic { + fn variadic_error<'tcx>( + sess: &'tcx Session, + span: Span, + ty: Ty<'tcx>, + cast_ty: &str, + ) { + use crate::structured_errors::MissingCastForVariadicArg; - // Okay, so here's where it gets complicated in regards to what errors - // we emit and how. - // There are 3 different "types" of errors we might encounter. - // 1) Missing/extra/swapped arguments - // 2) Valid but incorrect arguments - // 3) Invalid arguments - // - Currently I think this only comes up with `CyclicTy` - // - // We first need to go through, remove those from (3) and emit those - // as their own error, particularly since they're error code and - // message is special. From what I can tell, we *must* emit these - // here (vs somewhere prior to this function) since the arguments - // become invalid *because* of how they get used in the function. - // It is what it is. - - let found_errors = !errors.is_empty(); - - errors.drain_filter(|error| { - let Error::Invalid(input_idx, arg_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; - let expected_ty = expected_input_tys[*arg_idx]; - let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap_or_else(|| tcx.ty_error()); - let cause = &self.misc(provided_args[*input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, e).emit(); - return true; + MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); } - false - }); - // We're done if we found errors, but we already emitted them. - // I don't think we *should* be able to enter this bit of code - // (`!call_appears_satisfied`) without *also* finding errors, but we - // don't want to accidentally not emit an error if there is some - // logic bug in the `ArgMatrix` code. - if found_errors && errors.is_empty() { - break 'errors; + // There are a few types which get autopromoted when passed via varargs + // in C but we just error out instead and require explicit casts. + let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); + match arg_ty.kind() { + ty::Float(ty::FloatTy::F32) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); + } + ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); + } + ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); + } + ty::FnDef(..) => { + let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); + let ptr_ty = self.resolve_vars_if_possible(ptr_ty); + variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + } + _ => {} + } } + } + if !call_appears_satisfied { // Next, let's construct the error let (error_span, full_call_span, ctor_of) = match &call_expr.kind { hir::ExprKind::Call( @@ -500,524 +444,633 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(CtorOf::Variant) => "enum variant", None => "function", }; - if c_variadic && provided_arg_count < minimum_input_count { - err_code = "E0060"; - } - // Next special case: The case where we expect a single tuple and - // wrapping all the args in parentheses (or adding a comma to - // already existing parentheses) will result in a tuple that - // satisfies the call. - // This isn't super ideal code, because we copy code from elsewhere - // and somewhat duplicate this. We also delegate to the general type - // mismatch suggestions for the single arg case. - let sugg_tuple_wrap_args = - self.suggested_tuple_wrap(&expected_input_tys, provided_args); - match sugg_tuple_wrap_args { - TupleMatchFound::None => {} - TupleMatchFound::Single => { - let expected_ty = expected_input_tys[0]; - let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap(); - let expected_ty = self.resolve_vars_if_possible(expected_ty); - let provided_ty = self.resolve_vars_if_possible(provided_ty); - let cause = &self.misc(provided_args[0].span); - let compatibility = demand_compatible(0, &mut final_arg_types); - let type_error = match compatibility { - Compatibility::Incompatible(Some(error)) => error, - _ => TypeError::Mismatch, - }; - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - let mut err = self.report_and_explain_type_error(trace, &type_error); - self.emit_coerce_suggestions( - &mut err, - &provided_args[0], - final_arg_types[0].map(|ty| ty.0).unwrap(), - final_arg_types[0].map(|ty| ty.1).unwrap(), - None, - None, - ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); - err.emit(); - break 'errors; - } - TupleMatchFound::Multiple(start, end) => { - let mut err = tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(minimum_input_count, "argument"), - potentially_plural_count(provided_arg_count, "argument"), - if provided_arg_count == 1 { "was" } else { "were" } - ), - DiagnosticId::Error(err_code.to_owned()), - ); - // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); - err.multipart_suggestion( - "use parentheses to construct a tuple", - vec![(start, '('.to_string()), (end, ')'.to_string())], - Applicability::MachineApplicable, - ); - err.emit(); - break 'errors; + let try_tuple_wrap_args = || { + // The case where we expect a single tuple and wrapping all the args + // in parentheses (or adding a comma to already existing parentheses) + // will result in a tuple that satisfies the call. + // This isn't super ideal code, because we copy code from elsewhere + // and somewhat duplicate this. We also delegate to the general type + // mismatch suggestions for the single arg case. + match self.suggested_tuple_wrap(&expected_input_tys, provided_args) { + TupleMatchFound::Single => { + let expected_ty = expected_input_tys[0]; + let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); + let cause = &self.misc(provided_args[0].span); + let compatibility = demand_compatible(0, &mut final_arg_types); + let type_error = match compatibility { + Compatibility::Incompatible(Some(error)) => error, + _ => TypeError::Mismatch, + }; + let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let mut err = self.report_and_explain_type_error(trace, &type_error); + self.emit_coerce_suggestions( + &mut err, + &provided_args[0], + final_arg_types[0].map(|ty| ty.0).unwrap(), + final_arg_types[0].map(|ty| ty.1).unwrap(), + None, + None, + ); + err.span_label( + full_call_span, + format!("arguments to this {} are incorrect", call_name), + ); + // Call out where the function is defined + label_fn_like(tcx, &mut err, fn_def_id); + err.emit(); + return true; + } + TupleMatchFound::Multiple(start, end) => { + let mut err = tcx.sess.struct_span_err_with_code( + full_call_span, + &format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count(minimum_input_count, "argument"), + potentially_plural_count(provided_arg_count, "argument"), + if provided_arg_count == 1 { "was" } else { "were" } + ), + DiagnosticId::Error(err_code.to_owned()), + ); + // Call out where the function is defined + label_fn_like(tcx, &mut err, fn_def_id); + err.multipart_suggestion( + "use parentheses to construct a tuple", + vec![(start, '('.to_string()), (end, ')'.to_string())], + Applicability::MachineApplicable, + ); + err.emit(); + return true; + } + TupleMatchFound::None => {} } + false + }; + + let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); + let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { + minimum_input_count + } else { + provided_arg_count + })); + debug_assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "expected formal_input_tys to be the same size as expected_input_tys" + ); + let formal_and_expected_inputs = IndexVec::from_iter( + formal_input_tys + .iter() + .copied() + .zip(expected_input_tys.iter().copied()) + .map(|vars| self.resolve_vars_if_possible(vars)), + ); + + self.report_arg_errors( + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + full_call_span, + error_span, + args_span, + call_name, + c_variadic, + err_code, + fn_def_id, + try_tuple_wrap_args, + ); + } + } + + fn report_arg_errors( + &self, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, + full_call_span: Span, + error_span: Span, + args_span: Span, + call_name: &str, + c_variadic: bool, + err_code: &str, + fn_def_id: Option<DefId>, + try_tuple_wrap_args: impl FnOnce() -> bool, + ) { + // Don't print if it has error types or is just plain `_` + fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { + tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) + } + + self.set_tainted_by_errors(); + let tcx = self.tcx; + + // A "softer" version of the `demand_compatible`, which checks types without persisting them, + // and treats error types differently + // This will allow us to "probe" for other argument orders that would likely have been correct + let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { + if provided_idx.as_usize() == expected_idx.as_usize() { + return compatibility_diagonal[provided_idx].clone(); + } + + let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; + // If either is an error type, we defy the usual convention and consider them to *not* be + // coercible. This prevents our error message heuristic from trying to pass errors into + // every argument. + if (formal_input_ty, expected_input_ty).references_error() { + return Compatibility::Incompatible(None); + } + + let provided_arg: &hir::Expr<'tcx> = &provided_args[provided_idx]; + let expectation = Expectation::rvalue_hint(self, expected_input_ty); + // FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure. + // + // I had another method of "soft" type checking before, + // but it was failing to find the type of some expressions (like "") + // so I prodded this method and made it pub(super) so I could call it, and it seems to work well. + let checked_ty = self.check_expr_kind(provided_arg, expectation); + + let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); + let can_coerce = self.can_coerce(checked_ty, coerced_ty); + if !can_coerce { + return Compatibility::Incompatible(None); + } + + let subtyping_result = self + .at(&self.misc(provided_arg.span), self.param_env) + .sup(formal_input_ty, coerced_ty); + + // Same as above: if either the coerce type or the checked type is an error type, + // consider them *not* compatible. + let references_error = (coerced_ty, checked_ty).references_error(); + match (references_error, &subtyping_result) { + (false, Ok(_)) => Compatibility::Compatible, + _ => Compatibility::Incompatible(subtyping_result.err()), } + }; + + // The algorithm here is inspired by levenshtein distance and longest common subsequence. + // We'll try to detect 4 different types of mistakes: + // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs + // - An input is missing, which isn't satisfied by *any* of the other arguments + // - Some number of arguments have been provided in the wrong order + // - A type is straight up invalid + + // First, let's find the errors + let (mut errors, matched_inputs) = + ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) + .find_errors(); + + // Precompute the provided types and spans, since that's all we typically need for below + let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args + .iter() + .map(|expr| { + let ty = self + .in_progress_typeck_results + .as_ref() + .unwrap() + .borrow() + .expr_ty_adjusted_opt(*expr) + .unwrap_or_else(|| tcx.ty_error()); + (self.resolve_vars_if_possible(ty), expr.span) + }) + .collect(); + + // Okay, so here's where it gets complicated in regards to what errors + // we emit and how. + // There are 3 different "types" of errors we might encounter. + // 1) Missing/extra/swapped arguments + // 2) Valid but incorrect arguments + // 3) Invalid arguments + // - Currently I think this only comes up with `CyclicTy` + // + // We first need to go through, remove those from (3) and emit those + // as their own error, particularly since they're error code and + // message is special. From what I can tell, we *must* emit these + // here (vs somewhere prior to this function) since the arguments + // become invalid *because* of how they get used in the function. + // It is what it is. + + if errors.is_empty() { + if cfg!(debug_assertions) { + span_bug!(error_span, "expected errors from argument matrix"); + } else { + tcx.sess + .struct_span_err( + error_span, + "argument type mismatch was detected, \ + but rustc had trouble determining where", + ) + .note( + "we would appreciate a bug report: \ + https://github.com/rust-lang/rust-clippy/issues/new", + ) + .emit(); + } + return; + } + + errors.drain_filter(|error| { + let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false }; + let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; + let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; + let cause = &self.misc(provided_span); + let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + if let Some(e) = error { + if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { + self.report_and_explain_type_error(trace, e).emit(); + return true; + } + } + false + }); + + // We're done if we found errors, but we already emitted them. + if errors.is_empty() { + return; + } + + // Okay, now that we've emitted the special errors separately, we + // are only left missing/extra/swapped and mismatched arguments, both + // can be collated pretty easily if needed. + + // Next special case: if there is only one "Incompatible" error, just emit that + if let [ + Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), + ] = &errors[..] + { + let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; + let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; + let cause = &self.misc(provided_arg_span); + let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let mut err = self.report_and_explain_type_error(trace, err); + self.emit_coerce_suggestions( + &mut err, + &provided_args[*provided_idx], + provided_ty, + Expectation::rvalue_hint(self, expected_ty) + .only_has_type(self) + .unwrap_or(formal_ty), + None, + None, + ); + err.span_label( + full_call_span, + format!("arguments to this {} are incorrect", call_name), + ); + // Call out where the function is defined + label_fn_like(tcx, &mut err, fn_def_id); + err.emit(); + return; + } + + // Second, let's try tuple wrapping the args. + // FIXME(compiler-errors): This is currently in its own closure because + // I didn't want to factor it out. + if try_tuple_wrap_args() { + return; + } + + let mut err = if formal_and_expected_inputs.len() == provided_args.len() { + struct_span_err!( + tcx.sess, + full_call_span, + E0308, + "arguments to this {} are incorrect", + call_name, + ) + } else { + tcx.sess.struct_span_err_with_code( + full_call_span, + &format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count(formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(provided_args.len(), "argument"), + if provided_args.len() == 1 { "was" } else { "were" } + ), + DiagnosticId::Error(err_code.to_owned()), + ) + }; + + // As we encounter issues, keep track of what we want to provide for the suggestion + let mut labels = vec![]; + // If there is a single error, we give a specific suggestion; otherwise, we change to + // "did you mean" with the suggested function call + enum SuggestionText { + None, + Provide(bool), + Remove(bool), + Swap, + Reorder, + DidYouMean, + } + let mut suggestion_text = SuggestionText::None; + + let mut errors = errors.into_iter().peekable(); + while let Some(error) = errors.next() { + match error { + Error::Invalid(provided_idx, expected_idx, compatibility) => { + let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; + if let Compatibility::Incompatible(error) = &compatibility { + let cause = &self.misc(provided_span); + let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + if let Some(e) = error { + self.note_type_err( + &mut err, + &trace.cause, + None, + Some(trace.values), + e, + false, + true, + ); + } + } - // Okay, now that we've emitted the special errors separately, we - // are only left missing/extra/swapped and mismatched arguments, both - // can be collated pretty easily if needed. - - // Next special case: if there is only one "Incompatible" error, just emit that - if errors.len() == 1 { - if let Some(Error::Invalid( - input_idx, - arg_idx, - Compatibility::Incompatible(Some(error)), - )) = errors.iter().next() - { - let expected_ty = expected_input_tys[*arg_idx]; - let provided_ty = final_arg_types[*input_idx] - .map(|ty| ty.0) - .unwrap_or_else(|| tcx.ty_error()); - let expected_ty = self.resolve_vars_if_possible(expected_ty); - let provided_ty = self.resolve_vars_if_possible(provided_ty); - let cause = &self.misc(provided_args[*input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - let mut err = self.report_and_explain_type_error(trace, error); self.emit_coerce_suggestions( &mut err, - &provided_args[*input_idx], + &provided_args[provided_idx], provided_ty, - final_arg_types[*input_idx] - .map(|ty| ty.1) - .unwrap_or_else(|| tcx.ty_error()), + Expectation::rvalue_hint(self, expected_ty) + .only_has_type(self) + .unwrap_or(formal_ty), None, None, ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); - err.emit(); - break 'errors; } - } - - let mut err = if minimum_input_count == provided_arg_count { - struct_span_err!( - tcx.sess, - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(minimum_input_count, "argument"), - potentially_plural_count(provided_arg_count, "argument"), - if provided_arg_count == 1 { "was" } else { "were" } - ), - DiagnosticId::Error(err_code.to_owned()), - ) - }; - - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, - } - let mut suggestion_text = SuggestionText::None; - - let mut errors = errors.into_iter().peekable(); - while let Some(error) = errors.next() { - match error { - Error::Invalid(input_idx, arg_idx, compatibility) => { - let expected_ty = expected_input_tys[arg_idx]; - let provided_ty = final_arg_types[input_idx] - .map(|ty| ty.0) - .unwrap_or_else(|| tcx.ty_error()); - let expected_ty = self.resolve_vars_if_possible(expected_ty); - let provided_ty = self.resolve_vars_if_possible(provided_ty); - if let Compatibility::Incompatible(error) = &compatibility { - let cause = &self.misc(provided_args[input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if let Some(e) = error { - self.note_type_err( - &mut err, - &trace.cause, - None, - Some(trace.values), - e, - false, - true, - ); - } + Error::Extra(arg_idx) => { + let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + // FIXME: not suggestable, use something else + format!(" of type `{}`", provided_ty) + } else { + "".to_string() + }; + labels + .push((provided_span, format!("argument{} unexpected", provided_ty_name))); + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + } + Error::Missing(expected_idx) => { + // If there are multiple missing arguments adjacent to each other, + // then we can provide a single error. + + let mut missing_idxs = vec![expected_idx]; + while let Some(e) = errors.next_if(|e| { + matches!(e, Error::Missing(next_expected_idx) + if *next_expected_idx == *missing_idxs.last().unwrap() + 1) + }) { + match e { + Error::Missing(expected_idx) => missing_idxs.push(expected_idx), + _ => unreachable!(), } - - self.emit_coerce_suggestions( - &mut err, - &provided_args[input_idx], - provided_ty, - // FIXME(compiler-errors): expected_ty? - final_arg_types[input_idx] - .map(|ty| ty.1) - .unwrap_or_else(|| tcx.ty_error()), - None, - None, - ); } - Error::Extra(arg_idx) => { - let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] { - if ty.references_error() || ty.has_infer_types() { - "".into() + + // NOTE: Because we might be re-arranging arguments, might have extra + // arguments, etc. it's hard to *really* know where we should provide + // this error label, so as a heuristic, we point to the provided arg, or + // to the call if the missing inputs pass the provided args. + match &missing_idxs[..] { + &[expected_idx] => { + let (_, input_ty) = formal_and_expected_inputs[expected_idx]; + let span = if let Some((_, arg_span)) = + provided_arg_tys.get(expected_idx.to_provided_idx()) + { + *arg_span } else { - format!(" of type `{}`", ty) - } - } else { - "".into() - }; - labels.push(( - provided_args[arg_idx].span, - format!("argument{} unexpected", arg_type), - )); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - } - Error::Missing(input_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. - - let mut missing_idxs = vec![input_idx]; - while let Some(e) = errors.next_if(|e| matches!(e, Error::Missing(input_idx) if *input_idx == (missing_idxs.last().unwrap() + 1))) { - match e { - Error::Missing(input_idx) => missing_idxs.push(input_idx), - _ => unreachable!(), - } + args_span + }; + let rendered = if !has_error_or_infer([input_ty]) { + format!(" of type `{}`", input_ty) + } else { + "".to_string() + }; + labels.push((span, format!("an argument{} is missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Provide(false), + SuggestionText::Provide(_) => SuggestionText::Provide(true), + _ => SuggestionText::DidYouMean, + }; } - - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[input_idx] => { - let expected_ty = expected_input_tys[input_idx]; - let input_ty = self.resolve_vars_if_possible(expected_ty); - let span = if input_idx < provided_arg_count { - let arg_span = provided_args[input_idx].span; - Span::new(arg_span.lo(), arg_span.hi(), arg_span.ctxt(), None) - } else { - args_span - }; - let arg_type = - if input_ty.references_error() || input_ty.has_infer_types() { - "".into() - } else { - format!(" of type `{}`", input_ty) - }; - labels.push((span, format!("an argument{} is missing", arg_type))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let first_input_ty = - self.resolve_vars_if_possible(expected_input_tys[first_idx]); - let second_input_ty = - self.resolve_vars_if_possible(expected_input_tys[second_idx]); - - let span = if second_idx < provided_arg_count { - let first_arg_span = provided_args[first_idx].span; - let second_arg_span = provided_args[second_idx].span; - Span::new( - first_arg_span.lo(), - second_arg_span.hi(), - first_arg_span.ctxt(), - None, - ) - } else { - args_span - }; - let any_unnameable = false - || first_input_ty.references_error() - || first_input_ty.has_infer_types() - || second_input_ty.references_error() - || second_input_ty.has_infer_types(); - let arg_type = if any_unnameable { - "".into() - } else { + &[first_idx, second_idx] => { + let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; + let span = if let (Some((_, first_span)), Some((_, second_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(second_idx.to_provided_idx()), + ) { + first_span.to(*second_span) + } else { + args_span + }; + let rendered = + if !has_error_or_infer([first_expected_ty, second_expected_ty]) { format!( " of type `{}` and `{}`", - first_input_ty, second_input_ty - ) - }; - labels - .push((span, format!("two arguments{} are missing", arg_type))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let first_input_ty = - self.resolve_vars_if_possible(expected_input_tys[first_idx]); - let second_input_ty = - self.resolve_vars_if_possible(expected_input_tys[second_idx]); - let third_input_ty = - self.resolve_vars_if_possible(expected_input_tys[third_idx]); - let span = if third_idx < provided_arg_count { - let first_arg_span = provided_args[first_idx].span; - let third_arg_span = provided_args[third_idx].span; - Span::new( - first_arg_span.lo(), - third_arg_span.hi(), - first_arg_span.ctxt(), - None, - ) - } else { - args_span - }; - let any_unnameable = false - || first_input_ty.references_error() - || first_input_ty.has_infer_types() - || second_input_ty.references_error() - || second_input_ty.has_infer_types() - || third_input_ty.references_error() - || third_input_ty.has_infer_types(); - let arg_type = if any_unnameable { - "".into() - } else { - format!( - " of type `{}`, `{}`, and `{}`", - first_input_ty, second_input_ty, third_input_ty - ) - }; - labels.push(( - span, - format!("three arguments{} are missing", arg_type), - )); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if last_idx < provided_arg_count { - let first_arg_span = provided_args[first_idx].span; - let last_arg_span = provided_args[last_idx].span; - Span::new( - first_arg_span.lo(), - last_arg_span.hi(), - first_arg_span.ctxt(), - None, + first_expected_ty, second_expected_ty ) } else { - // Otherwise just label the whole function - args_span - }; - labels.push((span, format!("multiple arguments are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, + "".to_string() }; - } + labels.push((span, format!("two arguments{} are missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; } - } - Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx) => { - let first_span = provided_args[input_idx].span; - let second_span = provided_args[other_input_idx].span; - - let first_expected_ty = - self.resolve_vars_if_possible(expected_input_tys[arg_idx]); - let first_provided_ty = if let Some((ty, _)) = final_arg_types[input_idx] { - format!(", found `{}`", ty) - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{}`{}", first_expected_ty, first_provided_ty), - )); - let other_expected_ty = - self.resolve_vars_if_possible(expected_input_tys[other_arg_idx]); - let other_provided_ty = - if let Some((ty, _)) = final_arg_types[other_input_idx] { - format!(", found `{}`", ty) + &[first_idx, second_idx, third_idx] => { + let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; + let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; + let span = if let (Some((_, first_span)), Some((_, third_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(third_idx.to_provided_idx()), + ) { + first_span.to(*third_span) } else { - String::new() + args_span }; - labels.push(( - second_span, - format!("expected `{}`{}", other_expected_ty, other_provided_ty), - )); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - let expected_ty = - self.resolve_vars_if_possible(expected_input_tys[dst_arg]); - let provided_ty = if let Some((ty, _)) = final_arg_types[dest_input] { - format!(", found `{}`", ty) + let rendered = if !has_error_or_infer([ + first_expected_ty, + second_expected_ty, + third_expected_ty, + ]) { + format!( + " of type `{}`, `{}`, and `{}`", + first_expected_ty, second_expected_ty, third_expected_ty + ) } else { - String::new() + "".to_string() + }; + labels.push((span, format!("three arguments{} are missing", rendered))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + missing_idxs => { + let first_idx = *missing_idxs.first().unwrap(); + let last_idx = *missing_idxs.last().unwrap(); + // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. + // It's hard to *really* know where we should provide this error label, so this is a + // decent heuristic + let span = if let (Some((_, first_span)), Some((_, last_span))) = ( + provided_arg_tys.get(first_idx.to_provided_idx()), + provided_arg_tys.get(last_idx.to_provided_idx()), + ) { + first_span.to(*last_span) + } else { + args_span + }; + labels.push((span, format!("multiple arguments are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, }; - labels.push(( - provided_args[dest_input].span, - format!("expected `{}`{}", expected_ty, provided_ty), - )); } - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; } } - } - - // If we have less than 5 things to say, it would be useful to call out exactly what's wrong - if labels.len() <= 5 { - for (span, label) in labels { - err.span_label(span, label); - } - } - - // Call out where the function is defined - label_fn_like(tcx, &mut err, fn_def_id); - - // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text { - let source_map = self.sess().source_map(); - let mut suggestion = format!( - "{}(", - source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| String::new()) - ); - for (arg_index, input_idx) in matched_inputs.iter().enumerate() { - let suggestion_text = if let Some(input_idx) = input_idx { - let arg_span = provided_args[*input_idx].span.source_callsite(); - let arg_text = source_map.span_to_snippet(arg_span).unwrap(); - arg_text + Error::Swap( + first_provided_idx, + second_provided_idx, + first_expected_idx, + second_expected_idx, + ) => { + let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; + let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; + let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { + format!(", found `{}`", first_provided_ty) } else { - // Propose a placeholder of the correct type - let expected_ty = expected_input_tys[arg_index]; - let input_ty = self.resolve_vars_if_possible(expected_ty); - if input_ty.is_unit() { - "()".to_string() - } else if !input_ty.is_ty_var() { - format!("/* {} */", input_ty) + String::new() + }; + labels.push(( + first_span, + format!("expected `{}`{}", first_expected_ty, first_provided_ty_name), + )); + + let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; + let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; + let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { + format!(", found `{}`", second_provided_ty) + } else { + String::new() + }; + labels.push(( + second_span, + format!("expected `{}`{}", second_expected_ty, second_provided_ty_name), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Swap, + _ => SuggestionText::DidYouMean, + }; + } + Error::Permutation(args) => { + for (dst_arg, dest_input) in args { + let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; + let (provided_ty, provided_span) = provided_arg_tys[dest_input]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + format!(", found `{}`", provided_ty) } else { - "/* value */".to_string() + String::new() + }; + // FIXME(compiler-errors): Why do we get permutations with the same type? + if expected_ty != provided_ty { + labels.push(( + provided_span, + format!("expected `{}`{}", expected_ty, provided_ty_name), + )); } - }; - suggestion += &suggestion_text; - if arg_index < minimum_input_count - 1 { - suggestion += ", "; } + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Reorder, + _ => SuggestionText::DidYouMean, + }; } - suggestion += ")"; - err.span_suggestion_verbose( - error_span, - &suggestion_text, - suggestion, - Applicability::HasPlaceholders, - ); } - err.emit(); } - for arg in provided_args.iter().skip(minimum_input_count) { - let arg_ty = self.check_expr(&arg); + // If we have less than 5 things to say, it would be useful to call out exactly what's wrong + if labels.len() <= 5 { + for (span, label) in labels { + err.span_label(span, label); + } + } - // If the function is c-style variadic, we skipped a bunch of arguments - // so we need to check those, and write out the types - // Ideally this would be folded into the above, for uniform style - // but c-variadic is already a corner case - if c_variadic { - fn variadic_error<'tcx>( - sess: &'tcx Session, - span: Span, - ty: Ty<'tcx>, - cast_ty: &str, - ) { - use crate::structured_errors::MissingCastForVariadicArg; + // Call out where the function is defined + label_fn_like(tcx, &mut err, fn_def_id); - MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); + // And add a suggestion block for all of the parameters + let suggestion_text = match suggestion_text { + SuggestionText::None => None, + SuggestionText::Provide(plural) => { + Some(format!("provide the argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Remove(plural) => { + Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Swap => Some("swap these arguments".to_string()), + SuggestionText::Reorder => Some("reorder these arguments".to_string()), + SuggestionText::DidYouMean => Some("did you mean".to_string()), + }; + if let Some(suggestion_text) = suggestion_text { + let source_map = self.sess().source_map(); + let mut suggestion = format!( + "{}(", + source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| fn_def_id + .map_or("".to_string(), |fn_def_id| tcx.item_name(fn_def_id).to_string())) + ); + let mut needs_comma = false; + for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { + if needs_comma { + suggestion += ", "; + } else { + needs_comma = true; } - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind() { - ty::Float(ty::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + 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()) { + arg_text + } else { + // Propose a placeholder of the correct type + let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; + if expected_ty.is_unit() { + "()".to_string() + } else if expected_ty.is_suggestable(tcx) { + format!("/* {} */", expected_ty) + } else { + "/* value */".to_string() } - _ => {} - } + }; + suggestion += &suggestion_text; } + suggestion += ")"; + err.span_suggestion_verbose( + error_span, + &suggestion_text, + suggestion, + Applicability::HasPlaceholders, + ); } + + err.emit(); } fn suggested_tuple_wrap( diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index f09c7f51f47..6ee989070b4 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -225,7 +225,7 @@ pub fn resolve_interior<'a, 'tcx>( // Note that each region slot in the types gets a new fresh late bound region, // which means that none of the regions inside relate to any other, even if // typeck had previously found constraints that would cause them to be related. - let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| { + let folded = fcx.tcx.fold_regions(erased, |_, current_depth| { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(counter), diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 8880049371e..79a13ce2fca 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -346,19 +346,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if let Some(def) = actual.ty_adt_def() { - if let Some(full_sp) = tcx.hir().span_if_local(def.did()) { - let def_sp = tcx.sess.source_map().guess_head_span(full_sp); - err.span_label( - def_sp, - format!( - "{} `{}` not found {}", - item_kind, - item_name, - if def.is_enum() && !is_method { "here" } else { "for this" } - ), - ); + let ty_span = match actual.kind() { + ty::Param(param_type) => { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let type_param = generics.type_param(param_type, self.tcx); + Some(self.tcx.def_span(type_param.def_id)) + } + ty::Adt(def, _) if def.did().is_local() => { + tcx.def_ident_span(def.did()).map(|span| span) } + _ => None, + }; + + if let Some(span) = ty_span { + err.span_label( + span, + format!( + "{item_kind} `{item_name}` not found for this {}", + actual.prefix_string(self.tcx) + ), + ); } if self.is_fn_ty(rcvr_ty, span) { @@ -987,6 +994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, + args.map(|args| args.len()), source, out_of_scope_traits, &unsatisfied_predicates, @@ -1725,6 +1733,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, + inputs_len: Option<usize>, source: SelfSource<'tcx>, valid_out_of_scope_traits: Vec<DefId>, unsatisfied_predicates: &[( @@ -1801,7 +1810,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not // implement the `AsRef` trait. let skip = skippable.contains(&did) - || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)); + || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) + || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box<Self>` as a receiver if it only works because of // an autoderef to `&self` @@ -1951,9 +1961,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; // Obtain the span for `param` and use it for a structured suggestion. - if let (Some(param), Some(table)) = (param_type, self.in_progress_typeck_results) { - let table_owner = table.borrow().hir_owner; - let generics = self.tcx.generics_of(table_owner.to_def_id()); + if let Some(param) = param_type { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); let type_param = generics.type_param(param, self.tcx); let hir = self.tcx.hir(); if let Some(def_id) = type_param.def_id.as_local() { diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index e26f211c1c1..0ede9ef7756 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -93,11 +93,7 @@ mod upvar; mod wfcheck; pub mod writeback; -use check::{ - check_abi, check_fn, check_impl_item_well_formed, check_item_well_formed, check_mod_item_types, - check_trait_item_well_formed, -}; -pub use check::{check_item_type, check_wf_new}; +use check::{check_abi, check_fn, check_mod_item_types}; pub use diverges::Diverges; pub use expectation::Expectation; pub use fn_ctxt::*; @@ -245,6 +241,7 @@ impl<'tcx> EnclosingBreakables<'tcx> { pub fn provide(providers: &mut Providers) { method::provide(providers); + wfcheck::provide(providers); *providers = Providers { typeck_item_bodies, typeck_const_arg, @@ -253,9 +250,6 @@ pub fn provide(providers: &mut Providers) { has_typeck_results, adt_destructor, used_trait_imports, - check_item_well_formed, - check_trait_item_well_formed, - check_impl_item_well_formed, check_mod_item_types, region_scope_tree, ..*providers diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index f45b94bcdff..e1ec9f13cd1 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -663,6 +663,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::Mutability::Not => "", }; + let mut_var_suggestion = 'block: { + if !matches!(mutbl, ast::Mutability::Mut) { + break 'block None; + } + + let ident_kind = match binding_parent { + hir::Node::Param(_) => "parameter", + hir::Node::Local(_) => "variable", + hir::Node::Arm(_) => "binding", + + // Provide diagnostics only if the parent pattern is struct-like, + // i.e. where `mut binding` makes sense + hir::Node::Pat(Pat { kind, .. }) => match kind { + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Or(..) + | PatKind::Tuple(..) + | PatKind::Slice(..) => "binding", + + PatKind::Wild + | PatKind::Binding(..) + | PatKind::Path(..) + | PatKind::Box(..) + | PatKind::Ref(..) + | PatKind::Lit(..) + | PatKind::Range(..) => break 'block None, + }, + + // Don't provide suggestions in other cases + _ => break 'block None, + }; + + Some(( + pat.span, + format!("to declare a mutable {ident_kind} use"), + format!("mut {binding}"), + )) + + }; + match binding_parent { // Check that there is explicit type (ie this is not a closure param with inferred type) // so we don't suggest moving something to the type that does not exist @@ -675,6 +715,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Applicability::MachineApplicable ); + + if let Some((sp, msg, sugg)) = mut_var_suggestion { + err.span_note(sp, format!("{msg}: `{sugg}`")); + } } hir::Node::Param(_) | hir::Node::Arm(_) | hir::Node::Pat(_) => { // rely on match ergonomics or it might be nested `&&pat` @@ -684,6 +728,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "", Applicability::MaybeIncorrect, ); + + if let Some((sp, msg, sugg)) = mut_var_suggestion { + err.span_note(sp, format!("{msg}: `{sugg}`")); + } + } + _ if let Some((sp, msg, sugg)) = mut_var_suggestion => { + err.span_suggestion(sp, msg, sugg, Applicability::MachineApplicable); } _ => {} // don't provide suggestions in other cases #55175 } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 161ec31793d..0ce63922098 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -366,7 +366,6 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn resolve_regions_and_report_errors(&self) { self.infcx.process_registered_region_obligations( self.outlives_environment.region_bound_pairs_map(), - Some(self.tcx.lifetimes.re_root_empty), self.param_env, ); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 79312153895..67eb88b73fd 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -7,21 +7,18 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit as hir_visit; -use rustc_hir::intravisit::Visitor; -use rustc_hir::itemlikevisit::ParItemLikeVisitor; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_middle::hir::nested_filter; +use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, EarlyBinder, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, - TypeSuperFoldable, TypeVisitor, + self, AdtKind, DefIdTree, EarlyBinder, GenericParamDefKind, ToPredicate, Ty, TyCtxt, + TypeFoldable, TypeSuperFoldable, TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -70,6 +67,23 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { } } +fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let node = tcx.hir().expect_owner(def_id); + match node { + hir::OwnerNode::Crate(_) => {} + hir::OwnerNode::Item(item) => check_item(tcx, item), + hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), + hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), + hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), + } + + if let Some(generics) = node.generics() { + for param in generics.params { + check_param_wf(tcx, param) + } + } +} + /// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are /// well-formed, meaning that they do not require any constraints not declared in the struct /// definition itself. For example, this definition would be illegal: @@ -84,8 +98,8 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { /// not included it frequently leads to confusing errors in fn bodies. So it's better to check /// the types first. #[instrument(skip(tcx), level = "debug")] -pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let item = tcx.hir().expect_item(def_id); +fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { + let def_id = item.def_id; debug!( ?item.def_id, @@ -156,20 +170,6 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { hir::ItemKind::Const(ty, ..) => { check_item_type(tcx, item.def_id, ty.span, false); } - hir::ItemKind::ForeignMod { items, .. } => { - for it in items.iter() { - let it = tcx.hir().foreign_item(it.id); - match it.kind { - hir::ForeignItemKind::Fn(decl, ..) => { - check_item_fn(tcx, it.def_id, it.ident, it.span, decl) - } - hir::ForeignItemKind::Static(ty, ..) => { - check_item_type(tcx, it.def_id, ty.span, true) - } - hir::ForeignItemKind::Type => (), - } - } - } hir::ItemKind::Struct(ref struct_def, ref ast_generics) => { check_type_defn(tcx, item, false, |fcx| vec![fcx.non_enum_variant(struct_def)]); @@ -191,13 +191,31 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { hir::ItemKind::TraitAlias(..) => { check_trait(tcx, item); } + // `ForeignItem`s are handled separately. + hir::ItemKind::ForeignMod { .. } => {} _ => {} } } -pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let trait_item = tcx.hir().expect_trait_item(def_id); +fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { + let def_id = item.def_id; + + debug!( + ?item.def_id, + item.name = ? tcx.def_path_str(def_id.to_def_id()) + ); + + match item.kind { + hir::ForeignItemKind::Fn(decl, ..) => { + check_item_fn(tcx, item.def_id, item.ident, item.span, decl) + } + hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true), + hir::ForeignItemKind::Type => (), + } +} + +fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { + let def_id = trait_item.def_id; let (method_sig, span) = match trait_item.kind { hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span), @@ -207,7 +225,7 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_object_unsafe_self_trait_by_name(tcx, trait_item); check_associated_item(tcx, trait_item.def_id, span, method_sig); - let encl_trait_def_id = tcx.hir().get_parent_item(hir_id); + let encl_trait_def_id = tcx.local_parent(def_id); let encl_trait = tcx.hir().expect_item(encl_trait_def_id); let encl_trait_def_id = encl_trait.def_id.to_def_id(); let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { @@ -596,13 +614,7 @@ fn ty_known_to_outlive<'tcx>( ) -> bool { resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| { let origin = infer::RelateParamBound(DUMMY_SP, ty, None); - let outlives = &mut TypeOutlives::new( - infcx, - tcx, - region_bound_pairs, - Some(infcx.tcx.lifetimes.re_root_empty), - param_env, - ); + let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env); outlives.type_must_outlive(origin, ty, region); }) } @@ -770,8 +782,8 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } } -pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let impl_item = tcx.hir().expect_impl_item(def_id); +fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { + let def_id = impl_item.def_id; let (method_sig, span) = match impl_item.kind { hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span), @@ -780,7 +792,7 @@ pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { _ => (None, impl_item.span), }; - check_associated_item(tcx, impl_item.def_id, span, method_sig); + check_associated_item(tcx, def_id, span, method_sig); } fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { @@ -990,6 +1002,15 @@ fn check_type_defn<'tcx, F>( 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(), + field.span, + ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))), + ) + } + // For DST, or when drop needs to copy things around, all // intermediate types must be sized. let needs_drop_copy = || { @@ -1006,6 +1027,7 @@ fn check_type_defn<'tcx, F>( } } }; + // All fields (except for possibly the last) should be sized. let all_sized = all_sized || variant.fields.is_empty() || needs_drop_copy(); let unsized_len = if all_sized { 0 } else { 1 }; for (idx, field) in @@ -1030,15 +1052,6 @@ fn check_type_defn<'tcx, F>( ); } - // All field types must be well-formed. - for field in &variant.fields { - fcx.register_wf_obligation( - field.ty.into(), - field.span, - ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))), - ) - } - // Explicit `enum` discriminant values must const-evaluate successfully. if let Some(discr_def_id) = variant.explicit_discr { let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id()); @@ -1827,67 +1840,12 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirI fcx.select_all_obligations_or_error(); } -#[derive(Clone, Copy)] -pub struct CheckTypeWellFormedVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> CheckTypeWellFormedVisitor<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> CheckTypeWellFormedVisitor<'tcx> { - CheckTypeWellFormedVisitor { tcx } - } -} - -impl<'tcx> ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { - fn visit_item(&self, i: &'tcx hir::Item<'tcx>) { - Visitor::visit_item(&mut self.clone(), i); - } - - fn visit_trait_item(&self, trait_item: &'tcx hir::TraitItem<'tcx>) { - Visitor::visit_trait_item(&mut self.clone(), trait_item); - } - - fn visit_impl_item(&self, impl_item: &'tcx hir::ImplItem<'tcx>) { - Visitor::visit_impl_item(&mut self.clone(), impl_item); - } - - fn visit_foreign_item(&self, foreign_item: &'tcx hir::ForeignItem<'tcx>) { - Visitor::visit_foreign_item(&mut self.clone(), foreign_item) - } -} - -impl<'tcx> Visitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - #[instrument(skip(self, i), level = "debug")] - fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { - trace!(?i); - self.tcx.ensure().check_item_well_formed(i.def_id); - hir_visit::walk_item(self, i); - } - - #[instrument(skip(self, trait_item), level = "debug")] - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - trace!(?trait_item); - self.tcx.ensure().check_trait_item_well_formed(trait_item.def_id); - hir_visit::walk_trait_item(self, trait_item); - } - - #[instrument(skip(self, impl_item), level = "debug")] - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - trace!(?impl_item); - self.tcx.ensure().check_impl_item_well_formed(impl_item.def_id); - hir_visit::walk_impl_item(self, impl_item); - } - - fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - check_param_wf(self.tcx, p); - hir_visit::walk_generic_param(self, p); - } +fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { + let items = tcx.hir_module_items(module); + items.par_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id)); } /////////////////////////////////////////////////////////////////////////// @@ -1972,3 +1930,7 @@ fn error_392( err.span_label(span, "unused parameter"); err } + +pub fn provide(providers: &mut Providers) { + *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; +} diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 3903448a007..447ec87f302 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -146,7 +146,7 @@ pub fn provide(providers: &mut Providers) { use self::builtin::coerce_unsized_info; use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls}; use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; - use self::orphan::orphan_check_crate; + use self::orphan::orphan_check_impl; *providers = Providers { coherent_trait, @@ -155,7 +155,7 @@ pub fn provide(providers: &mut Providers) { inherent_impls, crate_inherent_impls_overlap_check, coerce_unsized_info, - orphan_check_crate, + orphan_check_impl, ..*providers }; } @@ -171,21 +171,12 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { check_impl(tcx, impl_def_id, trait_ref); check_object_overlap(tcx, impl_def_id, trait_ref); - } - builtin::check_trait(tcx, def_id); -} -pub fn check_coherence(tcx: TyCtxt<'_>) { - tcx.sess.time("unsafety_checking", || unsafety::check(tcx)); - tcx.ensure().orphan_check_crate(()); - - for &trait_def_id in tcx.all_local_trait_impls(()).keys() { - tcx.ensure().coherent_trait(trait_def_id); + tcx.sess.time("unsafety_checking", || unsafety::check_item(tcx, impl_def_id)); + tcx.sess.time("orphan_checking", || tcx.ensure().orphan_check_impl(impl_def_id)); } - // these queries are executed for side-effects (error reporting): - tcx.ensure().crate_inherent_impls(()); - tcx.ensure().crate_inherent_impls_overlap_check(()); + builtin::check_trait(tcx, def_id); } /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 9ddfc8d5cc8..f3a043a08a3 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -18,26 +18,29 @@ use rustc_span::Span; use rustc_trait_selection::traits; use std::ops::ControlFlow; -pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] { - let mut errors = Vec::new(); - for (&trait_def_id, impls_of_trait) in tcx.all_local_trait_impls(()) { - for &impl_of_trait in impls_of_trait { - match orphan_check_impl(tcx, impl_of_trait) { - Ok(()) => {} - Err(_) => errors.push(impl_of_trait), - } - } +#[instrument(skip(tcx), level = "debug")] +pub(crate) fn orphan_check_impl( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if let Some(err) = trait_ref.error_reported() { + return Err(err); + } - if tcx.trait_is_auto(trait_def_id) { - lint_auto_trait_impls(tcx, trait_def_id, impls_of_trait); - } + let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); + if tcx.trait_is_auto(trait_ref.def_id) { + lint_auto_trait_impl(tcx, trait_ref, impl_def_id); } - tcx.arena.alloc_slice(&errors) + + ret } -#[instrument(skip(tcx), level = "debug")] -fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { - let trait_ref = tcx.impl_trait_ref(def_id).unwrap(); +fn do_orphan_check_impl<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; let item = tcx.hir().item(hir::ItemId { def_id }); @@ -329,89 +332,82 @@ fn emit_orphan_check_error<'tcx>( /// Lint impls of auto traits if they are likely to have /// unsound or surprising effects on auto impls. -fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) { - let mut non_covering_impls = Vec::new(); - for &impl_def_id in impls { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if trait_ref.references_error() { - return; - } +fn lint_auto_trait_impl<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + impl_def_id: LocalDefId, +) { + if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { + return; + } - if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { + assert_eq!(trait_ref.substs.len(), 1); + let self_ty = trait_ref.self_ty(); + let (self_type_did, substs) = match self_ty.kind() { + ty::Adt(def, substs) => (def.did(), substs), + _ => { + // FIXME: should also lint for stuff like `&i32` but + // considering that auto traits are unstable, that + // isn't too important for now as this only affects + // crates using `nightly`, and std. return; } + }; - assert_eq!(trait_ref.substs.len(), 1); - let self_ty = trait_ref.self_ty(); - let (self_type_did, substs) = match self_ty.kind() { - ty::Adt(def, substs) => (def.did(), substs), - _ => { - // FIXME: should also lint for stuff like `&i32` but - // considering that auto traits are unstable, that - // isn't too important for now as this only affects - // crates using `nightly`, and std. - continue; - } - }; + // Impls which completely cover a given root type are fine as they + // disable auto impls entirely. So only lint if the substs + // are not a permutation of the identity substs. + let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else { + // ok + return; + }; - // Impls which completely cover a given root type are fine as they - // disable auto impls entirely. So only lint if the substs - // are not a permutation of the identity substs. - match tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) { - Ok(()) => {} // ok - Err(arg) => { - // Ideally: - // - // - compute the requirements for the auto impl candidate - // - check whether these are implied by the non covering impls - // - if not, emit the lint - // - // What we do here is a bit simpler: - // - // - badly check if an auto impl candidate definitely does not apply - // for the given simplified type - // - if so, do not lint - if fast_reject_auto_impl(tcx, trait_def_id, self_ty) { - // ok - } else { - non_covering_impls.push((impl_def_id, self_type_did, arg)); - } - } - } + // Ideally: + // + // - compute the requirements for the auto impl candidate + // - check whether these are implied by the non covering impls + // - if not, emit the lint + // + // What we do here is a bit simpler: + // + // - badly check if an auto impl candidate definitely does not apply + // for the given simplified type + // - if so, do not lint + if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) { + // ok + return; } - for &(impl_def_id, self_type_did, arg) in &non_covering_impls { - tcx.struct_span_lint_hir( - lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, - tcx.hir().local_def_id_to_hir_id(impl_def_id), - tcx.def_span(impl_def_id), - |err| { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); - let mut err = err.build(&format!( - "cross-crate traits with a default impl, like `{}`, \ + tcx.struct_span_lint_hir( + lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, + tcx.hir().local_def_id_to_hir_id(impl_def_id), + tcx.def_span(impl_def_id), + |err| { + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); + let mut err = err.build(&format!( + "cross-crate traits with a default impl, like `{}`, \ should not be specialized", - tcx.def_path_str(trait_def_id), - )); - match arg { - ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); - } - ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); - } + tcx.def_path_str(trait_ref.def_id), + )); + match arg { + ty::util::NotUniqueParam::DuplicateParam(arg) => { + err.note(&format!("`{}` is mentioned multiple times", arg)); } - err.span_note( - item_span, - &format!( - "try using the same sequence of generic parameters as the {} definition", - self_descr, - ), - ); - err.emit(); - }, - ); - } + ty::util::NotUniqueParam::NotParam(arg) => { + err.note(&format!("`{}` is not a generic parameter", arg)); + } + } + err.span_note( + item_span, + &format!( + "try using the same sequence of generic parameters as the {} definition", + self_descr, + ), + ); + err.emit(); + }, + ); } fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs index 3cfc96ccbfd..e45fb5fe41c 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_typeck/src/coherence/unsafety.rs @@ -6,37 +6,18 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Unsafety; use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::LocalDefId; -pub fn check(tcx: TyCtxt<'_>) { - for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { - let item = tcx.hir().item(id); - if let hir::ItemKind::Impl(ref impl_) = item.kind { - check_unsafety_coherence( - tcx, - item, - Some(&impl_.generics), - impl_.unsafety, - impl_.polarity, - ); - } - } - } -} +pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { + debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl)); + let item = tcx.hir().expect_item(def_id); + let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() }; -fn check_unsafety_coherence<'tcx>( - tcx: TyCtxt<'tcx>, - item: &hir::Item<'_>, - impl_generics: Option<&hir::Generics<'_>>, - unsafety: hir::Unsafety, - polarity: hir::ImplPolarity, -) { if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { let trait_def = tcx.trait_def(trait_ref.def_id); - let unsafe_attr = impl_generics.and_then(|generics| { - generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle") - }); - match (trait_def.unsafety, unsafe_attr, unsafety, polarity) { + let unsafe_attr = + impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); + match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) { (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { struct_span_err!( tcx.sess, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 1f2e6ad86bd..2a52167c597 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -393,7 +393,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { } fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { - let ty = self.tcx.fold_regions(ty, &mut false, |r, _| match *r { + let ty = self.tcx.fold_regions(ty, |r, _| match *r { ty::ReErased => self.tcx.lifetimes.re_static, _ => r, }); @@ -1917,7 +1917,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( Some(ty) => { let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; // Typeck doesn't expect erased regions to be returned from `type_of`. - let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r { + let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r { ty::ReErased => tcx.lifetimes.re_static, _ => r, }); diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 7011dd6e15c..6ee2b544916 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -772,7 +772,7 @@ fn infer_placeholder_type<'a>( } // Typeck doesn't expect erased regions to be returned from `type_of`. - tcx.fold_regions(ty, &mut false, |r, _| match *r { + tcx.fold_regions(ty, |r, _| match *r { ty::ReErased => tcx.lifetimes.re_static, _ => r, }) diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 67a3d4a4d02..4cdec615d82 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -6,18 +6,18 @@ use rustc_session::{parse::ParseSess, SessionDiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(SessionDiagnostic)] -#[error(code = "E0062", slug = "typeck-field-multiply-specified-in-initializer")] +#[error(typeck::field_multiply_specified_in_initializer, code = "E0062")] pub struct FieldMultiplySpecifiedInInitializer { #[primary_span] #[label] pub span: Span, - #[label = "previous-use-label"] + #[label(typeck::previous_use_label)] pub prev_span: Span, pub ident: Ident, } #[derive(SessionDiagnostic)] -#[error(code = "E0092", slug = "typeck-unrecognized-atomic-operation")] +#[error(typeck::unrecognized_atomic_operation, code = "E0092")] pub struct UnrecognizedAtomicOperation<'a> { #[primary_span] #[label] @@ -26,7 +26,7 @@ pub struct UnrecognizedAtomicOperation<'a> { } #[derive(SessionDiagnostic)] -#[error(code = "E0094", slug = "typeck-wrong-number-of-generic-arguments-to-intrinsic")] +#[error(typeck::wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")] pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { #[primary_span] #[label] @@ -37,7 +37,7 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { } #[derive(SessionDiagnostic)] -#[error(code = "E0093", slug = "typeck-unrecognized-intrinsic-function")] +#[error(typeck::unrecognized_intrinsic_function, code = "E0093")] pub struct UnrecognizedIntrinsicFunction { #[primary_span] #[label] @@ -46,19 +46,19 @@ pub struct UnrecognizedIntrinsicFunction { } #[derive(SessionDiagnostic)] -#[error(code = "E0195", slug = "typeck-lifetimes-or-bounds-mismatch-on-trait")] +#[error(typeck::lifetimes_or_bounds_mismatch_on_trait, code = "E0195")] pub struct LifetimesOrBoundsMismatchOnTrait { #[primary_span] #[label] pub span: Span, - #[label = "generics-label"] + #[label(typeck::generics_label)] pub generics_span: Option<Span>, pub item_kind: &'static str, pub ident: Ident, } #[derive(SessionDiagnostic)] -#[error(code = "E0120", slug = "typeck-drop-impl-on-wrong-item")] +#[error(typeck::drop_impl_on_wrong_item, code = "E0120")] pub struct DropImplOnWrongItem { #[primary_span] #[label] @@ -66,18 +66,18 @@ pub struct DropImplOnWrongItem { } #[derive(SessionDiagnostic)] -#[error(code = "E0124", slug = "typeck-field-already-declared")] +#[error(typeck::field_already_declared, code = "E0124")] pub struct FieldAlreadyDeclared { pub field_name: Ident, #[primary_span] #[label] pub span: Span, - #[label = "previous-decl-label"] + #[label(typeck::previous_decl_label)] pub prev_span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0184", slug = "typeck-copy-impl-on-type-with-dtor")] +#[error(typeck::copy_impl_on_type_with_dtor, code = "E0184")] pub struct CopyImplOnTypeWithDtor { #[primary_span] #[label] @@ -85,14 +85,14 @@ pub struct CopyImplOnTypeWithDtor { } #[derive(SessionDiagnostic)] -#[error(code = "E0203", slug = "typeck-multiple-relaxed-default-bounds")] +#[error(typeck::multiple_relaxed_default_bounds, code = "E0203")] pub struct MultipleRelaxedDefaultBounds { #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0206", slug = "typeck-copy-impl-on-non-adt")] +#[error(typeck::copy_impl_on_non_adt, code = "E0206")] pub struct CopyImplOnNonAdt { #[primary_span] #[label] @@ -100,23 +100,23 @@ pub struct CopyImplOnNonAdt { } #[derive(SessionDiagnostic)] -#[error(code = "E0224", slug = "typeck-trait-object-declared-with-no-traits")] +#[error(typeck::trait_object_declared_with_no_traits, code = "E0224")] pub struct TraitObjectDeclaredWithNoTraits { #[primary_span] pub span: Span, - #[label = "alias-span"] + #[label(typeck::alias_span)] pub trait_alias_span: Option<Span>, } #[derive(SessionDiagnostic)] -#[error(code = "E0227", slug = "typeck-ambiguous-lifetime-bound")] +#[error(typeck::ambiguous_lifetime_bound, code = "E0227")] pub struct AmbiguousLifetimeBound { #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0229", slug = "typeck-assoc-type-binding-not-allowed")] +#[error(typeck::assoc_type_binding_not_allowed, code = "E0229")] pub struct AssocTypeBindingNotAllowed { #[primary_span] #[label] @@ -124,14 +124,14 @@ pub struct AssocTypeBindingNotAllowed { } #[derive(SessionDiagnostic)] -#[error(code = "E0436", slug = "typeck-functional-record-update-on-non-struct")] +#[error(typeck::functional_record_update_on_non_struct, code = "E0436")] pub struct FunctionalRecordUpdateOnNonStruct { #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0516", slug = "typeck-typeof-reserved-keyword-used")] +#[error(typeck::typeof_reserved_keyword_used, code = "E0516")] pub struct TypeofReservedKeywordUsed<'tcx> { pub ty: Ty<'tcx>, #[primary_span] @@ -142,25 +142,25 @@ pub struct TypeofReservedKeywordUsed<'tcx> { } #[derive(SessionDiagnostic)] -#[error(code = "E0572", slug = "typeck-return-stmt-outside-of-fn-body")] +#[error(typeck::return_stmt_outside_of_fn_body, code = "E0572")] pub struct ReturnStmtOutsideOfFnBody { #[primary_span] pub span: Span, - #[label = "encl-body-label"] + #[label(typeck::encl_body_label)] pub encl_body_span: Option<Span>, - #[label = "encl-fn-label"] + #[label(typeck::encl_fn_label)] pub encl_fn_span: Option<Span>, } #[derive(SessionDiagnostic)] -#[error(code = "E0627", slug = "typeck-yield-expr-outside-of-generator")] +#[error(typeck::yield_expr_outside_of_generator, code = "E0627")] pub struct YieldExprOutsideOfGenerator { #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0639", slug = "typeck-struct-expr-non-exhaustive")] +#[error(typeck::struct_expr_non_exhaustive, code = "E0639")] pub struct StructExprNonExhaustive { #[primary_span] pub span: Span, @@ -168,26 +168,26 @@ pub struct StructExprNonExhaustive { } #[derive(SessionDiagnostic)] -#[error(code = "E0699", slug = "typeck-method-call-on-unknown-type")] +#[error(typeck::method_call_on_unknown_type, code = "E0699")] pub struct MethodCallOnUnknownType { #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error(code = "E0719", slug = "typeck-value-of-associated-struct-already-specified")] +#[error(typeck::value_of_associated_struct_already_specified, code = "E0719")] pub struct ValueOfAssociatedStructAlreadySpecified { #[primary_span] #[label] pub span: Span, - #[label = "previous-bound-label"] + #[label(typeck::previous_bound_label)] pub prev_span: Span, pub item_name: Ident, pub def_path: String, } #[derive(SessionDiagnostic)] -#[error(code = "E0745", slug = "typeck-address-of-temporary-taken")] +#[error(typeck::address_of_temporary_taken, code = "E0745")] pub struct AddressOfTemporaryTaken { #[primary_span] #[label] @@ -197,7 +197,7 @@ pub struct AddressOfTemporaryTaken { #[derive(SessionSubdiagnostic)] pub enum AddReturnTypeSuggestion<'tcx> { #[suggestion( - slug = "typeck-add-return-type-add", + typeck::add_return_type_add, code = "-> {found} ", applicability = "machine-applicable" )] @@ -207,7 +207,7 @@ pub enum AddReturnTypeSuggestion<'tcx> { found: Ty<'tcx>, }, #[suggestion( - slug = "typeck-add-return-type-missing-here", + typeck::add_return_type_missing_here, code = "-> _ ", applicability = "has-placeholders" )] @@ -219,12 +219,12 @@ pub enum AddReturnTypeSuggestion<'tcx> { #[derive(SessionSubdiagnostic)] pub enum ExpectedReturnTypeLabel<'tcx> { - #[label(slug = "typeck-expected-default-return-type")] + #[label(typeck::expected_default_return_type)] Unit { #[primary_span] span: Span, }, - #[label(slug = "typeck-expected-return-type")] + #[label(typeck::expected_return_type)] Other { #[primary_span] span: Span, @@ -233,7 +233,7 @@ pub enum ExpectedReturnTypeLabel<'tcx> { } #[derive(SessionDiagnostic)] -#[error(slug = "typeck-unconstrained-opaque-type")] +#[error(typeck::unconstrained_opaque_type)] #[note] pub struct UnconstrainedOpaqueType { #[primary_span] @@ -301,7 +301,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { } #[derive(SessionDiagnostic)] -#[error(code = "E0183", slug = "typeck-manual-implementation")] +#[error(typeck::manual_implementation, code = "E0183")] #[help] pub struct ManualImplementation { #[primary_span] @@ -311,7 +311,7 @@ pub struct ManualImplementation { } #[derive(SessionDiagnostic)] -#[error(slug = "typeck-substs-on-overridden-impl")] +#[error(typeck::substs_on_overridden_impl)] pub struct SubstsOnOverriddenImpl { #[primary_span] pub span: Span, diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index c089d25d222..981c35e184b 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -13,7 +13,6 @@ use min_specialization::check_min_specialization; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::query::Providers; @@ -54,25 +53,15 @@ mod min_specialization; /// impl<'a> Trait<Foo> for Bar { type X = &'a i32; } /// // ^ 'a is unused and appears in assoc type, error /// ``` -pub fn impl_wf_check(tcx: TyCtxt<'_>) { - // We will tag this as part of the WF check -- logically, it is, - // but it's one that we must perform earlier than the rest of - // WfCheck. - tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)) -} - fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let min_specialization = tcx.features().min_specialization; let module = tcx.hir_module_items(module_def_id); for id in module.items() { if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { - let item = tcx.hir().item(id); - if let hir::ItemKind::Impl(ref impl_) = item.kind { - enforce_impl_params_are_constrained(tcx, item.def_id, impl_.items); - enforce_impl_items_are_distinct(tcx, impl_.items); - if min_specialization { - check_min_specialization(tcx, item.def_id.to_def_id(), item.span); - } + enforce_impl_params_are_constrained(tcx, id.def_id); + enforce_impl_items_are_distinct(tcx, id.def_id); + if min_specialization { + check_min_specialization(tcx, id.def_id); } } } @@ -82,11 +71,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_impl_wf, ..*providers }; } -fn enforce_impl_params_are_constrained( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - impl_item_refs: &[hir::ImplItemRef], -) { +fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { // Every lifetime used in an associated type must be constrained. let impl_self_ty = tcx.type_of(impl_def_id); if impl_self_ty.references_error() { @@ -114,9 +99,9 @@ fn enforce_impl_params_are_constrained( ); // Disallow unconstrained lifetimes, but only if they appear in assoc types. - let lifetimes_in_associated_types: FxHashSet<_> = impl_item_refs + let lifetimes_in_associated_types: FxHashSet<_> = tcx + .associated_item_def_ids(impl_def_id) .iter() - .map(|item_ref| item_ref.id.def_id) .flat_map(|def_id| { let item = tcx.associated_item(def_id); match item.kind { @@ -216,33 +201,32 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) } /// Enforce that we do not have two items in an impl with the same name. -fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_item_refs: &[hir::ImplItemRef]) { +fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { let mut seen_type_items = FxHashMap::default(); let mut seen_value_items = FxHashMap::default(); - for impl_item_ref in impl_item_refs { - let impl_item = tcx.hir().impl_item(impl_item_ref.id); + for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) { + let impl_item = tcx.associated_item(impl_item_ref); let seen_items = match impl_item.kind { - hir::ImplItemKind::TyAlias(_) => &mut seen_type_items, + ty::AssocKind::Type => &mut seen_type_items, _ => &mut seen_value_items, }; - match seen_items.entry(impl_item.ident.normalize_to_macros_2_0()) { + let span = tcx.def_span(impl_item_ref); + let ident = impl_item.ident(tcx); + match seen_items.entry(ident.normalize_to_macros_2_0()) { Occupied(entry) => { let mut err = struct_span_err!( tcx.sess, - impl_item.span, + span, E0201, "duplicate definitions with name `{}`:", - impl_item.ident - ); - err.span_label( - *entry.get(), - format!("previous definition of `{}` here", impl_item.ident), + ident ); - err.span_label(impl_item.span, "duplicate definition"); + err.span_label(*entry.get(), format!("previous definition of `{}` here", ident)); + err.span_label(span, "duplicate definition"); err.emit(); } Vacant(entry) => { - entry.insert(impl_item.span); + entry.insert(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 0ecc28e6054..f07396ce74f 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -79,19 +79,19 @@ use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::traits::{self, translate_substs, wf}; -pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: DefId, span: Span) { +pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { tcx.infer_ctxt().enter(|infcx| { - check_always_applicable(&infcx, impl_def_id, node, span); + check_always_applicable(&infcx, impl_def_id, node); }); } } -fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: DefId) -> Option<Node> { +fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> { let trait_ref = tcx.impl_trait_ref(impl1_def_id)?; let trait_def = tcx.trait_def(trait_ref.def_id); - let impl2_node = trait_def.ancestors(tcx, impl1_def_id).ok()?.nth(1)?; + let impl2_node = trait_def.ancestors(tcx, impl1_def_id.to_def_id()).ok()?.nth(1)?; let always_applicable_trait = matches!(trait_def.specialization_kind, TraitSpecializationKind::AlwaysApplicable); @@ -103,15 +103,8 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: DefId) -> Option<No } /// Check that `impl1` is a sound specialization -fn check_always_applicable( - infcx: &InferCtxt<'_, '_>, - impl1_def_id: DefId, - impl2_node: Node, - span: Span, -) { - if let Some((impl1_substs, impl2_substs)) = - get_impl_substs(infcx, impl1_def_id, impl2_node, span) - { +fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, impl2_node: Node) { + if let Some((impl1_substs, impl2_substs)) = get_impl_substs(infcx, impl1_def_id, impl2_node) { let impl2_def_id = impl2_node.def_id(); debug!( "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", @@ -126,17 +119,10 @@ fn check_always_applicable( unconstrained_parent_impl_substs(tcx, impl2_def_id, impl2_substs) }; + let span = tcx.def_span(impl1_def_id); check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); - - check_predicates( - infcx, - impl1_def_id.expect_local(), - impl1_substs, - impl2_node, - impl2_substs, - span, - ); + check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } @@ -152,20 +138,21 @@ fn check_always_applicable( /// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`. fn get_impl_substs<'tcx>( infcx: &InferCtxt<'_, 'tcx>, - impl1_def_id: DefId, + impl1_def_id: LocalDefId, impl2_node: Node, - span: Span, ) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { let tcx = infcx.tcx; let param_env = tcx.param_env(impl1_def_id); - let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id); - let impl2_substs = translate_substs(infcx, param_env, impl1_def_id, impl1_substs, impl2_node); + let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id()); + let impl2_substs = + translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); // Conservatively use an empty `ParamEnv`. let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); + infcx.resolve_regions_and_report_errors(impl1_def_id.to_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 }); return None; }; diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index d613edf0ab0..b6d4f5fcda6 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -509,11 +509,21 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { } tcx.sess.track_errors(|| { - tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx)); + tcx.sess.time("impl_wf_inference", || { + tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)) + }); })?; tcx.sess.track_errors(|| { - tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx)); + tcx.sess.time("coherence_checking", || { + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { + tcx.ensure().coherent_trait(trait_def_id); + } + + // these queries are executed for side-effects (error reporting): + tcx.ensure().crate_inherent_impls(()); + tcx.ensure().crate_inherent_impls_overlap_check(()); + }); })?; if tcx.features().rustc_attrs { @@ -523,7 +533,9 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { } tcx.sess.track_errors(|| { - tcx.sess.time("wf_checking", || check::check_wf_new(tcx)); + tcx.sess.time("wf_checking", || { + tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) + }); })?; // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync. |
