diff options
| author | Folkert de Vries <flokkievids@gmail.com> | 2025-08-03 10:44:41 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-03 10:44:41 +0000 |
| commit | c691374cc19d583103f3da5d655efb308a1648b9 (patch) | |
| tree | 767496caef5b54e56f46a834d0d612705ce5e488 /compiler | |
| parent | daa742afe5970109c1e15b391226f78087b10439 (diff) | |
| parent | 49aa0ecc7b251003e61c38a56471195a2d3dabee (diff) | |
| download | rust-c691374cc19d583103f3da5d655efb308a1648b9.tar.gz rust-c691374cc19d583103f3da5d655efb308a1648b9.zip | |
Merge pull request #1889 from rust-lang/rustc-pull
Rustc pull update
Diffstat (limited to 'compiler')
293 files changed, 5251 insertions, 4661 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 80b44e432ee..c2405553756 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::fmt::{self, Write}; use std::ops::{Bound, Deref}; use std::{cmp, iter}; @@ -5,7 +6,7 @@ use std::{cmp, iter}; use rustc_hashes::Hash64; use rustc_index::Idx; use rustc_index::bit_set::BitMatrix; -use tracing::debug; +use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer, @@ -313,7 +314,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { scalar_valid_range: (Bound<u128>, Bound<u128>), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator<Item = (VariantIdx, i128)>, - dont_niche_optimize_enum: bool, always_sized: bool, ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> { let (present_first, present_second) = { @@ -352,13 +352,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { // structs. (We have also handled univariant enums // that allow representation optimization.) assert!(is_enum); - self.layout_of_enum( - repr, - variants, - discr_range_of_repr, - discriminants, - dont_niche_optimize_enum, - ) + self.layout_of_enum(repr, variants, discr_range_of_repr, discriminants) } } @@ -599,7 +593,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator<Item = (VariantIdx, i128)>, - dont_niche_optimize_enum: bool, ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> { // Until we've decided whether to use the tagged or // niche filling LayoutData, we don't want to intern the @@ -618,7 +611,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> { - if dont_niche_optimize_enum { + if repr.inhibit_enum_layout_opt() { return None; } @@ -774,30 +767,63 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let niche_filling_layout = calculate_niche_filling_layout(); - let (mut min, mut max) = (i128::MAX, i128::MIN); let discr_type = repr.discr_type(); - let bits = Integer::from_attr(dl, discr_type).size().bits(); - for (i, mut val) in discriminants { - if !repr.c() && variants[i].iter().any(|f| f.is_uninhabited()) { - continue; - } - if discr_type.is_signed() { - // sign extend the raw representation to be an i128 - val = (val << (128 - bits)) >> (128 - bits); - } - if val < min { - min = val; - } - if val > max { - max = val; - } - } - // We might have no inhabited variants, so pretend there's at least one. - if (min, max) == (i128::MAX, i128::MIN) { - min = 0; - max = 0; - } - assert!(min <= max, "discriminant range is {min}...{max}"); + let discr_int = Integer::from_attr(dl, discr_type); + // Because we can only represent one range of valid values, we'll look for the + // largest range of invalid values and pick everything else as the range of valid + // values. + + // First we need to sort the possible discriminant values so that we can look for the largest gap: + let valid_discriminants: BTreeSet<i128> = discriminants + .filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited())) + .map(|(_, val)| { + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + // FIXME: do this at the discriminant iterator creation sites + discr_int.size().sign_extend(val as u128) + } else { + val + } + }) + .collect(); + trace!(?valid_discriminants); + let discriminants = valid_discriminants.iter().copied(); + //let next_discriminants = discriminants.clone().cycle().skip(1); + let next_discriminants = + discriminants.clone().chain(valid_discriminants.first().copied()).skip(1); + // Iterate over pairs of each discriminant together with the next one. + // Since they were sorted, we can now compute the niche sizes and pick the largest. + let discriminants = discriminants.zip(next_discriminants); + let largest_niche = discriminants.max_by_key(|&(start, end)| { + trace!(?start, ?end); + // If this is a wraparound range, the niche size is `MAX - abs(diff)`, as the diff between + // the two end points is actually the size of the valid discriminants. + let dist = if start > end { + // Overflow can happen for 128 bit discriminants if `end` is negative. + // But in that case casting to `u128` still gets us the right value, + // as the distance must be positive if the lhs of the subtraction is larger than the rhs. + let dist = start.wrapping_sub(end); + if discr_type.is_signed() { + discr_int.signed_max().wrapping_sub(dist) as u128 + } else { + discr_int.size().unsigned_int_max() - dist as u128 + } + } else { + // Overflow can happen for 128 bit discriminants if `start` is negative. + // But in that case casting to `u128` still gets us the right value, + // as the distance must be positive if the lhs of the subtraction is larger than the rhs. + end.wrapping_sub(start) as u128 + }; + trace!(?dist); + dist + }); + trace!(?largest_niche); + + // `max` is the last valid discriminant before the largest niche + // `min` is the first valid discriminant after the largest niche + let (max, min) = largest_niche + // We might have no inhabited variants, so pretend there's at least one. + .unwrap_or((0, 0)); let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max); let mut align = dl.aggregate_align; diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5bd73502d98..14e256b8045 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1205,6 +1205,19 @@ impl Integer { } } + /// Returns the smallest signed value that can be represented by this Integer. + #[inline] + pub fn signed_min(self) -> i128 { + use Integer::*; + match self { + I8 => i8::MIN as i128, + I16 => i16::MIN as i128, + I32 => i32::MIN as i128, + I64 => i64::MIN as i128, + I128 => i128::MIN, + } + } + /// Finds the smallest Integer type which can represent the signed value. #[inline] pub fn fit_signed(x: i128) -> Integer { @@ -1376,6 +1389,28 @@ impl WrappingRange { } } + /// Returns `true` if all the values in `other` are contained in this range, + /// when the values are considered as having width `size`. + #[inline(always)] + pub fn contains_range(&self, other: Self, size: Size) -> bool { + if self.is_full_for(size) { + true + } else { + let trunc = |x| size.truncate(x); + + let delta = self.start; + let max = trunc(self.end.wrapping_sub(delta)); + + let other_start = trunc(other.start.wrapping_sub(delta)); + let other_end = trunc(other.end.wrapping_sub(delta)); + + // Having shifted both input ranges by `delta`, now we only need to check + // whether `0..=max` contains `other_start..=other_end`, which can only + // happen if the other doesn't wrap since `self` isn't everything. + (other_start <= other_end) && (other_end <= max) + } + } + /// Returns `self` with replaced `start` #[inline(always)] fn with_start(mut self, start: u128) -> Self { diff --git a/compiler/rustc_abi/src/tests.rs b/compiler/rustc_abi/src/tests.rs index d993012378c..d49c2d44af8 100644 --- a/compiler/rustc_abi/src/tests.rs +++ b/compiler/rustc_abi/src/tests.rs @@ -5,3 +5,66 @@ fn align_constants() { assert_eq!(Align::ONE, Align::from_bytes(1).unwrap()); assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap()); } + +#[test] +fn wrapping_range_contains_range() { + let size16 = Size::from_bytes(16); + + let a = WrappingRange { start: 10, end: 20 }; + assert!(a.contains_range(a, size16)); + assert!(a.contains_range(WrappingRange { start: 11, end: 19 }, size16)); + assert!(a.contains_range(WrappingRange { start: 10, end: 10 }, size16)); + assert!(a.contains_range(WrappingRange { start: 20, end: 20 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 10, end: 21 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 9, end: 20 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 4, end: 6 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 24, end: 26 }, size16)); + + assert!(!a.contains_range(WrappingRange { start: 16, end: 14 }, size16)); + + let b = WrappingRange { start: 20, end: 10 }; + assert!(b.contains_range(b, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 20 }, size16)); + assert!(b.contains_range(WrappingRange { start: 10, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 0, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 30 }, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 9 }, size16)); + assert!(b.contains_range(WrappingRange { start: 21, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 999, end: 9999 }, size16)); + assert!(b.contains_range(WrappingRange { start: 999, end: 9 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 19, end: 19 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 11, end: 11 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 19, end: 11 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 11, end: 19 }, size16)); + + let f = WrappingRange { start: 0, end: u128::MAX }; + assert!(f.contains_range(WrappingRange { start: 10, end: 20 }, size16)); + assert!(f.contains_range(WrappingRange { start: 20, end: 10 }, size16)); + + let g = WrappingRange { start: 2, end: 1 }; + assert!(g.contains_range(WrappingRange { start: 10, end: 20 }, size16)); + assert!(g.contains_range(WrappingRange { start: 20, end: 10 }, size16)); + + let size1 = Size::from_bytes(1); + let u8r = WrappingRange { start: 0, end: 255 }; + let i8r = WrappingRange { start: 128, end: 127 }; + assert!(u8r.contains_range(i8r, size1)); + assert!(i8r.contains_range(u8r, size1)); + assert!(!u8r.contains_range(i8r, size16)); + assert!(i8r.contains_range(u8r, size16)); + + let boolr = WrappingRange { start: 0, end: 1 }; + assert!(u8r.contains_range(boolr, size1)); + assert!(i8r.contains_range(boolr, size1)); + assert!(!boolr.contains_range(u8r, size1)); + assert!(!boolr.contains_range(i8r, size1)); + + let cmpr = WrappingRange { start: 255, end: 1 }; + assert!(u8r.contains_range(cmpr, size1)); + assert!(i8r.contains_range(cmpr, size1)); + assert!(!cmpr.contains_range(u8r, size1)); + assert!(!cmpr.contains_range(i8r, size1)); + + assert!(!boolr.contains_range(cmpr, size1)); + assert!(cmpr.contains_range(boolr, size1)); +} diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 97e07095875..984b280e81b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2849,7 +2849,7 @@ impl InlineAsmOperand { } } -#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] +#[derive(Clone, Copy, Encodable, Decodable, Debug, HashStable_Generic, Walkable, PartialEq, Eq)] pub enum AsmMacro { /// The `asm!` macro Asm, diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index c3222b79e55..2cc07694afb 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -95,7 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { // Let statements are allowed to have impl trait in bindings. - let super_ = l.super_; + let super_ = l.super_.map(|span| self.lower_span(span)); let ty = l.ty.as_ref().map(|t| { self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) }); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 15e736261d5..657792c9397 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -282,9 +282,11 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Field(el, ident) => { hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident)) } - ExprKind::Index(el, er, brackets_span) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span) - } + ExprKind::Index(el, er, brackets_span) => hir::ExprKind::Index( + self.lower_expr(el), + self.lower_expr(er), + self.lower_span(*brackets_span), + ), ExprKind::Range(e1, e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) } @@ -334,7 +336,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Struct(se) => { let rest = match &se.rest { StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), - StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp), + StructRest::Rest(sp) => { + hir::StructTailExpr::DefaultFields(self.lower_span(*sp)) + } StructRest::None => hir::StructTailExpr::None, }; hir::ExprKind::Struct( @@ -678,6 +682,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::Arm { hir_id, pat, guard, body, span } } + fn lower_capture_clause(&mut self, capture_clause: CaptureBy) -> CaptureBy { + match capture_clause { + CaptureBy::Ref => CaptureBy::Ref, + CaptureBy::Use { use_kw } => CaptureBy::Use { use_kw: self.lower_span(use_kw) }, + CaptureBy::Value { move_kw } => CaptureBy::Value { move_kw: self.lower_span(move_kw) }, + } + } + /// Lower/desugar a coroutine construct. /// /// In particular, this creates the correct async resume argument and `_task_context`. @@ -769,7 +781,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Closure(self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: hir::ClosureBinder::Default, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params: &[], fn_decl, body, @@ -1035,7 +1047,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { - hir::ExprKind::Use(self.lower_expr(expr), use_kw_span) + hir::ExprKind::Use(self.lower_expr(expr), self.lower_span(use_kw_span)) } fn lower_expr_closure( @@ -1083,7 +1095,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: binder_clause, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params, fn_decl, body: body_id, @@ -1197,7 +1209,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: binder_clause, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params, fn_decl, body, @@ -2101,7 +2113,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> { let lit = hir::Lit { - span: sp, + span: self.lower_span(sp), node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)), }; self.expr(sp, hir::ExprKind::Lit(lit)) @@ -2120,7 +2132,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { - let lit = hir::Lit { span: sp, node: ast::LitKind::Str(value, ast::StrStyle::Cooked) }; + let lit = hir::Lit { + span: self.lower_span(sp), + node: ast::LitKind::Str(value, ast::StrStyle::Cooked), + }; self.expr(sp, hir::ExprKind::Lit(lit)) } @@ -2206,7 +2221,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(hir::Path { span: self.lower_span(span), res, - segments: arena_vec![self; hir::PathSegment::new(ident, hir_id, res)], + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], }), )); diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 5b1dcab87b9..ec9d26eb33f 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -402,6 +402,8 @@ fn expand_format_args<'hir>( fmt: &FormatArgs, allow_const: bool, ) -> hir::ExprKind<'hir> { + let macsp = ctx.lower_span(macsp); + let mut incomplete_lit = String::new(); let lit_pieces = ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| { diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 1ef64f5a352..5b63206d7d6 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -164,11 +164,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_item(&mut self, i: &'hir Item<'hir>) { debug_assert_eq!(i.owner_id, self.owner); self.with_parent(i.hir_id(), |this| { - if let ItemKind::Struct(_, _, struct_def) = &i.kind { + if let ItemKind::Struct(_, _, struct_def) = &i.kind // If this is a tuple or unit-like struct, register the constructor. - if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { - this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); - } + && let Some(ctor_hir_id) = struct_def.ctor_hir_id() + { + this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); } intravisit::walk_item(this, i); }); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ddf01b69e7f..1899dcda361 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -627,6 +627,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { // For non-empty lists we can just drop all the data, the prefix is already // present in HIR as a part of nested imports. + let span = self.lower_span(span); self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span }) }; hir::ItemKind::Use(path, hir::UseKind::ListStem) @@ -1567,7 +1568,7 @@ impl<'hir> LoweringContext<'_, 'hir> { attrs: &[hir::Attribute], ) -> hir::FnHeader { let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind { - hir::IsAsync::Async(span) + hir::IsAsync::Async(self.lower_span(span)) } else { hir::IsAsync::NotAsync }; @@ -1804,7 +1805,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = Res::Def(DefKind::TyParam, def_id); let ident = self.lower_ident(ident); let ty_path = self.arena.alloc(hir::Path { - span: param_span, + span: self.lower_span(param_span), res, segments: self .arena diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9aef189a29d..189c82b614c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2368,7 +2368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, modifiers: TraitBoundModifiers, ) -> hir::TraitBoundModifiers { - hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity } + let constness = match modifiers.constness { + BoundConstness::Never => BoundConstness::Never, + BoundConstness::Always(span) => BoundConstness::Always(self.lower_span(span)), + BoundConstness::Maybe(span) => BoundConstness::Maybe(self.lower_span(span)), + }; + let polarity = match modifiers.polarity { + BoundPolarity::Positive => BoundPolarity::Positive, + BoundPolarity::Negative(span) => BoundPolarity::Negative(self.lower_span(span)), + BoundPolarity::Maybe(span) => BoundPolarity::Maybe(self.lower_span(span)), + }; + hir::TraitBoundModifiers { constness, polarity } } // Helper methods for building HIR. @@ -2414,6 +2424,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { init: Option<&'hir hir::Expr<'hir>>, ) -> hir::Stmt<'hir> { let hir_id = self.next_id(); + let span = self.lower_span(span); let local = hir::LetStmt { super_: Some(span), hir_id, @@ -2421,7 +2432,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pat, els: None, source: hir::LocalSource::Normal, - span: self.lower_span(span), + span, ty: None, }; self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local))) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a08dae11153..895a457ec1d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -640,16 +640,16 @@ impl<'a> AstValidator<'a> { return; } - if let Some(header) = fk.header() { - if let Const::Yes(const_span) = header.constness { - let mut spans = variadic_spans.clone(); - spans.push(const_span); - self.dcx().emit_err(errors::ConstAndCVariadic { - spans, - const_span, - variadic_spans: variadic_spans.clone(), - }); - } + if let Some(header) = fk.header() + && let Const::Yes(const_span) = header.constness + { + let mut spans = variadic_spans.clone(); + spans.push(const_span); + self.dcx().emit_err(errors::ConstAndCVariadic { + spans, + const_span, + variadic_spans: variadic_spans.clone(), + }); } match (fk.ctxt(), fk.header()) { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8114733f406..662357ce884 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,16 +630,11 @@ fn check_incompatible_features(sess: &Session, features: &Features) { .iter() .filter(|(f1, f2)| features.enabled(*f1) && features.enabled(*f2)) { - if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) { - if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) - { - let spans = vec![f1_span, f2_span]; - sess.dcx().emit_err(errors::IncompatibleFeatures { - spans, - f1: f1_name, - f2: f2_name, - }); - } + if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) + && let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) + { + let spans = vec![f1_span, f2_span]; + sess.dcx().emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name }); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index def0cb74d29..f0cf0c1487f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -572,10 +572,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) { - if let Some(cmnts) = self.comments_mut() { - if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) { - self.print_comment(cmnt); - } + if let Some(cmnts) = self.comments_mut() + && let Some(cmnt) = cmnts.trailing_comment(span, next_pos) + { + self.print_comment(cmnt); } } diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9f99b33adcc..55019cd57a7 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -110,18 +110,10 @@ pub enum DeprecatedSince { Err, } -#[derive( - Copy, - Debug, - Eq, - PartialEq, - Encodable, - Decodable, - Clone, - HashStable_Generic, - PrintAttribute -)] -pub enum CoverageStatus { +/// Successfully-parsed value of a `#[coverage(..)]` attribute. +#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum CoverageAttrKind { On, Off, } @@ -157,6 +149,19 @@ pub enum UsedBy { Linker, } +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MacroUseArgs { + UseAll, + UseSpecific(ThinVec<Ident>), +} + +impl Default for MacroUseArgs { + fn default() -> Self { + Self::UseSpecific(ThinVec::new()) + } +} + #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] pub struct StrippedCfgItem<ModId = DefId> { pub parent_module: ModId, @@ -291,8 +296,8 @@ pub enum AttributeKind { /// Represents `#[const_trait]`. ConstTrait(Span), - /// Represents `#[coverage]`. - Coverage(Span, CoverageStatus), + /// Represents `#[coverage(..)]`. + Coverage(Span, CoverageAttrKind), ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), @@ -351,9 +356,15 @@ pub enum AttributeKind { /// Represents `#[loop_match]`. LoopMatch(Span), + /// Represents `#[macro_escape]`. + MacroEscape(Span), + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), + /// Represents `#[macro_use]`. + MacroUse { span: Span, arguments: MacroUseArgs }, + /// Represents `#[marker]`. Marker(Span), @@ -397,12 +408,24 @@ pub enum AttributeKind { /// Represents `#[pointee]` Pointee(Span), + /// Represents `#[proc_macro]` + ProcMacro(Span), + + /// Represents `#[proc_macro_attribute]` + ProcMacroAttribute(Span), + + /// Represents `#[proc_macro_derive]` + ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span }, + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). PubTransparent(Span), /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, + /// Represents `#[rustc_builtin_macro]`. + RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span }, + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box<u128>, Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 86d9ddba4d2..af2d46d0bc7 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -45,7 +45,9 @@ impl AttributeKind { LinkOrdinal { .. } => No, LinkSection { .. } => Yes, // Needed for rustdoc LoopMatch(..) => No, + MacroEscape(..) => No, MacroTransparency(..) => Yes, + MacroUse { .. } => No, Marker(..) => No, MayDangle(..) => No, MustUse { .. } => Yes, @@ -59,8 +61,12 @@ impl AttributeKind { PassByValue(..) => Yes, Path(..) => No, Pointee(..) => No, + ProcMacro(..) => No, + ProcMacroAttribute(..) => No, + ProcMacroDerive { .. } => No, PubTransparent(..) => Yes, Repr { .. } => No, + RustcBuiltinMacro { .. } => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index ecca0e39063..4c5af805ca9 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; use rustc_span::hygiene::Transparency; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use stability::*; use thin_vec::ThinVec; pub use version::*; @@ -172,7 +172,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); print_disp!(u16, bool, NonZero<u32>); -print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); +print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); /// Finds attributes in sequences of attributes by pattern matching. /// diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index bb28121c2c5..afa9abed0bb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy}; +use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -78,16 +78,16 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser { return None; }; - let status = match arg.path().word_sym() { - Some(sym::off) => CoverageStatus::Off, - Some(sym::on) => CoverageStatus::On, + let kind = match arg.path().word_sym() { + Some(sym::off) => CoverageAttrKind::Off, + Some(sym::on) => CoverageAttrKind::On, None | Some(_) => { fail_incorrect_argument(arg.span()); return None; } }; - Some(AttributeKind::Coverage(cx.attr_span, status)) + Some(AttributeKind::Coverage(cx.attr_span, kind)) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs new file mode 100644 index 00000000000..eade49180ac --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -0,0 +1,115 @@ +use rustc_attr_data_structures::{AttributeKind, MacroUseArgs}; +use rustc_errors::DiagArgValue; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; + +use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; +use crate::parser::ArgParser; +use crate::session_diagnostics; + +pub(crate) struct MacroEscapeParser; +impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser { + const PATH: &[Symbol] = &[sym::macro_escape]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape; +} + +/// `#[macro_use]` attributes can either: +/// - Use all macros from a crate, if provided without arguments +/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]` +/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used. +#[derive(Default)] +pub(crate) struct MacroUseParser { + state: MacroUseArgs, + + /// Spans of all `#[macro_use]` arguments with arguments, used for linting + uses_attr_spans: ThinVec<Span>, + /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute + /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments + first_span: Option<Span>, +} + +const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ..."); + +impl<S: Stage> AttributeParser<S> for MacroUseParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + &[sym::macro_use], + MACRO_USE_TEMPLATE, + |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| { + let span = cx.attr_span; + group.first_span.get_or_insert(span); + match args { + ArgParser::NoArgs => { + match group.state { + MacroUseArgs::UseAll => { + let first_span = group.first_span.expect( + "State is UseAll is some so this is not the first attribute", + ); + // Since there is a `#[macro_use]` import already, give a warning + cx.warn_unused_duplicate(first_span, span); + } + MacroUseArgs::UseSpecific(_) => { + group.state = MacroUseArgs::UseAll; + group.first_span = Some(span); + // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported + for specific_use in group.uses_attr_spans.drain(..) { + cx.warn_unused_duplicate(span, specific_use); + } + } + } + } + ArgParser::List(list) => { + if list.is_empty() { + cx.warn_empty_attribute(list.span); + return; + } + + match &mut group.state { + MacroUseArgs::UseAll => { + let first_span = group.first_span.expect( + "State is UseAll is some so this is not the first attribute", + ); + cx.warn_unused_duplicate(first_span, span); + } + MacroUseArgs::UseSpecific(arguments) => { + // Store here so if we encounter a `UseAll` later we can still lint this attribute + group.uses_attr_spans.push(cx.attr_span); + + for item in list.mixed() { + let Some(item) = item.meta_item() else { + cx.expected_identifier(item.span()); + continue; + }; + if let Err(err_span) = item.args().no_args() { + cx.expected_no_args(err_span); + continue; + } + let Some(item) = item.path().word() else { + cx.expected_identifier(item.span()); + continue; + }; + arguments.push(item); + } + } + } + } + ArgParser::NameValue(_) => { + let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use); + cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + } + } + }, + )]; + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { + Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 200f1381960..0c10517d044 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -36,10 +36,12 @@ pub(crate) mod inline; pub(crate) mod link_attrs; pub(crate) mod lint_helpers; pub(crate) mod loop_match; +pub(crate) mod macro_attrs; pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; +pub(crate) mod proc_macro_attrs; pub(crate) mod repr; pub(crate) mod rustc_internal; pub(crate) mod semantics; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index e0a3e675509..42af3ed0bfa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser { ArgParser::List(_) => { let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use"); - cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput { + cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs new file mode 100644 index 00000000000..4de77dc268e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -0,0 +1,139 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; + +use crate::attributes::{ + AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct ProcMacroParser; +impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser { + const PATH: &[Symbol] = &[sym::proc_macro]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro; +} + +pub(crate) struct ProcMacroAttributeParser; +impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroAttributeParser { + const PATH: &[Symbol] = &[sym::proc_macro_attribute]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute; +} + +pub(crate) struct ProcMacroDeriveParser; +impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser { + const PATH: &[Symbol] = &[sym::proc_macro_derive]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = + template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?; + Some(AttributeKind::ProcMacroDerive { + trait_name: trait_name.expect("Trait name is mandatory, so it is present"), + helper_attrs, + span: cx.attr_span, + }) + } +} + +pub(crate) struct RustcBuiltinMacroParser; +impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser { + const PATH: &[Symbol] = &[sym::rustc_builtin_macro]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = + template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?; + Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span }) + } +} + +fn parse_derive_like<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, + trait_name_mandatory: bool, +) -> Option<(Option<Symbol>, ThinVec<Symbol>)> { + let Some(list) = args.list() else { + // For #[rustc_builtin_macro], it is permitted to leave out the trait name + if args.no_args().is_ok() && !trait_name_mandatory { + return Some((None, ThinVec::new())); + } + cx.expected_list(cx.attr_span); + return None; + }; + let mut items = list.mixed(); + + // Parse the name of the trait that is derived. + let Some(trait_attr) = items.next() else { + cx.expected_at_least_one_argument(list.span); + return None; + }; + let Some(trait_attr) = trait_attr.meta_item() else { + cx.unexpected_literal(trait_attr.span()); + return None; + }; + let Some(trait_ident) = trait_attr.path().word() else { + cx.expected_identifier(trait_attr.path().span()); + return None; + }; + if !trait_ident.name.can_be_raw() { + cx.expected_identifier(trait_ident.span); + return None; + } + if let Err(e) = trait_attr.args().no_args() { + cx.expected_no_args(e); + return None; + }; + + // Parse optional attributes + let mut attributes = ThinVec::new(); + if let Some(attrs) = items.next() { + let Some(attr_list) = attrs.meta_item() else { + cx.expected_list(attrs.span()); + return None; + }; + if !attr_list.path().word_is(sym::attributes) { + cx.expected_specific_argument(attrs.span(), vec!["attributes"]); + return None; + } + let Some(attr_list) = attr_list.args().list() else { + cx.expected_list(attrs.span()); + return None; + }; + + // Parse item in `attributes(...)` argument + for attr in attr_list.mixed() { + let Some(attr) = attr.meta_item() else { + cx.expected_identifier(attr.span()); + return None; + }; + if let Err(e) = attr.args().no_args() { + cx.expected_no_args(e); + return None; + }; + let Some(ident) = attr.path().word() else { + cx.expected_identifier(attr.path().span()); + return None; + }; + if !ident.name.can_be_raw() { + cx.expected_identifier(ident.span); + return None; + } + attributes.push(ident.name); + } + } + + // If anything else is specified, we should reject it + if let Some(next) = items.next() { + cx.expected_no_args(next.span()); + } + + Some((Some(trait_ident.name), attributes)) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 59337749c87..c54fc6b41f8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -74,8 +74,15 @@ impl<S: Stage> AttributeParser<S> for StabilityParser { template!(NameValueStr: "deprecation message"), |this, cx, args| { reject_outside_std!(cx); - this.allowed_through_unstable_modules = - args.name_value().and_then(|i| i.value_as_str()) + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return; + }; + let Some(value_str) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return; + }; + this.allowed_through_unstable_modules = Some(value_str); }, ), ]; @@ -247,7 +254,12 @@ pub(crate) fn parse_stability<S: Stage>( let mut feature = None; let mut since = None; - for param in args.list()?.mixed() { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span); + return None; + }; + + for param in list.mixed() { let param_span = param.span(); let Some(param) = param.meta_item() else { cx.emit_err(session_diagnostics::UnsupportedLiteral { @@ -322,7 +334,13 @@ pub(crate) fn parse_unstability<S: Stage>( let mut is_soft = false; let mut implied_by = None; let mut old_name = None; - for param in args.list()?.mixed() { + + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span); + return None; + }; + + for param in list.mixed() { let Some(param) = param.meta_item() else { cx.emit_err(session_diagnostics::UnsupportedLiteral { span: param.span(), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4d692d9562c..9b86d101840 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -33,10 +33,14 @@ use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; +use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; +use crate::attributes::proc_macro_attrs::{ + ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, +}; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, @@ -126,6 +130,7 @@ attribute_parsers!( BodyStabilityParser, ConfusablesParser, ConstStabilityParser, + MacroUseParser, NakedParser, StabilityParser, UsedParser, @@ -152,6 +157,8 @@ attribute_parsers!( Single<MustUseParser>, Single<OptimizeParser>, Single<PathAttributeParser>, + Single<ProcMacroDeriveParser>, + Single<RustcBuiltinMacroParser>, Single<RustcForceInlineParser>, Single<RustcLayoutScalarValidRangeEnd>, Single<RustcLayoutScalarValidRangeStart>, @@ -174,6 +181,7 @@ attribute_parsers!( Single<WithoutArgs<FfiPureParser>>, Single<WithoutArgs<FundamentalParser>>, Single<WithoutArgs<LoopMatchParser>>, + Single<WithoutArgs<MacroEscapeParser>>, Single<WithoutArgs<MarkerParser>>, Single<WithoutArgs<MayDangleParser>>, Single<WithoutArgs<NoImplicitPreludeParser>>, @@ -183,6 +191,8 @@ attribute_parsers!( Single<WithoutArgs<ParenSugarParser>>, Single<WithoutArgs<PassByValueParser>>, Single<WithoutArgs<PointeeParser>>, + Single<WithoutArgs<ProcMacroAttributeParser>>, + Single<WithoutArgs<ProcMacroParser>>, Single<WithoutArgs<PubTransparentParser>>, Single<WithoutArgs<SpecializationTraitParser>>, Single<WithoutArgs<StdInternalSymbolParser>>, @@ -386,6 +396,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } + /// emit an error that a `name` was expected here + pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedIdentifier, + }) + } + /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9a400e0fe10..1de25ca252b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput { #[derive(Diagnostic)] #[diag(attr_parsing_ill_formed_attribute_input)] -pub(crate) struct MustUseIllFormedAttributeInput { +pub(crate) struct IllFormedAttributeInputLint { #[primary_span] pub span: Span, pub num_suggestions: usize, @@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason { /// Should we tell the user to write a list when they didn't? list: bool, }, + ExpectedIdentifier, } pub(crate) struct AttributeParseError { @@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.code(E0538); } AttributeParseErrorReason::UnexpectedLiteral => { - diag.span_label(self.span, format!("didn't expect a literal here")); + diag.span_label(self.span, "didn't expect a literal here"); diag.code(E0565); } AttributeParseErrorReason::ExpectedNoArgs => { - diag.span_label(self.span, format!("didn't expect any arguments here")); + diag.span_label(self.span, "didn't expect any arguments here"); diag.code(E0565); } AttributeParseErrorReason::ExpectedNameValue(None) => { @@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { } } } + AttributeParseErrorReason::ExpectedIdentifier => { + diag.span_label(self.span, "expected a valid identifier here"); + } } let suggestions = self.template.suggestions(false, &name); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 040a0607db5..be8b3f0bc1e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1290,6 +1290,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { span, format!("if `{ty}` implemented `Clone`, you could clone the value"), ); + } else if let ty::Adt(_, _) = ty.kind() + && let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() + { + // For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point + // at the types that should be `Clone`. + let ocx = ObligationCtxt::new_with_diagnostics(self.infcx); + let cause = ObligationCause::misc(expr.span, self.mir_def_id()); + ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); + let errors = ocx.select_all_or_error(); + if errors.iter().all(|error| { + match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { + Some(clause) => match clause.self_ty().skip_binder().kind() { + ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait, + _ => false, + }, + None => false, + } + }) { + let mut type_spans = vec![]; + let mut types = FxIndexSet::default(); + for clause in errors + .iter() + .filter_map(|e| e.obligation.predicate.as_clause()) + .filter_map(|c| c.as_trait_clause()) + { + let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue }; + type_spans.push(self.infcx.tcx.def_span(def.did())); + types.insert( + self.infcx + .tcx + .short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()), + ); + } + let mut span: MultiSpan = type_spans.clone().into(); + for sp in type_spans { + span.push_span_label(sp, "consider implementing `Clone` for this type"); + } + span.push_span_label(expr.span, "you could clone this value"); + let types: Vec<_> = types.into_iter().collect(); + let msg = match &types[..] { + [only] => format!("`{only}`"), + [head @ .., last] => format!( + "{} and `{last}`", + head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ") + ), + [] => unreachable!(), + }; + err.span_note( + span, + format!("if {msg} implemented `Clone`, you could clone the value"), + ); + } } } @@ -2332,7 +2384,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(body_expr) = finder.body_expr && let Some(loop_span) = finder.loop_span && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) - && let Some(trait_did) = tcx.trait_of_item(def_id) + && let Some(trait_did) = tcx.trait_of_assoc(def_id) && tcx.is_diagnostic_item(sym::Iterator, trait_did) { if let Some(loop_bind) = finder.loop_bind { @@ -2481,13 +2533,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Check that the parent of the closure is a method call, // with receiver matching with local's type (modulo refs) - if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) { - if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind { - let recv_ty = typeck_results.expr_ty(recv); + if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) + && let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind + { + let recv_ty = typeck_results.expr_ty(recv); - if recv_ty.peel_refs() != local_ty { - return; - } + if recv_ty.peel_refs() != local_ty { + return; } } @@ -2753,16 +2805,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // With the place of a union and a field access into it, we traverse the second // borrowed place and look for an access to a different field of the same union. for (place_base, elem) in second_borrowed_place.iter_projections().rev() { - if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(place_base) { - if field != target_field && place_base == target_base { - return Some(( - self.describe_any_place(place_base), - self.describe_any_place(first_borrowed_place.as_ref()), - self.describe_any_place(second_borrowed_place.as_ref()), - union_ty.to_string(), - )); - } + if let ProjectionElem::Field(field, _) = elem + && let Some(union_ty) = union_ty(place_base) + { + if field != target_field && place_base == target_base { + return Some(( + self.describe_any_place(place_base), + self.describe_any_place(first_borrowed_place.as_ref()), + self.describe_any_place(second_borrowed_place.as_ref()), + union_ty.to_string(), + )); } } } @@ -2949,16 +3001,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { from_closure: false, .. } = explanation - { - if let Err(diag) = self.try_report_cannot_return_reference_to_local( + && let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, borrow_span, span, category, opt_place_desc.as_ref(), - ) { - return diag; - } + ) + { + return diag; } let name = format!("`{name}`"); @@ -3720,30 +3771,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let loan_span = loan_spans.args_or_use(); let descr_place = self.describe_any_place(place.as_ref()); - if let BorrowKind::Fake(_) = loan.kind { - if let Some(section) = self.classify_immutable_section(loan.assigned_place) { - let mut err = self.cannot_mutate_in_immutable_section( - span, - loan_span, - &descr_place, - section, - "assign", - ); + if let BorrowKind::Fake(_) = loan.kind + && let Some(section) = self.classify_immutable_section(loan.assigned_place) + { + let mut err = self.cannot_mutate_in_immutable_section( + span, + loan_span, + &descr_place, + section, + "assign", + ); - loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - match kind { - hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, - hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - BorrowUseInClosure { var_span } - } + loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + match kind { + hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, + hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { + BorrowUseInClosure { var_span } } - }); + } + }); - self.buffer_error(err); + self.buffer_error(err); - return; - } + return; } let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); @@ -3996,119 +4047,116 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", target, stmt ); - if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if let Some(assigned_to) = place.as_local() { - debug!( - "annotate_argument_and_return_for_borrow: assigned_to={:?} \ + if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(assigned_to) = place.as_local() + { + debug!( + "annotate_argument_and_return_for_borrow: assigned_to={:?} \ rvalue={:?}", - assigned_to, rvalue - ); - // Check if our `target` was captured by a closure. - if let Rvalue::Aggregate( - box AggregateKind::Closure(def_id, args), - operands, - ) = rvalue - { - let def_id = def_id.expect_local(); - for operand in operands { - let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = - operand - else { - continue; - }; - debug!( - "annotate_argument_and_return_for_borrow: assigned_from={:?}", - assigned_from - ); + assigned_to, rvalue + ); + // Check if our `target` was captured by a closure. + if let Rvalue::Aggregate(box AggregateKind::Closure(def_id, args), operands) = + rvalue + { + let def_id = def_id.expect_local(); + for operand in operands { + let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = + operand + else { + continue; + }; + debug!( + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from + ); - // Find the local from the operand. - let Some(assigned_from_local) = - assigned_from.local_or_deref_local() - else { - continue; - }; + // Find the local from the operand. + let Some(assigned_from_local) = assigned_from.local_or_deref_local() + else { + continue; + }; - if assigned_from_local != target { - continue; - } + if assigned_from_local != target { + continue; + } - // If a closure captured our `target` and then assigned - // into a place then we should annotate the closure in - // case it ends up being assigned into the return place. - annotated_closure = - self.annotate_fn_sig(def_id, args.as_closure().sig()); - debug!( - "annotate_argument_and_return_for_borrow: \ + // If a closure captured our `target` and then assigned + // into a place then we should annotate the closure in + // case it ends up being assigned into the return place. + annotated_closure = + self.annotate_fn_sig(def_id, args.as_closure().sig()); + debug!( + "annotate_argument_and_return_for_borrow: \ annotated_closure={:?} assigned_from_local={:?} \ assigned_to={:?}", - annotated_closure, assigned_from_local, assigned_to - ); + annotated_closure, assigned_from_local, assigned_to + ); - if assigned_to == mir::RETURN_PLACE { - // If it was assigned directly into the return place, then - // return now. - return annotated_closure; - } else { - // Otherwise, update the target. - target = assigned_to; - } + if assigned_to == mir::RETURN_PLACE { + // If it was assigned directly into the return place, then + // return now. + return annotated_closure; + } else { + // Otherwise, update the target. + target = assigned_to; } - - // If none of our closure's operands matched, then skip to the next - // statement. - continue; } - // Otherwise, look at other types of assignment. - let assigned_from = match rvalue { - Rvalue::Ref(_, _, assigned_from) => assigned_from, - Rvalue::Use(operand) => match operand { - Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { - assigned_from - } - _ => continue, - }, + // If none of our closure's operands matched, then skip to the next + // statement. + continue; + } + + // Otherwise, look at other types of assignment. + let assigned_from = match rvalue { + Rvalue::Ref(_, _, assigned_from) => assigned_from, + Rvalue::Use(operand) => match operand { + Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { + assigned_from + } _ => continue, - }; - debug!( - "annotate_argument_and_return_for_borrow: \ + }, + _ => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: \ assigned_from={:?}", - assigned_from, - ); + assigned_from, + ); - // Find the local from the rvalue. - let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { - continue; - }; - debug!( - "annotate_argument_and_return_for_borrow: \ + // Find the local from the rvalue. + let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { + continue; + }; + debug!( + "annotate_argument_and_return_for_borrow: \ assigned_from_local={:?}", - assigned_from_local, - ); + assigned_from_local, + ); - // Check if our local matches the target - if so, we've assigned our - // borrow to a new place. - if assigned_from_local != target { - continue; - } + // Check if our local matches the target - if so, we've assigned our + // borrow to a new place. + if assigned_from_local != target { + continue; + } - // If we assigned our `target` into a new place, then we should - // check if it was the return place. - debug!( - "annotate_argument_and_return_for_borrow: \ + // If we assigned our `target` into a new place, then we should + // check if it was the return place. + debug!( + "annotate_argument_and_return_for_borrow: \ assigned_from_local={:?} assigned_to={:?}", - assigned_from_local, assigned_to - ); - if assigned_to == mir::RETURN_PLACE { - // If it was then return the annotated closure if there was one, - // else, annotate this function. - return annotated_closure.or_else(fallback); - } - - // If we didn't assign into the return place, then we just update - // the target. - target = assigned_to; + assigned_from_local, assigned_to + ); + if assigned_to == mir::RETURN_PLACE { + // If it was then return the annotated closure if there was one, + // else, annotate this function. + return annotated_closure.or_else(fallback); } + + // If we didn't assign into the return place, then we just update + // the target. + target = assigned_to; } } @@ -4120,32 +4168,31 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); if let TerminatorKind::Call { destination, target: Some(_), args, .. } = &terminator.kind + && let Some(assigned_to) = destination.as_local() { - if let Some(assigned_to) = destination.as_local() { + debug!( + "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", + assigned_to, args + ); + for operand in args { + let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = + &operand.node + else { + continue; + }; debug!( - "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", - assigned_to, args + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from, ); - for operand in args { - let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = - &operand.node - else { - continue; - }; + + if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { debug!( - "annotate_argument_and_return_for_borrow: assigned_from={:?}", - assigned_from, + "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", + assigned_from_local, ); - if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { - debug!( - "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", - assigned_from_local, - ); - - if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { - return annotated_closure.or_else(fallback); - } + if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { + return annotated_closure.or_else(fallback); } } } @@ -4244,10 +4291,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // as the HIR doesn't have full types for closure arguments. let return_ty = sig.output().skip_binder(); let mut return_span = fn_decl.output.span(); - if let hir::FnRetTy::Return(ty) = &fn_decl.output { - if let hir::TyKind::Ref(lifetime, _) = ty.kind { - return_span = lifetime.ident.span; - } + if let hir::FnRetTy::Return(ty) = &fn_decl.output + && let hir::TyKind::Ref(lifetime, _) = ty.kind + { + return_span = lifetime.ident.span; } Some(AnnotatedBorrowFnSignature::NamedFunction { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f9e52239d6f..fdca6b56540 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -341,7 +341,7 @@ impl<'tcx> BorrowExplanation<'tcx> { } } } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { - let sp = info.span.find_oldest_ancestor_in_same_ctxt(); + let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span); if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. @@ -917,30 +917,29 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { if let TerminatorKind::Call { destination, target: Some(block), args, .. } = &terminator.kind + && let Some(dest) = destination.as_local() { - if let Some(dest) = destination.as_local() { - debug!( - "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", - target, dest, args - ); - // Check if one of the arguments to this function is the target place. - let found_target = args.iter().any(|arg| { - if let Operand::Move(place) = arg.node { - if let Some(potential) = place.as_local() { - potential == target - } else { - false - } + debug!( + "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", + target, dest, args + ); + // Check if one of the arguments to this function is the target place. + let found_target = args.iter().any(|arg| { + if let Operand::Move(place) = arg.node { + if let Some(potential) = place.as_local() { + potential == target } else { false } - }); - - // If it is, follow this to the next block and update the target. - if found_target { - target = dest; - queue.push(block.start_location()); + } else { + false } + }); + + // If it is, follow this to the next block and update the target. + if found_target { + target = dest; + queue.push(block.start_location()); } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 9ad91d605a7..56fdaf1c724 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -266,48 +266,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { args, .. } = &terminator.kind + && let ty::FnDef(id, _) = *const_.ty().kind() { - if let ty::FnDef(id, _) = *const_.ty().kind() { - debug!("add_moved_or_invoked_closure_note: id={:?}", id); - if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) { - let closure = match args.first() { - Some(Spanned { - node: Operand::Copy(place) | Operand::Move(place), .. - }) if target == place.local_or_deref_local() => { - place.local_or_deref_local().unwrap() - } - _ => return false, - }; + debug!("add_moved_or_invoked_closure_note: id={:?}", id); + if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) { + let closure = match args.first() { + Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. }) + if target == place.local_or_deref_local() => + { + place.local_or_deref_local().unwrap() + } + _ => return false, + }; - debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); - if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { - let did = did.expect_local(); - if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic(OnClosureNote::InvokedTwice { - place_name: &ty::place_to_string_for_capture( - self.infcx.tcx, - hir_place, - ), - span: *span, - }); - return true; - } + debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); + if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { + let did = did.expect_local(); + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { + diag.subdiagnostic(OnClosureNote::InvokedTwice { + place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), + span: *span, + }); + return true; } } } } // Check if we are just moving a closure after it has been invoked. - if let Some(target) = target { - if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { - let did = did.expect_local(); - if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic(OnClosureNote::MovedTwice { - place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), - span: *span, - }); - return true; - } + if let Some(target) = target + && let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() + { + let did = did.expect_local(); + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { + diag.subdiagnostic(OnClosureNote::MovedTwice { + place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), + span: *span, + }); + return true; } } false @@ -942,7 +938,7 @@ impl<'tcx> BorrowedContentSource<'tcx> { fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> { match *func.kind() { ty::FnDef(def_id, args) => { - let trait_id = tcx.trait_of_item(def_id)?; + let trait_id = tcx.trait_of_assoc(def_id)?; if tcx.is_lang_item(trait_id, LangItem::Deref) || tcx.is_lang_item(trait_id, LangItem::DerefMut) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 447bf7d091a..1067f1e40ef 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -115,10 +115,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec<GroupedMoveError<'tcx>>, - error: MoveError<'tcx>, + MoveError { place: original_path, location, kind }: MoveError<'tcx>, ) { - let MoveError { place: original_path, location, kind } = error; - // Note: that the only time we assign a place isn't a temporary // to a user variable is when initializing it. // If that ever stops being the case, then the ever initialized @@ -128,36 +126,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .statements .get(location.statement_index) .map(|stmt| &stmt.kind) + && let Some(local) = place.as_local() { - if let Some(local) = place.as_local() { - let local_decl = &self.body.local_decls[local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - })) = *local_decl.local_info() - { - let stmt_source_info = self.body.source_info(location); - self.append_binding_error( - grouped_errors, - kind, - original_path, - *move_from, - local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; - } + let local_decl = &self.body.local_decls[local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() + { + let stmt_source_info = self.body.source_info(location); + self.append_binding_error( + grouped_errors, + kind, + original_path, + *move_from, + local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; } } @@ -251,54 +248,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } fn report(&mut self, error: GroupedMoveError<'tcx>) { - let (mut err, err_span) = { - let (span, use_spans, original_path, kind) = match error { - GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. } - | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => { - (span, None, original_path, kind) - } - GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => { - (use_spans.args_or_use(), Some(use_spans), original_path, kind) - } - }; - debug!( - "report: original_path={:?} span={:?}, kind={:?} \ - original_path.is_upvar_field_projection={:?}", - original_path, - span, - kind, - self.is_upvar_field_projection(original_path.as_ref()) - ); - if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) { - // If the type may implement Copy, skip the error. - // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check - self.dcx().span_delayed_bug( + let (span, use_spans, original_path, kind) = match error { + GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. } + | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => { + (span, None, original_path, kind) + } + GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => { + (use_spans.args_or_use(), Some(use_spans), original_path, kind) + } + }; + debug!( + "report: original_path={:?} span={:?}, kind={:?} \ + original_path.is_upvar_field_projection={:?}", + original_path, + span, + kind, + self.is_upvar_field_projection(original_path.as_ref()) + ); + if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) { + // If the type may implement Copy, skip the error. + // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check + self.dcx() + .span_delayed_bug(span, "Type may implement copy, but there is no other error."); + return; + } + let mut err = match kind { + &IllegalMoveOriginKind::BorrowedContent { target_place } => self + .report_cannot_move_from_borrowed_content( + original_path, + target_place, span, - "Type may implement copy, but there is no other error.", - ); - return; + use_spans, + ), + &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + self.cannot_move_out_of_interior_of_drop(span, ty) + } + &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index)) } - ( - match kind { - &IllegalMoveOriginKind::BorrowedContent { target_place } => self - .report_cannot_move_from_borrowed_content( - original_path, - target_place, - span, - use_spans, - ), - &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { - self.cannot_move_out_of_interior_of_drop(span, ty) - } - &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { - self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index)) - } - }, - span, - ) }; - self.add_move_hints(error, &mut err, err_span); + self.add_move_hints(error, &mut err, span); self.buffer_error(err); } @@ -483,7 +473,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.cannot_move_out_of_interior_noncopy(span, ty, None) } ty::Closure(def_id, closure_args) - if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() => + if def_id.as_local() == Some(self.mir_def_id()) + && let Some(upvar_field) = upvar_field => { let closure_kind_ty = closure_args.as_closure().kind_ty(); let closure_kind = match closure_kind_ty.to_opt_closure_kind() { @@ -496,7 +487,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let capture_description = format!("captured variable in an `{closure_kind}` closure"); - let upvar = &self.upvars[upvar_field.unwrap().index()]; + let upvar = &self.upvars[upvar_field.index()]; let upvar_hir_id = upvar.get_root_variable(); let upvar_name = upvar.to_string(tcx); let upvar_span = tcx.hir_span(upvar_hir_id); @@ -606,7 +597,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { - let use_span = use_spans.var_or_use(); + let mut use_span = use_spans.var_or_use(); let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { Some(desc) => format!("`{desc}`"), @@ -623,6 +614,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } + if let Some(upvar_field) = self + .prefixes(original_path.as_ref(), PrefixSet::All) + .find_map(|p| self.is_upvar_field_projection(p)) + { + // Look for the introduction of the original binding being moved. + let upvar = &self.upvars[upvar_field.index()]; + let upvar_hir_id = upvar.get_root_variable(); + use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) { + hir::Node::Param(param) => { + // Instead of pointing at the path where we access the value within a + // closure, we point at the type of the outer `fn` argument. + param.ty_span + } + hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) { + // We point at the type of the outer let-binding. + (Some(ty), _) => ty.span, + // We point at the initializer of the outer let-binding, but only if it + // isn't something that spans multiple lines, like a closure, as the + // ASCII art gets messy. + (None, Some(init)) + if !self.infcx.tcx.sess.source_map().is_multiline(init.span) => + { + init.span + } + _ => use_span, + }, + _ => use_span, + }; + } + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, ty: place_ty, @@ -630,12 +651,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { span: use_span, }); + let mut pointed_at_span = false; use_spans.args_subdiag(err, |args_span| { + if args_span == span || args_span == use_span { + pointed_at_span = true; + } crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { - place: place_desc, + place: place_desc.clone(), args_span, } }); + if !pointed_at_span && use_span != span { + err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { + place: place_desc, + args_span: span, + }); + } self.add_note_for_packed_struct_derive(err, original_path.local); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a06540f8325..5d9416b59fc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -682,7 +682,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let my_def = self.body.source.def_id(); let Some(td) = - self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) + self.infcx.tcx.impl_of_assoc(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) else { return (false, false, None); }; @@ -880,7 +880,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let opt_suggestions = tcx .typeck(path_segment.hir_id.owner.def_id) .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| tcx.impl_of_method(def_id)) + .and_then(|def_id| tcx.impl_of_assoc(def_id)) .map(|def_id| tcx.associated_items(def_id)) .map(|assoc_items| { assoc_items @@ -1056,7 +1056,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .tcx .typeck(path_segment.hir_id.owner.def_id) .type_dependent_def_id(cur_expr.hir_id) - .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) + .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id)) .map(|def_id| self.infcx.tcx.associated_items(def_id)) .map(|assoc_items| { assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable() diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index edd14d155f6..517f9e88cd9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -528,15 +528,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // match_adt_and_segment in this case. Res::Def(DefKind::TyAlias, _) => (), _ => { - if let Some(last_segment) = path.segments.last() { - if let Some(highlight) = self.match_adt_and_segment( + if let Some(last_segment) = path.segments.last() + && let Some(highlight) = self.match_adt_and_segment( args, needle_fr, last_segment, search_stack, - ) { - return Some(highlight); - } + ) + { + return Some(highlight); } } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5f1b655c6b6..68f1637e07e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -822,10 +822,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { - if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) { - continue; - } + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements + && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) + { + continue; } // Type-test failed. Report the error. @@ -1479,40 +1479,36 @@ impl<'tcx> RegionInferenceContext<'tcx> { shorter_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>, ) -> RegionRelationCheckResult { - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements // Shrink `longer_fr` until we find a non-local region (if we do). // We'll call it `fr-` -- it's ever so slightly smaller than // `longer_fr`. - if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) - { - debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); + && let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) + { + debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); - let blame_span_category = self.find_outlives_blame_span( - longer_fr, - NllRegionVariableOrigin::FreeRegion, - shorter_fr, - ); + let blame_span_category = self.find_outlives_blame_span( + longer_fr, + NllRegionVariableOrigin::FreeRegion, + shorter_fr, + ); - // Grow `shorter_fr` until we find some non-local regions. (We - // always will.) We'll call them `shorter_fr+` -- they're ever - // so slightly larger than `shorter_fr`. - let shorter_fr_plus = - self.universal_region_relations.non_local_upper_bounds(shorter_fr); - debug!( - "try_propagate_universal_region_error: shorter_fr_plus={:?}", - shorter_fr_plus - ); - for fr in shorter_fr_plus { - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: fr, - blame_span: blame_span_category.1.span, - category: blame_span_category.0, - }); - } - return RegionRelationCheckResult::Propagated; + // Grow `shorter_fr` until we find some non-local regions. (We + // always will.) We'll call them `shorter_fr+` -- they're ever + // so slightly larger than `shorter_fr`. + let shorter_fr_plus = + self.universal_region_relations.non_local_upper_bounds(shorter_fr); + debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus); + for fr in shorter_fr_plus { + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: fr, + blame_span: blame_span_category.1.span, + category: blame_span_category.0, + }); } + return RegionRelationCheckResult::Propagated; } RegionRelationCheckResult::Error @@ -2085,11 +2081,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { let locations = self.scc_values.locations_outlived_by(scc); for location in locations { let bb = &body[location.block]; - if let Some(terminator) = &bb.terminator { + if let Some(terminator) = &bb.terminator // terminator of a loop should be TerminatorKind::FalseUnwind - if let TerminatorKind::FalseUnwind { .. } = terminator.kind { - return Some(location); - } + && let TerminatorKind::FalseUnwind { .. } = terminator.kind + { + return Some(location); } } None diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d500088c259..f5fedbf95c1 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -669,24 +669,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } - if let Some(annotation_index) = self.rvalue_user_ty(rv) { - if let Err(terr) = self.relate_type_and_user_type( + if let Some(annotation_index) = self.rvalue_user_ty(rv) + && let Err(terr) = self.relate_type_and_user_type( rv_ty, ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), - ) { - let annotation = &self.user_type_annotations[annotation_index]; - span_mirbug!( - self, - stmt, - "bad user type on rvalue ({:?} = {:?}): {:?}", - annotation, - rv_ty, - terr - ); - } + ) + { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + stmt, + "bad user type on rvalue ({:?} = {:?}): {:?}", + annotation, + rv_ty, + terr + ); } if !self.unsized_feature_enabled() { @@ -1761,7 +1761,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); assert!(!matches!( - tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), + tcx.impl_of_assoc(def_id).map(|imp| tcx.def_kind(imp)), Some(DefKind::Impl { of_trait: true }) )); self.prove_predicates( diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index f138f265320..240c9a5223b 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -969,13 +969,28 @@ fn for_each_late_bound_region_in_item<'tcx>( mir_def_id: LocalDefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if !tcx.def_kind(mir_def_id).is_fn_like() { - return; - } + let bound_vars = match tcx.def_kind(mir_def_id) { + DefKind::Fn | DefKind::AssocFn => { + tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) + } + // We extract the bound vars from the deduced closure signature, since we may have + // only deduced that a param in the closure signature is late-bound from a constraint + // that we discover during typeck. + DefKind::Closure => { + let ty = tcx.type_of(mir_def_id).instantiate_identity(); + match *ty.kind() { + ty::Closure(_, args) => args.as_closure().sig().bound_vars(), + ty::CoroutineClosure(_, args) => { + args.as_coroutine_closure().coroutine_closure_sig().bound_vars() + } + ty::Coroutine(_, _) | ty::Error(_) => return, + _ => unreachable!("unexpected type for closure: {ty}"), + } + } + _ => return, + }; - for (idx, bound_var) in - tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate() - { + for (idx, bound_var) in bound_vars.iter().enumerate() { if let ty::BoundVariableKind::Region(kind) = bound_var { let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind); let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 42b7e0e06d1..09f5e6f6efc 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -1,11 +1,13 @@ -use std::mem; +use std::{mem, slice}; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, NodeId, attr}; +use rustc_ast::{self as ast, HasNodeId, NodeId, attr}; use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::AttributeKind; +use rustc_attr_parsing::AttributeParser; use rustc_errors::DiagCtxtHandle; -use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs}; +use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; use rustc_session::Session; @@ -22,7 +24,7 @@ struct ProcMacroDerive { trait_name: Symbol, function_ident: Ident, span: Span, - attrs: Vec<Symbol>, + attrs: ThinVec<Symbol>, } struct ProcMacroDef { @@ -41,6 +43,7 @@ struct CollectProcMacros<'a> { macros: Vec<ProcMacro>, in_root: bool, dcx: DiagCtxtHandle<'a>, + session: &'a Session, source_map: &'a SourceMap, is_proc_macro_crate: bool, is_test_crate: bool, @@ -63,6 +66,7 @@ pub fn inject( macros: Vec::new(), in_root: true, dcx, + session: sess, source_map: sess.source_map(), is_proc_macro_crate, is_test_crate, @@ -98,8 +102,18 @@ impl<'a> CollectProcMacros<'a> { function_ident: Ident, attr: &'a ast::Attribute, ) { - let Some((trait_name, proc_attrs)) = - parse_macro_name_and_helper_attrs(self.dcx, attr, "derive") + let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive { + trait_name, + helper_attrs, + .. + })) = AttributeParser::parse_limited( + self.session, + slice::from_ref(attr), + sym::proc_macro_derive, + item.span, + item.node_id(), + None, + ) else { return; }; @@ -110,7 +124,7 @@ impl<'a> CollectProcMacros<'a> { span: item.span, trait_name, function_ident, - attrs: proc_attrs, + attrs: helper_attrs, })); } else { let msg = if !self.in_root { diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index 682e7c9b17a..2068b5ca54d 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -47,8 +47,6 @@ pub fn inject( ast::ItemKind::ExternCrate(None, Ident::new(name, ident_span)), ); - krate.items.insert(0, item); - let root = (edition == Edition2015).then_some(kw::PathRoot); let import_path = root @@ -75,6 +73,6 @@ pub fn inject( }), ); - krate.items.insert(0, use_item); + krate.items.splice(0..0, [item, use_item]); krate.items.len() - orig_num_items } diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index f6e6bbc2387..f3d1d5c43ea 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -19,7 +19,7 @@ index 1e336bf..35e6f54 100644 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] - #![feature(array_chunks)] + #![feature(array_ptr_get)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs index b735957..ea728b6 100644 --- a/coretests/tests/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 532702bb1a4..492f4dc4452 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -33,6 +33,7 @@ rustc = "$(pwd)/../dist/bin/rustc-clif" cargo = "$(rustup which cargo)" full-bootstrap = true local-rebuild = true +compiletest-allow-stage0 = true [rust] download-rustc = false diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 7e356b4b462..52e02c857c7 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -166,5 +166,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} +./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} popd diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 85adf0f3716..a04cfa27237 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -74,7 +74,7 @@ pub(crate) fn codegen_tls_ref<'tcx>( pub(crate) fn eval_mir_constant<'tcx>( fx: &FunctionCx<'_, '_, 'tcx>, constant: &ConstOperand<'tcx>, -) -> (ConstValue<'tcx>, Ty<'tcx>) { +) -> (ConstValue, Ty<'tcx>) { let cv = fx.monomorphize(constant.const_); // This cannot fail because we checked all required_consts in advance. let val = cv @@ -93,7 +93,7 @@ pub(crate) fn codegen_constant_operand<'tcx>( pub(crate) fn codegen_const_value<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - const_val: ConstValue<'tcx>, + const_val: ConstValue, ty: Ty<'tcx>, ) -> CValue<'tcx> { let layout = fx.layout_of(ty); @@ -210,8 +210,7 @@ pub(crate) fn codegen_const_value<'tcx>( .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()), layout, ), - ConstValue::Slice { data, meta } => { - let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data); + ConstValue::Slice { alloc_id, meta } => { let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx); let len = fx.bcx.ins().iconst(fx.pointer_type, meta as i64); CValue::by_val_pair(ptr, len, layout) diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index bc0fdd40b6e..2c8271c36a9 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -561,8 +561,6 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc asm test suite"); - env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); - let codegen_backend_path = format!( "{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}", pwd = std::env::current_dir() @@ -588,6 +586,8 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { &"always", &"--stage", &"0", + &"--set", + &"build.compiletest-allow-stage0=true", &"tests/assembly-llvm/asm", &"--compiletest-rustc-args", &rustc_args, @@ -1047,7 +1047,6 @@ where // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc {test_type} test suite"); - env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); let extra = if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; @@ -1070,6 +1069,8 @@ where &"always", &"--stage", &"0", + &"--set", + &"build.compiletest-allow-stage0=true", &format!("tests/{test_type}"), &"--compiletest-rustc-args", &rustc_args, diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 55a28bc9493..a70ac08f01a 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -3,12 +3,4 @@ codegen_gcc_unwinding_inline_asm = codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err} -codegen_gcc_dynamic_linking_with_lto = - cannot prefer dynamic linking when performing LTO - .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO - -codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs - -codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` - codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index e554dd2500b..d558dfbc1c4 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -25,35 +25,21 @@ use std::sync::Arc; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::memmap::Mmap; use rustc_errors::{DiagCtxtHandle, FatalError}; -use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; -use rustc_session::config::{CrateType, Lto}; +use rustc_session::config::Lto; use rustc_target::spec::RelocModel; use tempfile::{TempDir, tempdir}; use crate::back::write::save_temp_bitcode; -use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib}; +use crate::errors::LtoBitcodeFromRlib; use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; -pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::Sdylib => true, - CrateType::Rlib | CrateType::ProcMacro => false, - } -} - struct LtoData { // TODO(antoyo): use symbols_below_threshold. //symbols_below_threshold: Vec<String>, @@ -63,18 +49,9 @@ struct LtoData { fn prepare_lto( cgcx: &CodegenContext<GccCodegenBackend>, + each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, ) -> Result<LtoData, FatalError> { - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; - let tmp_path = match tempdir() { Ok(tmp_path) => tmp_path, Err(error) => { @@ -83,20 +60,6 @@ fn prepare_lto( } }; - let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { - Some(name.clone()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - let mut symbols_below_threshold = { - let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<String>>() - }; - info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); - // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode // from the archive. @@ -105,32 +68,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - dcx.emit_err(LtoDisallowed); - return Err(FatalError); - } - if *crate_type == CrateType::Dylib && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoDylib); - return Err(FatalError); - } - } - - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(DynamicLinkingWithLTO); - return Err(FatalError); - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = - cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - { - let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold"); - symbols_below_threshold - .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); - } - + for path in each_linked_rlib_for_lto { let archive_data = unsafe { Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib") }; @@ -174,19 +112,18 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext<GccCodegenBackend>, + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<GccCodegenBackend>>, - cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, ) -> Result<ModuleCodegen<GccContext>, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let lto_data = prepare_lto(cgcx, dcx)?; + let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?; /*let symbols_below_threshold = lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/ fat_lto( cgcx, dcx, modules, - cached_modules, lto_data.upstream_modules, lto_data.tmp_path, //<o_data.symbols_below_threshold, @@ -197,7 +134,6 @@ fn fat_lto( cgcx: &CodegenContext<GccCodegenBackend>, _dcx: DiagCtxtHandle<'_>, modules: Vec<FatLtoInput<GccCodegenBackend>>, - cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], @@ -211,21 +147,12 @@ fn fat_lto( // modules that are serialized in-memory. // * `in_memory` contains modules which are already parsed and in-memory, // such as from multi-CGU builds. - // - // All of `cached_modules` (cached from previous incremental builds) can - // immediately go onto the `serialized_modules` modules list and then we can - // split the `modules` array into these two lists. let mut in_memory = Vec::new(); - serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| { - info!("pushing cached module {:?}", wp.cgu_name); - (buffer, CString::new(wp.cgu_name).unwrap()) - })); for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), FatLtoInput::Serialized { name, buffer } => { info!("pushing serialized module {:?}", name); - let buffer = SerializedModule::Local(buffer); serialized_modules.push((buffer, CString::new(name).unwrap())); } } @@ -356,12 +283,13 @@ impl ModuleBufferMethods for ModuleBuffer { /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext<GccCodegenBackend>, + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let lto_data = prepare_lto(cgcx, dcx)?; + let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx)?; if cgcx.opts.cg.linker_plugin_lto.enabled() { unreachable!( "We should never reach this case if the LTO step \ diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 113abe70805..c1231142c65 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -4,7 +4,6 @@ use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; -use rustc_errors::DiagCtxtHandle; use rustc_fs_util::link_or_copy; use rustc_session::config::OutputType; use rustc_span::fatal_error::FatalError; @@ -258,14 +257,6 @@ pub(crate) fn codegen( )) } -pub(crate) fn link( - _cgcx: &CodegenContext<GccCodegenBackend>, - _dcx: DiagCtxtHandle<'_>, - mut _modules: Vec<ModuleCodegen<GccContext>>, -) -> Result<ModuleCodegen<GccContext>, FatalError> { - unimplemented!(); -} - pub(crate) fn save_temp_bitcode( cgcx: &CodegenContext<GccCodegenBackend>, _module: &ModuleCodegen<GccContext>, diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index b7e7343460f..0aa16bd88b4 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -15,19 +15,6 @@ pub(crate) struct CopyBitcode { } #[derive(Diagnostic)] -#[diag(codegen_gcc_dynamic_linking_with_lto)] -#[note] -pub(crate) struct DynamicLinkingWithLTO; - -#[derive(Diagnostic)] -#[diag(codegen_gcc_lto_disallowed)] -pub(crate) struct LtoDisallowed; - -#[derive(Diagnostic)] -#[diag(codegen_gcc_lto_dylib)] -pub(crate) struct LtoDylib; - -#[derive(Diagnostic)] #[diag(codegen_gcc_lto_bitcode_from_rlib)] pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index af416929ea7..a3120682500 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -81,6 +81,7 @@ mod type_of; use std::any::Any; use std::fmt::Debug; use std::ops::Deref; +use std::path::PathBuf; #[cfg(not(feature = "master"))] use std::sync::atomic::AtomicBool; #[cfg(not(feature = "master"))] @@ -358,23 +359,28 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_and_optimize_fat_lto( cgcx: &CodegenContext<Self>, + // FIXME(bjorn3): Limit LTO exports to these symbols + _exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, diff_fncs: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { if !diff_fncs.is_empty() { unimplemented!(); } - back::lto::run_fat(cgcx, modules, cached_modules) + back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules) } fn run_thin_lto( cgcx: &CodegenContext<Self>, + // FIXME(bjorn3): Limit LTO exports to these symbols + _exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> { - back::lto::run_thin(cgcx, modules, cached_modules) + back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules) } fn print_pass_timings(&self) { @@ -420,14 +426,6 @@ impl WriteBackendMethods for GccCodegenBackend { fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { unimplemented!(); } - - fn run_link( - cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, - modules: Vec<ModuleCodegen<Self::Module>>, - ) -> Result<ModuleCodegen<Self::Module>, FatalError> { - back::write::link(cgcx, dcx, modules) - } } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index f197ea74473..ce9a51b539d 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -2,10 +2,6 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z au codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} -codegen_llvm_dynamic_linking_with_lto = - cannot prefer dynamic linking when performing LTO - .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO - codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture @@ -16,13 +12,7 @@ codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_ codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} -codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err}) - -codegen_llvm_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs - -codegen_llvm_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` - -codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto` +codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err}) codegen_llvm_mismatch_data_layout = data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 84302009da9..c269f11e931 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -1,33 +1,29 @@ use std::collections::BTreeMap; use std::ffi::{CStr, CString}; use std::fs::File; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; +use object::{Object, ObjectSection}; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; -use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_errors::{DiagCtxtHandle, FatalError}; -use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; -use rustc_session::config::{self, CrateType, Lto}; +use rustc_session::config::{self, Lto}; use tracing::{debug, info}; use crate::back::write::{ self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode, }; -use crate::errors::{ - DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro, -}; +use crate::errors::{LlvmError, LtoBitcodeFromRlib}; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, build_string}; use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; @@ -36,45 +32,21 @@ use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx, attributes}; /// session to determine which CGUs we can reuse. const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; -fn crate_type_allows_lto(crate_type: CrateType) -> bool { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::ProcMacro - | CrateType::Sdylib => true, - CrateType::Rlib => false, - } -} - fn prepare_lto( cgcx: &CodegenContext<LlvmCodegenBackend>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> { - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types), - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; + let mut symbols_below_threshold = exported_symbols_for_lto + .iter() + .map(|symbol| CString::new(symbol.to_owned()).unwrap()) + .collect::<Vec<CString>>(); - let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { - Some(CString::new(name.as_str()).unwrap()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - let mut symbols_below_threshold = { - let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<CString>>() - }; - info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); + // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to + // __llvm_profile_runtime, therefore we won't know until link time if this symbol + // should have default visibility. + symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode @@ -84,37 +56,7 @@ fn prepare_lto( // with either fat or thin LTO let mut upstream_modules = Vec::new(); if cgcx.lto != Lto::ThinLocal { - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - dcx.emit_err(LtoDisallowed); - return Err(FatalError); - } else if *crate_type == CrateType::Dylib { - if !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoDylib); - return Err(FatalError); - } - } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(LtoProcMacro); - return Err(FatalError); - } - } - - if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { - dcx.emit_err(DynamicLinkingWithLTO); - return Err(FatalError); - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = - cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); - { - let _timer = - cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold"); - symbols_below_threshold - .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter)); - } - + for path in each_linked_rlib_for_lto { let archive_data = unsafe { Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib")) .expect("couldn't map rlib") @@ -147,10 +89,6 @@ fn prepare_lto( } } - // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to - // __llvm_profile_runtime, therefore we won't know until link time if this symbol - // should have default visibility. - symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); Ok((symbols_below_threshold, upstream_modules)) } @@ -168,46 +106,32 @@ fn get_bitcode_slice_from_object_data<'a>( // name" which in the public API for sections gets treated as part of the section name, but // internally in MachOObjectFile.cpp gets treated separately. let section_name = bitcode_section_name(cgcx).to_str().unwrap().trim_start_matches("__LLVM,"); - let mut len = 0; - let data = unsafe { - llvm::LLVMRustGetSliceFromObjectDataByName( - obj.as_ptr(), - obj.len(), - section_name.as_ptr(), - section_name.len(), - &mut len, - ) - }; - if !data.is_null() { - assert!(len != 0); - let bc = unsafe { slice::from_raw_parts(data, len) }; - // `bc` must be a sub-slice of `obj`. - assert!(obj.as_ptr() <= bc.as_ptr()); - assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr()); + let obj = + object::File::parse(obj).map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })?; - Ok(bc) - } else { - assert!(len == 0); - Err(LtoBitcodeFromRlib { - llvm_err: llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()), - }) - } + let section = obj + .section_by_name(section_name) + .ok_or_else(|| LtoBitcodeFromRlib { err: format!("Can't find section {section_name}") })?; + + section.data().map_err(|err| LtoBitcodeFromRlib { err: err.to_string() }) } /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext<LlvmCodegenBackend>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<LlvmCodegenBackend>>, - cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; + let (symbols_below_threshold, upstream_modules) = + prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>(); - fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold) + fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold) } /// Performs thin LTO by performing necessary global analysis and returning two @@ -215,12 +139,15 @@ pub(crate) fn run_fat( /// can simply be copied over from the incr. comp. cache. pub(crate) fn run_thin( cgcx: &CodegenContext<LlvmCodegenBackend>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; + let (symbols_below_threshold, upstream_modules) = + prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>(); if cgcx.opts.cg.linker_plugin_lto.enabled() { @@ -245,7 +172,6 @@ fn fat_lto( cgcx: &CodegenContext<LlvmCodegenBackend>, dcx: DiagCtxtHandle<'_>, modules: Vec<FatLtoInput<LlvmCodegenBackend>>, - cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, symbols_below_threshold: &[*const libc::c_char], ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { @@ -258,21 +184,12 @@ fn fat_lto( // modules that are serialized in-memory. // * `in_memory` contains modules which are already parsed and in-memory, // such as from multi-CGU builds. - // - // All of `cached_modules` (cached from previous incremental builds) can - // immediately go onto the `serialized_modules` modules list and then we can - // split the `modules` array into these two lists. let mut in_memory = Vec::new(); - serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| { - info!("pushing cached module {:?}", wp.cgu_name); - (buffer, CString::new(wp.cgu_name).unwrap()) - })); for module in modules { match module { FatLtoInput::InMemory(m) => in_memory.push(m), FatLtoInput::Serialized { name, buffer } => { info!("pushing serialized module {:?}", name); - let buffer = SerializedModule::Local(buffer); serialized_modules.push((buffer, CString::new(name).unwrap())); } } @@ -573,10 +490,10 @@ fn thin_lto( // Save the current ThinLTO import information for the next compilation // session, overwriting the previous serialized data (if any). - if let Some(path) = key_map_path { - if let Err(err) = curr_key_map.save_to_file(&path) { - return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err })); - } + if let Some(path) = key_map_path + && let Err(err) = curr_key_map.save_to_file(&path) + { + return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err })); } Ok((opt_jobs, copy_jobs)) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6f8fba2a30d..85a06f457eb 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -796,29 +796,6 @@ pub(crate) fn optimize( Ok(()) } -pub(crate) fn link( - cgcx: &CodegenContext<LlvmCodegenBackend>, - dcx: DiagCtxtHandle<'_>, - mut modules: Vec<ModuleCodegen<ModuleLlvm>>, -) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { - use super::lto::{Linker, ModuleBuffer}; - // Sort the modules by name to ensure deterministic behavior. - modules.sort_by(|a, b| a.name.cmp(&b.name)); - let (first, elements) = - modules.split_first().expect("Bug! modules must contain at least one module."); - - let mut linker = Linker::new(first.module_llvm.llmod()); - for module in elements { - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_link_module", &*module.name); - let buffer = ModuleBuffer::new(module.module_llvm.llmod()); - linker - .add(buffer.data()) - .map_err(|()| llvm_err(dcx, LlvmError::SerializeModule { name: &module.name }))?; - } - drop(linker); - Ok(modules.remove(0)) -} - pub(crate) fn codegen( cgcx: &CodegenContext<LlvmCodegenBackend>, module: ModuleCodegen<ModuleLlvm>, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0ade9edb0d2..f712b3b83fa 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -687,10 +687,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bx.nonnull_metadata(load); } - if let Some(pointee) = layout.pointee_info_at(bx, offset) { - if let Some(_) = pointee.safe { - bx.align_metadata(load, pointee.align); - } + if let Some(pointee) = layout.pointee_info_at(bx, offset) + && let Some(_) = pointee.safe + { + bx.align_metadata(load, pointee.align); } } abi::Primitive::Float(_) => {} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index a9be833a643..8c9dfcfd18c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -46,21 +46,17 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) { debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); // FIXME(#132395): Can this be none even when coverage is enabled? - let instances_used = match cx.coverage_cx { - Some(ref cx) => cx.instances_used.borrow(), - None => return, - }; + let Some(ref coverage_cx) = cx.coverage_cx else { return }; - let mut covfun_records = instances_used - .iter() - .copied() + let mut covfun_records = coverage_cx + .instances_used() + .into_iter() // Sort by symbol name, so that the global file table is built in an // order that doesn't depend on the stable-hash-based order in which // instances were visited during codegen. .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) .filter_map(|instance| prepare_covfun_record(tcx, instance, true)) .collect::<Vec<_>>(); - drop(instances_used); // In a single designated CGU, also prepare covfun records for functions // in this crate that were instrumented for coverage, but are unused. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index eefbd7cf6c4..119237abd6b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -5,7 +5,7 @@ use rustc_abi::Size; use rustc_codegen_ssa::traits::{ BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use tracing::{debug, instrument}; @@ -20,9 +20,14 @@ mod mapgen; /// Extra per-CGU context/state needed for coverage instrumentation. pub(crate) struct CguCoverageContext<'ll, 'tcx> { - /// Coverage data for each instrumented function identified by DefId. - pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>, - pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>, + /// Associates function instances with an LLVM global that holds the + /// function's symbol name, as needed by LLVM coverage intrinsics. + /// + /// Instances in this map are also considered "used" for the purposes of + /// emitting covfun records. Every covfun record holds a hash of its + /// symbol name, and `llvm-cov` will exit fatally if it can't resolve that + /// hash back to an entry in the binary's `__llvm_prf_names` linker section. + pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>, pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>, covfun_section_name: OnceCell<CString>, @@ -31,7 +36,6 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> { impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { pub(crate) fn new() -> Self { Self { - instances_used: RefCell::<FxIndexSet<_>>::default(), pgo_func_name_var_map: Default::default(), mcdc_condition_bitmap_map: Default::default(), covfun_section_name: Default::default(), @@ -53,6 +57,14 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { .and_then(|bitmap_map| bitmap_map.get(decision_depth as usize)) .copied() // Dereference Option<&&Value> to Option<&Value> } + + /// Returns the list of instances considered "used" in this CGU, as + /// inferred from the keys of `pgo_func_name_var_map`. + pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> { + // Collecting into a Vec is way easier than trying to juggle RefCell + // projections, and this should only run once per CGU anyway. + self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>() + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { @@ -78,7 +90,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// string, to hold the function name passed to LLVM intrinsic /// `instrprof.increment()`. The `Value` is only created once per instance. /// Multiple invocations with the same instance return the same `Value`. - fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { + /// + /// This has the side-effect of causing coverage codegen to consider this + /// function "used", making it eligible to emit an associated covfun record. + fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { debug!("getting pgo_func_name_var for instance={:?}", instance); let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut(); pgo_func_name_var_map.entry(instance).or_insert_with(|| { @@ -102,7 +117,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; } - let fn_name = self.get_pgo_func_name_var(instance); + let fn_name = self.ensure_pgo_func_name_var(instance); let hash = self.const_u64(function_coverage_info.function_source_hash); let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32); self.mcdc_parameters(fn_name, hash, bitmap_bits); @@ -151,11 +166,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; - // Mark the instance as used in this CGU, for coverage purposes. - // This includes functions that were not partitioned into this CGU, - // but were MIR-inlined into one of this CGU's functions. - coverage_cx.instances_used.borrow_mut().insert(instance); - match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" @@ -163,7 +173,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { CoverageKind::VirtualCounter { bcb } if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) => { - let fn_name = bx.get_pgo_func_name_var(instance); + let fn_name = bx.ensure_pgo_func_name_var(instance); let hash = bx.const_u64(function_coverage_info.function_source_hash); let num_counters = bx.const_u32(ids_info.num_counters); let index = bx.const_u32(id.as_u32()); @@ -193,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { "bitmap index of the decision out of range" ); - let fn_name = bx.get_pgo_func_name_var(instance); + let fn_name = bx.ensure_pgo_func_name_var(instance); let hash = bx.const_u64(function_coverage_info.function_source_hash); let bitmap_index = bx.const_u32(bitmap_idx); bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 56fb12d3c22..d1502d2b1e6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -285,8 +285,8 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( // Item(T), // } // ``` - let is_expanding_recursive = - debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + let is_expanding_recursive = adt_def.is_enum() + && debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { if def_id == *parent_def_id { args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 5ca2505cec4..6cbf2dbf7d3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -533,7 +533,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // First, let's see if this is a method within an inherent impl. Because // if yes, we want to make the result subroutine DIE a child of the // subroutine's self-type. - if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) { + if let Some(impl_def_id) = cx.tcx.impl_of_assoc(instance.def_id()) { // If the method does *not* belong to a trait, proceed if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions( diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 31d49e86319..627b0c9ff3b 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -20,11 +20,6 @@ pub(crate) struct SymbolAlreadyDefined<'a> { #[diag(codegen_llvm_sanitizer_memtag_requires_mte)] pub(crate) struct SanitizerMemtagRequiresMte; -#[derive(Diagnostic)] -#[diag(codegen_llvm_dynamic_linking_with_lto)] -#[note] -pub(crate) struct DynamicLinkingWithLTO; - pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { @@ -42,21 +37,9 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { pub(crate) struct AutoDiffWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_lto_disallowed)] -pub(crate) struct LtoDisallowed; - -#[derive(Diagnostic)] -#[diag(codegen_llvm_lto_dylib)] -pub(crate) struct LtoDylib; - -#[derive(Diagnostic)] -#[diag(codegen_llvm_lto_proc_macro)] -pub(crate) struct LtoProcMacro; - -#[derive(Diagnostic)] #[diag(codegen_llvm_lto_bitcode_from_rlib)] pub(crate) struct LtoBitcodeFromRlib { - pub llvm_err: String, + pub err: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index fcc0d378f06..7b27e496986 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -382,26 +382,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let width = size.bits(); let llty = self.type_ix(width); match name { - sym::ctlz | sym::cttz => { - let y = self.const_bool(false); - let ret = self.call_intrinsic( - format!("llvm.{name}"), - &[llty], - &[args[0].immediate(), y], - ); - - self.intcast(ret, result.layout.llvm_type(self), false) - } - sym::ctlz_nonzero => { - let y = self.const_bool(true); - let ret = - self.call_intrinsic("llvm.ctlz", &[llty], &[args[0].immediate(), y]); - self.intcast(ret, result.layout.llvm_type(self), false) - } - sym::cttz_nonzero => { - let y = self.const_bool(true); + sym::ctlz | sym::ctlz_nonzero | sym::cttz | sym::cttz_nonzero => { + let y = + self.const_bool(name == sym::ctlz_nonzero || name == sym::cttz_nonzero); + let llvm_name = if name == sym::ctlz || name == sym::ctlz_nonzero { + "llvm.ctlz" + } else { + "llvm.cttz" + }; let ret = - self.call_intrinsic("llvm.cttz", &[llty], &[args[0].immediate(), y]); + self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]); self.intcast(ret, result.layout.llvm_type(self), false) } sym::ctpop => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index aaf21f9ada9..ca84b6de8b1 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -22,6 +22,7 @@ use std::any::Any; use std::ffi::CStr; use std::mem::ManuallyDrop; +use std::path::PathBuf; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; @@ -167,20 +168,15 @@ impl WriteBackendMethods for LlvmCodegenBackend { let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); print!("{stats}"); } - fn run_link( - cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, - modules: Vec<ModuleCodegen<Self::Module>>, - ) -> Result<ModuleCodegen<Self::Module>, FatalError> { - back::write::link(cgcx, dcx, modules) - } fn run_and_optimize_fat_lto( cgcx: &CodegenContext<Self>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, diff_fncs: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { - let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?; + let mut module = + back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules)?; if !diff_fncs.is_empty() { builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; @@ -194,10 +190,18 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_thin_lto( cgcx: &CodegenContext<Self>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> { - back::lto::run_thin(cgcx, modules, cached_modules) + back::lto::run_thin( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + modules, + cached_modules, + ) } fn optimize( cgcx: &CodegenContext<Self>, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index edfb29dd1be..0d0cb5f139e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2612,13 +2612,6 @@ unsafe extern "C" { len: usize, Identifier: *const c_char, ) -> Option<&Module>; - pub(crate) fn LLVMRustGetSliceFromObjectDataByName( - data: *const u8, - len: usize, - name: *const u8, - name_len: usize, - out_len: &mut usize, - ) -> *const u8; pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 0fb987bdf82..53899da183a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -405,6 +405,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { ("mips64" | "mips64r6", _) => false, // Selection bug <https://github.com/llvm/llvm-project/issues/95471> ("nvptx64", _) => false, + // Unsupported https://github.com/llvm/llvm-project/issues/121122 + ("amdgpu", _) => false, // ABI bugs <https://github.com/rust-lang/rust/issues/125109> et al. (full // list at <https://github.com/rust-lang/rust/issues/116909>) ("powerpc" | "powerpc64", _) => false, @@ -433,6 +435,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` // (ld is 80-bit extended precision). + // + // musl does not implement the symbols required for f128 math at all. + _ if target_env == "musl" => false, ("x86_64", _) => false, (_, "linux") if target_pointer_width == 64 => true, _ => false, diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index cfae1b3ec98..94501da69a7 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,8 +8,8 @@ edition = "2024" ar_archive_writer = "0.4.2" bitflags = "2.4.1" bstr = "1.11.3" -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# `cc` in `rustc_llvm` if you update the `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_llvm`. cc = "=1.2.16" itertools = "0.12" pathdiff = "0.2.0" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index c7bd6ffd1f2..a70d0011d16 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -35,6 +35,10 @@ codegen_ssa_dlltool_fail_import_library = {$stdout} {$stderr} +codegen_ssa_dynamic_linking_with_lto = + cannot prefer dynamic linking when performing LTO + .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO + codegen_ssa_error_calling_dlltool = Error calling dlltool '{$dlltool_path}': {$error} @@ -191,6 +195,12 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} +codegen_ssa_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs + +codegen_ssa_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto` + +codegen_ssa_lto_proc_macro = lto cannot be used for `proc-macro` crate type without `-Zdylib-lto` + codegen_ssa_malformed_cgu_name = found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case). diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5ce301c0eb9..162fbf3d6e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3369,12 +3369,12 @@ fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn s let section = elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version"); - if let Some((_, section)) = section { - if let Some(mut notes) = section.notes(endian, data)? { - return Ok(notes.any(|note| { - note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION) - })); - } + if let Some((_, section)) = section + && let Some(mut notes) = section.notes(endian, data)? + { + return Ok(notes.any(|note| { + note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION) + })); } Ok(false) diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 74f39022afb..b9e0c957363 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use rustc_abi::Endian; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_hashes::Hash128; use rustc_session::Session; @@ -214,7 +214,7 @@ pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>( /// It exports all the provided symbols, but is otherwise empty. fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> { use object::write::elf as write; - use object::{Architecture, elf}; + use object::{AddressSize, Architecture, elf}; let mut stub_buf = Vec::new(); @@ -226,54 +226,94 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] // It is important that the order of reservation matches the order of writing. // The object crate contains many debug asserts that fire if you get this wrong. + let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) + else { + sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )); + }; + let endianness = match sess.target.options.endian { Endian::Little => object::Endianness::Little, Endian::Big => object::Endianness::Big, }; - let mut stub = write::Writer::new(endianness, true, &mut stub_buf); + + let is_64 = match arch.address_size() { + Some(AddressSize::U8 | AddressSize::U16 | AddressSize::U32) => false, + Some(AddressSize::U64) => true, + _ => sess.dcx().fatal(format!( + "raw-dylib is not supported for the architecture `{}`", + sess.target.arch + )), + }; + + let mut stub = write::Writer::new(endianness, is_64, &mut stub_buf); + + let mut vers = Vec::new(); + let mut vers_map = FxHashMap::default(); + let mut syms = Vec::new(); + + for symbol in symbols { + let symbol_name = symbol.name.as_str(); + if let Some((name, version_name)) = symbol_name.split_once('@') { + assert!(!version_name.contains('@')); + let dynstr = stub.add_dynamic_string(name.as_bytes()); + let ver = if let Some(&ver_id) = vers_map.get(version_name) { + ver_id + } else { + let id = vers.len(); + vers_map.insert(version_name, id); + let dynstr = stub.add_dynamic_string(version_name.as_bytes()); + vers.push((version_name, dynstr)); + id + }; + syms.push((name, dynstr, Some(ver))); + } else { + let dynstr = stub.add_dynamic_string(symbol_name.as_bytes()); + syms.push((symbol_name, dynstr, None)); + } + } + + let soname = stub.add_dynamic_string(soname.as_bytes()); // These initial reservations don't reserve any bytes in the binary yet, // they just allocate in the internal data structures. - // First, we crate the dynamic symbol table. It starts with a null symbol + // First, we create the dynamic symbol table. It starts with a null symbol // and then all the symbols and their dynamic strings. stub.reserve_null_dynamic_symbol_index(); - let dynstrs = symbols - .iter() - .map(|sym| { - stub.reserve_dynamic_symbol_index(); - (sym, stub.add_dynamic_string(sym.name.as_str().as_bytes())) - }) - .collect::<Vec<_>>(); - - let soname = stub.add_dynamic_string(soname.as_bytes()); + for _ in syms.iter() { + stub.reserve_dynamic_symbol_index(); + } // Reserve the sections. // We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to. stub.reserve_shstrtab_section_index(); let text_section_name = stub.add_section_name(".text".as_bytes()); let text_section = stub.reserve_section_index(); - stub.reserve_dynstr_section_index(); stub.reserve_dynsym_section_index(); + stub.reserve_dynstr_section_index(); + if !vers.is_empty() { + stub.reserve_gnu_versym_section_index(); + stub.reserve_gnu_verdef_section_index(); + } stub.reserve_dynamic_section_index(); // These reservations now determine the actual layout order of the object file. stub.reserve_file_header(); stub.reserve_shstrtab(); stub.reserve_section_headers(); - stub.reserve_dynstr(); stub.reserve_dynsym(); + stub.reserve_dynstr(); + if !vers.is_empty() { + stub.reserve_gnu_versym(); + stub.reserve_gnu_verdef(1 + vers.len(), 1 + vers.len()); + } stub.reserve_dynamic(2); // DT_SONAME, DT_NULL // First write the ELF header with the arch information. - let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) - else { - sess.dcx().fatal(format!( - "raw-dylib is not supported for the architecture `{}`", - sess.target.arch - )); - }; let e_machine = match (arch, sub_arch) { (Architecture::Aarch64, None) => elf::EM_AARCH64, (Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64, @@ -342,18 +382,19 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] sh_addralign: 1, sh_entsize: 0, }); - stub.write_dynstr_section_header(0); stub.write_dynsym_section_header(0, 1); + stub.write_dynstr_section_header(0); + if !vers.is_empty() { + stub.write_gnu_versym_section_header(0); + stub.write_gnu_verdef_section_header(0); + } stub.write_dynamic_section_header(0); - // .dynstr - stub.write_dynstr(); - // .dynsym stub.write_null_dynamic_symbol(); - for (_, name) in dynstrs { + for (_name, dynstr, _ver) in syms.iter().copied() { stub.write_dynamic_symbol(&write::Sym { - name: Some(name), + name: Some(dynstr), st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE, st_other: elf::STV_DEFAULT, section: Some(text_section), @@ -363,10 +404,47 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] }); } + // .dynstr + stub.write_dynstr(); + + // ld.bfd is unhappy if these sections exist without any symbols, so we only generate them when necessary. + if !vers.is_empty() { + // .gnu_version + stub.write_null_gnu_versym(); + for (_name, _dynstr, ver) in syms.iter().copied() { + stub.write_gnu_versym(if let Some(ver) = ver { + assert!((2 + ver as u16) < elf::VERSYM_HIDDEN); + elf::VERSYM_HIDDEN | (2 + ver as u16) + } else { + 1 + }); + } + + // .gnu_version_d + stub.write_align_gnu_verdef(); + stub.write_gnu_verdef(&write::Verdef { + version: elf::VER_DEF_CURRENT, + flags: elf::VER_FLG_BASE, + index: 1, + aux_count: 1, + name: soname, + }); + for (ver, (_name, dynstr)) in vers.into_iter().enumerate() { + stub.write_gnu_verdef(&write::Verdef { + version: elf::VER_DEF_CURRENT, + flags: 0, + index: 2 + ver as u16, + aux_count: 1, + name: dynstr, + }); + } + } + // .dynamic // the DT_SONAME will be used by the linker to populate DT_NEEDED // which the loader uses to find the library. // DT_NULL terminates the .dynamic table. + stub.write_align_dynamic(); stub.write_dynamic_string(elf::DT_SONAME, soname); stub.write_dynamic(elf::DT_NULL, 0); diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index b49b6783bbd..c95038375a1 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -2,7 +2,15 @@ use std::ffi::CString; use std::sync::Arc; use rustc_data_structures::memmap::Mmap; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, Lto}; +use tracing::info; +use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate}; +use crate::back::write::CodegenContext; +use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; pub struct ThinModule<B: WriteBackendMethods> { @@ -52,3 +60,86 @@ impl<M: ModuleBufferMethods> SerializedModule<M> { } } } + +fn crate_type_allows_lto(crate_type: CrateType) -> bool { + match crate_type { + CrateType::Executable + | CrateType::Dylib + | CrateType::Staticlib + | CrateType::Cdylib + | CrateType::ProcMacro + | CrateType::Sdylib => true, + CrateType::Rlib => false, + } +} + +pub(super) fn exported_symbols_for_lto( + tcx: TyCtxt<'_>, + each_linked_rlib_for_lto: &[CrateNum], +) -> Vec<String> { + let export_threshold = match tcx.sess.lto() { + // We're just doing LTO for our one crate + Lto::ThinLocal => SymbolExportLevel::Rust, + + // We're doing LTO for the entire crate graph + Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()), + + Lto::No => return vec![], + }; + + let copy_symbols = |cnum| { + tcx.exported_non_generic_symbols(cnum) + .iter() + .chain(tcx.exported_generic_symbols(cnum)) + .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| { + if info.level.is_below_threshold(export_threshold) || info.used { + Some(symbol_name_for_instance_in_crate(tcx, s, cnum)) + } else { + None + } + }) + .collect::<Vec<_>>() + }; + let mut symbols_below_threshold = { + let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + copy_symbols(LOCAL_CRATE) + }; + info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); + + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, include their exported symbols. + if tcx.sess.lto() != Lto::ThinLocal { + for &cnum in each_linked_rlib_for_lto { + let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); + symbols_below_threshold.extend(copy_symbols(cnum)); + } + } + + symbols_below_threshold +} + +pub(super) fn check_lto_allowed<B: WriteBackendMethods>(cgcx: &CodegenContext<B>) { + if cgcx.lto == Lto::ThinLocal { + // Crate local LTO is always allowed + return; + } + + let dcx = cgcx.create_dcx(); + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + dcx.handle().emit_fatal(LtoDisallowed); + } else if *crate_type == CrateType::Dylib { + if !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(LtoDylib); + } + } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(LtoProcMacro); + } + } + + if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto { + dcx.handle().emit_fatal(DynamicLinkingWithLTO); + } +} diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 2f5eca2d6b2..297bdec2bc2 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -86,7 +86,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S // Only consider nodes that actually have exported symbols. match tcx.def_kind(def_id) { DefKind::Fn | DefKind::Static { .. } => {} - DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {} + DefKind::AssocFn if tcx.impl_of_assoc(def_id.to_def_id()).is_some() => {} _ => return None, }; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 24e0a4eb533..6773d3e24e9 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::assert_matches::assert_matches; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -9,7 +8,7 @@ use std::{fs, io, mem, str, thread}; use rustc_abi::Size; use rustc_ast::attr; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; @@ -20,14 +19,12 @@ use rustc_errors::{ Suggestions, }; use rustc_fs_util::link_or_copy; -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; -use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{ @@ -40,7 +37,7 @@ use tracing::debug; use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; -use super::symbol_export::symbol_name_for_instance_in_crate; +use crate::back::lto::check_lto_allowed; use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir}; use crate::traits::*; use crate::{ @@ -332,8 +329,6 @@ pub type TargetMachineFactoryFn<B> = Arc< + Sync, >; -type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>; - /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] pub struct CodegenContext<B: WriteBackendMethods> { @@ -343,10 +338,8 @@ pub struct CodegenContext<B: WriteBackendMethods> { pub save_temps: bool, pub fewer_names: bool, pub time_trace: bool, - pub exported_symbols: Option<Arc<ExportedSymbols>>, pub opts: Arc<config::Options>, pub crate_types: Vec<CrateType>, - pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, pub output_filenames: Arc<OutputFilenames>, pub invocation_temp: Option<String>, pub regular_module_config: Arc<ModuleConfig>, @@ -378,8 +371,6 @@ pub struct CodegenContext<B: WriteBackendMethods> { /// The incremental compilation session directory, or None if we are not /// compiling incrementally pub incr_comp_session_dir: Option<PathBuf>, - /// Channel back to the main control thread to send messages to - pub coordinator_send: Sender<Box<dyn Any + Send>>, /// `true` if the codegen should be run in parallel. /// /// Depends on [`ExtraBackendMethods::supports_parallel()`] and `-Zno_parallel_backend`. @@ -401,13 +392,21 @@ impl<B: WriteBackendMethods> CodegenContext<B> { fn generate_thin_lto_work<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, ) -> Vec<(WorkItem<B>, u64)> { let _prof_timer = cgcx.prof.generic_activity("codegen_thin_generate_lto_work"); - let (lto_modules, copy_jobs) = - B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + let (lto_modules, copy_jobs) = B::run_thin_lto( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_thin_lto, + import_only_modules, + ) + .unwrap_or_else(|e| e.raise()); lto_modules .into_iter() .map(|module| { @@ -723,6 +722,8 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> { CopyPostLtoArtifacts(CachedModuleCodegen), /// Performs fat LTO on the given module. FatLto { + exported_symbols_for_lto: Arc<Vec<String>>, + each_linked_rlib_for_lto: Vec<PathBuf>, needs_fat_lto: Vec<FatLtoInput<B>>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, autodiff: Vec<AutoDiffItem>, @@ -796,10 +797,6 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> { /// The backend has finished compiling a CGU, nothing more required. Finished(CompiledModule), - /// The backend has finished compiling a CGU, which now needs linking - /// because `-Zcombine-cgu` was specified. - NeedsLink(ModuleCodegen<B::Module>), - /// The backend has finished compiling a CGU, which now needs to go through /// fat LTO. NeedsFatLto(FatLtoInput<B>), @@ -810,7 +807,7 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> { } pub enum FatLtoInput<B: WriteBackendMethods> { - Serialized { name: String, buffer: B::ModuleBuffer }, + Serialized { name: String, buffer: SerializedModule<B::ModuleBuffer> }, InMemory(ModuleCodegen<B::Module>), } @@ -883,7 +880,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( }; match lto_type { - ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), + ComputedLtoType::No => { + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) + } ComputedLtoType::Thin => { let (name, thin_buffer) = B::prepare_thin(module, false); if let Some(path) = bitcode { @@ -899,7 +899,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); - Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { name, buffer })) + Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { + name, + buffer: SerializedModule::Local(buffer), + })) } None => Ok(WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module))), }, @@ -992,12 +995,24 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( fn execute_fat_lto_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - needs_fat_lto: Vec<FatLtoInput<B>>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], + mut needs_fat_lto: Vec<FatLtoInput<B>>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, autodiff: Vec<AutoDiffItem>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { - let module = B::run_and_optimize_fat_lto(cgcx, needs_fat_lto, import_only_modules, autodiff)?; + for (module, wp) in import_only_modules { + needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module }) + } + + let module = B::run_and_optimize_fat_lto( + cgcx, + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_fat_lto, + autodiff, + )?; let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } @@ -1008,20 +1023,8 @@ fn execute_thin_lto_work_item<B: ExtraBackendMethods>( module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { let module = B::optimize_thin(cgcx, module)?; - finish_intra_module_work(cgcx, module, module_config) -} - -fn finish_intra_module_work<B: ExtraBackendMethods>( - cgcx: &CodegenContext<B>, - module: ModuleCodegen<B::Module>, - module_config: &ModuleConfig, -) -> Result<WorkItemResult<B>, FatalError> { - if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator { - let module = B::codegen(cgcx, module, module_config)?; - Ok(WorkItemResult::Finished(module)) - } else { - Ok(WorkItemResult::NeedsLink(module)) - } + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) } /// Messages sent to the coordinator. @@ -1032,7 +1035,7 @@ pub(crate) enum Message<B: WriteBackendMethods> { /// The backend has finished processing a work item for a codegen unit. /// Sent from a backend worker thread. - WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize }, + WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>> }, /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the @@ -1103,52 +1106,28 @@ fn start_executing_work<B: ExtraBackendMethods>( autodiff_items: &[AutoDiffItem], shared_emitter: SharedEmitter, codegen_worker_send: Sender<CguMessage>, - coordinator_receive: Receiver<Box<dyn Any + Send>>, + coordinator_receive: Receiver<Message<B>>, regular_config: Arc<ModuleConfig>, allocator_config: Arc<ModuleConfig>, - tx_to_llvm_workers: Sender<Box<dyn Any + Send>>, + tx_to_llvm_workers: Sender<Message<B>>, ) -> thread::JoinHandle<Result<CompiledModules, ()>> { let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; let autodiff_items = autodiff_items.to_vec(); let mut each_linked_rlib_for_lto = Vec::new(); + let mut each_linked_rlib_file_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { if link::ignored_for_lto(sess, crate_info, cnum) { return; } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + each_linked_rlib_for_lto.push(cnum); + each_linked_rlib_file_for_lto.push(path.to_path_buf()); })); // Compute the set of symbols we need to retain when doing LTO (if we need to) - let exported_symbols = { - let mut exported_symbols = FxHashMap::default(); - - let copy_symbols = |cnum| { - let symbols = tcx - .exported_non_generic_symbols(cnum) - .iter() - .chain(tcx.exported_generic_symbols(cnum)) - .map(|&(s, lvl)| (symbol_name_for_instance_in_crate(tcx, s, cnum), lvl)) - .collect(); - Arc::new(symbols) - }; - - match sess.lto() { - Lto::No => None, - Lto::ThinLocal => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - Some(Arc::new(exported_symbols)) - } - Lto::Fat | Lto::Thin => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - for &(cnum, ref _path) in &each_linked_rlib_for_lto { - exported_symbols.insert(cnum, copy_symbols(cnum)); - } - Some(Arc::new(exported_symbols)) - } - } - }; + let exported_symbols_for_lto = + Arc::new(lto::exported_symbols_for_lto(tcx, &each_linked_rlib_for_lto)); // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. @@ -1158,7 +1137,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let coordinator_send2 = coordinator_send.clone(); let helper = jobserver::client() .into_helper_thread(move |token| { - drop(coordinator_send2.send(Box::new(Message::Token::<B>(token)))); + drop(coordinator_send2.send(Message::Token::<B>(token))); }) .expect("failed to spawn helper thread"); @@ -1183,18 +1162,15 @@ fn start_executing_work<B: ExtraBackendMethods>( let cgcx = CodegenContext::<B> { crate_types: tcx.crate_types().to_vec(), - each_linked_rlib_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, time_trace: sess.opts.unstable_opts.llvm_time_trace, opts: Arc::new(sess.opts.clone()), prof: sess.prof.clone(), - exported_symbols, remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - coordinator_send, expanded_args: tcx.sess.expanded_args.clone(), diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), @@ -1350,23 +1326,10 @@ fn start_executing_work<B: ExtraBackendMethods>( // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || { - let mut worker_id_counter = 0; - let mut free_worker_ids = Vec::new(); - let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| { - if let Some(id) = free_worker_ids.pop() { - id - } else { - let id = worker_id_counter; - worker_id_counter += 1; - id - } - }; - // This is where we collect codegen units that have gone all the way // through codegen and LLVM. let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; - let mut needs_link = Vec::new(); let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); @@ -1442,12 +1405,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let (item, _) = work_items.pop().expect("queue empty - queue_full_enough() broken?"); main_thread_state = MainThreadState::Lending; - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); } } } else if codegen_state == Completed { @@ -1474,12 +1432,18 @@ fn start_executing_work<B: ExtraBackendMethods>( let needs_fat_lto = mem::take(&mut needs_fat_lto); let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); + let each_linked_rlib_file_for_lto = + mem::take(&mut each_linked_rlib_file_for_lto); + + check_lto_allowed(&cgcx); if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); work_items.push(( WorkItem::FatLto { + exported_symbols_for_lto: Arc::clone(&exported_symbols_for_lto), + each_linked_rlib_for_lto: each_linked_rlib_file_for_lto, needs_fat_lto, import_only_modules, autodiff: autodiff_items.clone(), @@ -1495,9 +1459,13 @@ fn start_executing_work<B: ExtraBackendMethods>( dcx.handle().emit_fatal(AutodiffWithoutLto {}); } - for (work, cost) in - generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules) - { + for (work, cost) in generate_thin_lto_work( + &cgcx, + &exported_symbols_for_lto, + &each_linked_rlib_file_for_lto, + needs_thin_lto, + import_only_modules, + ) { let insertion_index = work_items .binary_search_by_key(&cost, |&(_, cost)| cost) .unwrap_or_else(|e| e); @@ -1516,12 +1484,7 @@ fn start_executing_work<B: ExtraBackendMethods>( MainThreadState::Idle => { if let Some((item, _)) = work_items.pop() { main_thread_state = MainThreadState::Lending; - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); } else { // There is no unstarted work, so let the main thread // take over for a running worker. Otherwise the @@ -1557,12 +1520,7 @@ fn start_executing_work<B: ExtraBackendMethods>( while running_with_own_token < tokens.len() && let Some((item, _)) = work_items.pop() { - spawn_work( - &cgcx, - &mut llvm_start_time, - get_worker_id(&mut free_worker_ids), - item, - ); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); running_with_own_token += 1; } } @@ -1570,23 +1528,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // Relinquish accidentally acquired extra tokens. tokens.truncate(running_with_own_token); - // If a thread exits successfully then we drop a token associated - // with that worker and update our `running_with_own_token` count. - // We may later re-acquire a token to continue running more work. - // We may also not actually drop a token here if the worker was - // running with an "ephemeral token". - let mut free_worker = |worker_id| { - if main_thread_state == MainThreadState::Lending { - main_thread_state = MainThreadState::Idle; - } else { - running_with_own_token -= 1; - } - - free_worker_ids.push(worker_id); - }; - - let msg = coordinator_receive.recv().unwrap(); - match *msg.downcast::<Message<B>>().ok().unwrap() { + match coordinator_receive.recv().unwrap() { // Save the token locally and the next turn of the loop will use // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. @@ -1653,14 +1595,22 @@ fn start_executing_work<B: ExtraBackendMethods>( codegen_state = Aborted; } - Message::WorkItem { result, worker_id } => { - free_worker(worker_id); + Message::WorkItem { result } => { + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running_with_own_token` count. + // We may later re-acquire a token to continue running more work. + // We may also not actually drop a token here if the worker was + // running with an "ephemeral token". + if main_thread_state == MainThreadState::Lending { + main_thread_state = MainThreadState::Idle; + } else { + running_with_own_token -= 1; + } match result { Ok(WorkItemResult::Finished(compiled_module)) => { match compiled_module.kind { ModuleKind::Regular => { - assert!(needs_link.is_empty()); compiled_modules.push(compiled_module); } ModuleKind::Allocator => { @@ -1669,10 +1619,6 @@ fn start_executing_work<B: ExtraBackendMethods>( } } } - Ok(WorkItemResult::NeedsLink(module)) => { - assert!(compiled_modules.is_empty()); - needs_link.push(module); - } Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => { assert!(!started_lto); assert!(needs_thin_lto.is_empty()); @@ -1709,17 +1655,6 @@ fn start_executing_work<B: ExtraBackendMethods>( return Err(()); } - let needs_link = mem::take(&mut needs_link); - if !needs_link.is_empty() { - assert!(compiled_modules.is_empty()); - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; - let module = - B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; - compiled_modules.push(module); - } - // Drop to print timings drop(llvm_start_time); @@ -1799,8 +1734,8 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext<B>, + coordinator_send: Sender<Message<B>>, llvm_start_time: &mut Option<VerboseTimingGuard<'a>>, - worker_id: usize, work: WorkItem<B>, ) { if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { @@ -1813,26 +1748,23 @@ fn spawn_work<'a, B: ExtraBackendMethods>( // Set up a destructor which will fire off a message that we're done as // we exit. struct Bomb<B: ExtraBackendMethods> { - coordinator_send: Sender<Box<dyn Any + Send>>, + coordinator_send: Sender<Message<B>>, result: Option<Result<WorkItemResult<B>, FatalError>>, - worker_id: usize, } impl<B: ExtraBackendMethods> Drop for Bomb<B> { fn drop(&mut self) { - let worker_id = self.worker_id; let msg = match self.result.take() { - Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id }, + Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result) }, Some(Err(FatalError)) => { - Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id } + Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)) } } - None => Message::WorkItem::<B> { result: Err(None), worker_id }, + None => Message::WorkItem::<B> { result: Err(None) }, }; - drop(self.coordinator_send.send(Box::new(msg))); + drop(self.coordinator_send.send(msg)); } } - let mut bomb = - Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None, worker_id }; + let mut bomb = Bomb::<B> { coordinator_send, result: None }; // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. @@ -1856,12 +1788,20 @@ fn spawn_work<'a, B: ExtraBackendMethods>( ); Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config)) } - WorkItem::FatLto { needs_fat_lto, import_only_modules, autodiff } => { + WorkItem::FatLto { + exported_symbols_for_lto, + each_linked_rlib_for_lto, + needs_fat_lto, + import_only_modules, + autodiff, + } => { let _timer = cgcx .prof .generic_activity_with_arg("codegen_module_perform_lto", "everything"); execute_fat_lto_work_item( &cgcx, + &exported_symbols_for_lto, + &each_linked_rlib_for_lto, needs_fat_lto, import_only_modules, autodiff, @@ -2029,7 +1969,7 @@ impl SharedEmitterMain { } pub struct Coordinator<B: ExtraBackendMethods> { - pub sender: Sender<Box<dyn Any + Send>>, + sender: Sender<Message<B>>, future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>, // Only used for the Message type. phantom: PhantomData<B>, @@ -2046,7 +1986,7 @@ impl<B: ExtraBackendMethods> Drop for Coordinator<B> { if let Some(future) = self.future.take() { // If we haven't joined yet, signal to the coordinator that it should spawn no more // work, and wait for worker threads to finish. - drop(self.sender.send(Box::new(Message::CodegenAborted::<B>))); + drop(self.sender.send(Message::CodegenAborted::<B>)); drop(future.join()); } } @@ -2105,7 +2045,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { pub(crate) fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); - drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>))); + drop(self.coordinator.sender.send(Message::CodegenComplete::<B>)); } pub(crate) fn check_for_errors(&self, sess: &Session) { @@ -2126,28 +2066,25 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { } pub(crate) fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>( - _backend: &B, - tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, + coordinator: &Coordinator<B>, module: ModuleCodegen<B::Module>, cost: u64, ) { let llvm_work_item = WorkItem::Optimize(module); - drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost }))); + drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost })); } pub(crate) fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>( - _backend: &B, - tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, + coordinator: &Coordinator<B>, module: CachedModuleCodegen, ) { let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module); - drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost: 0 }))); + drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost: 0 })); } pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( - _backend: &B, tcx: TyCtxt<'_>, - tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, + coordinator: &Coordinator<B>, module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); @@ -2161,10 +2098,10 @@ pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( }) }; // Schedule the module to be loaded - drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule::<B> { + drop(coordinator.sender.send(Message::AddImportOnlyModule::<B> { module_data: SerializedModule::FromUncompressedFile(mmap), work_product: module.source, - }))); + })); } fn pre_lto_bitcode_filename(module_name: &str) -> String { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 833456abb8a..a5807c56e31 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -702,8 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // These modules are generally cheap and won't throw off scheduling. let cost = 0; submit_codegened_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, ModuleCodegen::new_allocator(llmod_id, module_llvm), cost, ); @@ -800,18 +799,12 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // compilation hang on post-monomorphization errors. tcx.dcx().abort_if_errors(); - submit_codegened_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, - module, - cost, - ); + submit_codegened_module_to_llvm(&ongoing_codegen.coordinator, module, cost); } CguReuse::PreLto => { submit_pre_lto_module_to_llvm( - &backend, tcx, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -820,8 +813,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } CguReuse::PostLto => { submit_post_lto_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index dd49db26689..5c54bce6e03 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ - AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr, + AttributeKind, InlineAttr, InstructionSetAttr, UsedBy, find_attr, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{self as hir, LangItem, lang_items}; +use rustc_hir::{self as hir, Attribute, LangItem, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; @@ -53,77 +53,196 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { } } -fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { - if cfg!(debug_assertions) { - let def_kind = tcx.def_kind(did); - assert!( - def_kind.has_codegen_attrs(), - "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", - ); +/// In some cases, attributes are only valid on functions, but it's the `check_attr` +/// pass that checks that they aren't used anywhere else, rather than this module. +/// In these cases, we bail from performing further checks that are only meaningful for +/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also +/// report a delayed bug, just in case `check_attr` isn't doing its job. +fn try_fn_sig<'tcx>( + tcx: TyCtxt<'tcx>, + did: LocalDefId, + attr_span: Span, +) -> Option<ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>>> { + use DefKind::*; + + let def_kind = tcx.def_kind(did); + if let Fn | AssocFn | Variant | Ctor(..) = def_kind { + Some(tcx.fn_sig(did)) + } else { + tcx.dcx().span_delayed_bug(attr_span, "this attribute can only be applied to functions"); + None } +} - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)); - let mut codegen_fn_attrs = CodegenFnAttrs::new(); - if tcx.should_inherit_track_caller(did) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; +// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr +fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<InstructionSetAttr> { + let list = attr.meta_item_list()?; + + match &list[..] { + [MetaItemInner::MetaItem(set)] => { + let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>(); + match segments.as_slice() { + [sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => { + tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() }); + None + } + [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32), + [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32), + _ => { + tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() }); + None + } + } + } + [] => { + tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() }); + None + } + _ => { + tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() }); + None + } + } +} + +// FIXME(jdonszelmann): remove when linkage becomes a parsed attr +fn parse_linkage_attr(tcx: TyCtxt<'_>, did: LocalDefId, attr: &Attribute) -> Option<Linkage> { + let val = attr.value_str()?; + let linkage = linkage_by_name(tcx, did, val.as_str()); + Some(linkage) +} + +// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr +fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> { + let list = attr.meta_item_list()?; + let mut sanitizer_set = SanitizerSet::empty(); + + for item in list.iter() { + match item.name() { + Some(sym::address) => { + sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS + } + Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI, + Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI, + Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY, + Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG, + Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK, + Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD, + Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS, + _ => { + tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); + } + } } + Some(sanitizer_set) +} + +// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr +fn parse_patchable_function_entry( + tcx: TyCtxt<'_>, + attr: &Attribute, +) -> Option<PatchableFunctionEntry> { + attr.meta_item_list().and_then(|l| { + let mut prefix = None; + let mut entry = None; + for item in l { + let Some(meta_item) = item.meta_item() else { + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); + continue; + }; + + let Some(name_value_lit) = meta_item.name_value_literal() else { + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); + continue; + }; + + let attrib_to_write = match meta_item.name() { + Some(sym::prefix_nops) => &mut prefix, + Some(sym::entry_nops) => &mut entry, + _ => { + tcx.dcx().emit_err(errors::UnexpectedParameterName { + span: item.span(), + prefix_nops: sym::prefix_nops, + entry_nops: sym::entry_nops, + }); + continue; + } + }; + + let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { + tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span }); + continue; + }; + + let Ok(val) = val.get().try_into() else { + tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); + continue; + }; + + *attrib_to_write = Some(val); + } + + if let (None, None) = (prefix, entry) { + tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); + } + + Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0))) + }) +} + +/// Spans that are collected when processing built-in attributes, +/// that are useful for emitting diagnostics later. +#[derive(Default)] +struct InterestingAttributeDiagnosticSpans { + link_ordinal: Option<Span>, + no_sanitize: Option<Span>, + inline: Option<Span>, + no_mangle: Option<Span>, +} + +/// Process the builtin attrs ([`hir::Attribute`]) on the item. +/// Many of them directly translate to codegen attrs. +fn process_builtin_attrs( + tcx: TyCtxt<'_>, + did: LocalDefId, + attrs: &[Attribute], + codegen_fn_attrs: &mut CodegenFnAttrs, +) -> InterestingAttributeDiagnosticSpans { + let mut interesting_spans = InterestingAttributeDiagnosticSpans::default(); + let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); + // If our rustc version supports autodiff/enzyme, then we call our handler // to check for any `#[rustc_autodiff(...)]` attributes. + // FIXME(jdonszelmann): merge with loop below if cfg!(llvm_enzyme) { let ad = autodiff_attrs(tcx, did.into()); codegen_fn_attrs.autodiff_item = ad; } - // When `no_builtins` is applied at the crate level, we should add the - // `no-builtins` attribute to each function to ensure it takes effect in LTO. - let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); - let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); - if no_builtins { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; - } - - let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - - let mut link_ordinal_span = None; - let mut no_sanitize_span = None; - for attr in attrs.iter() { - // In some cases, attribute are only valid on functions, but it's the `check_attr` - // pass that check that they aren't used anywhere else, rather this module. - // In these cases, we bail from performing further checks that are only meaningful for - // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also - // report a delayed bug, just in case `check_attr` isn't doing its job. - let fn_sig = |attr_span| { - use DefKind::*; - - let def_kind = tcx.def_kind(did); - if let Fn | AssocFn | Variant | Ctor(..) = def_kind { - Some(tcx.fn_sig(did)) - } else { - tcx.dcx() - .span_delayed_bug(attr_span, "this attribute can only be applied to functions"); - None - } - }; - if let hir::Attribute::Parsed(p) = attr { match p { AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => { - codegen_fn_attrs.export_name = Some(*name); + codegen_fn_attrs.export_name = Some(*name) + } + AttributeKind::Inline(inline, span) => { + codegen_fn_attrs.inline = *inline; + interesting_spans.inline = Some(*span); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), AttributeKind::LinkOrdinal { ordinal, span } => { codegen_fn_attrs.link_ordinal = Some(*ordinal); - link_ordinal_span = Some(*span); + interesting_spans.link_ordinal = Some(*span); } AttributeKind::LinkSection { name, .. } => { codegen_fn_attrs.link_section = Some(*name) } AttributeKind::NoMangle(attr_span) => { + interesting_spans.no_mangle = Some(*attr_span); if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else { @@ -137,6 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }); } } + AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, AttributeKind::TargetFeature(features, attr_span) => { let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); @@ -184,7 +304,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let is_closure = tcx.is_closure_like(did.to_def_id()); if !is_closure - && let Some(fn_sig) = fn_sig(*attr_span) + && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) && fn_sig.skip_binder().abi() != ExternAbi::Rust { tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); @@ -232,155 +352,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::linkage => { - if let Some(val) = attr.value_str() { - let linkage = Some(linkage_by_name(tcx, did, val.as_str())); - if tcx.is_foreign_item(did) { - codegen_fn_attrs.import_linkage = linkage; - - if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( - attr.span(), - "extern mutable statics are not allowed with `#[linkage]`", - ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); - } - } else { - codegen_fn_attrs.linkage = linkage; + let linkage = parse_linkage_attr(tcx, did, attr); + + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + + if tcx.is_mutable_static(did.into()) { + let mut diag = tcx.dcx().struct_span_err( + attr.span(), + "extern mutable statics are not allowed with `#[linkage]`", + ); + diag.note( + "marking the extern static mutable would allow changing which \ + symbol the static references rather than make the target of the \ + symbol mutable", + ); + diag.emit(); } + } else { + codegen_fn_attrs.linkage = linkage; } } sym::no_sanitize => { - no_sanitize_span = Some(attr.span()); - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - match item.name() { - Some(sym::address) => { - codegen_fn_attrs.no_sanitize |= - SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS - } - Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, - Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, - Some(sym::memory) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY - } - Some(sym::memtag) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG - } - Some(sym::shadow_call_stack) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK - } - Some(sym::thread) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD - } - Some(sym::hwaddress) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS - } - _ => { - tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); - } - } - } - } + interesting_spans.no_sanitize = Some(attr.span()); + codegen_fn_attrs.no_sanitize |= + parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); } sym::instruction_set => { - codegen_fn_attrs.instruction_set = - attr.meta_item_list().and_then(|l| match &l[..] { - [MetaItemInner::MetaItem(set)] => { - let segments = - set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>(); - match segments.as_slice() { - [sym::arm, sym::a32 | sym::t32] - if !tcx.sess.target.has_thumb_interworking => - { - tcx.dcx().emit_err(errors::UnsupportedInstructionSet { - span: attr.span(), - }); - None - } - [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32), - [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32), - _ => { - tcx.dcx().emit_err(errors::InvalidInstructionSet { - span: attr.span(), - }); - None - } - } - } - [] => { - tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() }); - None - } - _ => { - tcx.dcx() - .emit_err(errors::MultipleInstructionSet { span: attr.span() }); - None - } - }) + codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } sym::patchable_function_entry => { - codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { - let mut prefix = None; - let mut entry = None; - for item in l { - let Some(meta_item) = item.meta_item() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let Some(name_value_lit) = meta_item.name_value_literal() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let attrib_to_write = match meta_item.name() { - Some(sym::prefix_nops) => &mut prefix, - Some(sym::entry_nops) => &mut entry, - _ => { - tcx.dcx().emit_err(errors::UnexpectedParameterName { - span: item.span(), - prefix_nops: sym::prefix_nops, - entry_nops: sym::entry_nops, - }); - continue; - } - }; - - let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { - tcx.dcx().emit_err(errors::InvalidLiteralValue { - span: name_value_lit.span, - }); - continue; - }; - - let Ok(val) = val.get().try_into() else { - tcx.dcx() - .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); - continue; - }; - - *attrib_to_write = Some(val); - } - - if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); - } - - Some(PatchableFunctionEntry::from_prefix_and_entry( - prefix.unwrap_or(0), - entry.unwrap_or(0), - )) - }) + codegen_fn_attrs.patchable_function_entry = + parse_patchable_function_entry(tcx, attr); } _ => {} } } + interesting_spans +} + +/// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary. +/// Please comment why when adding a new one! +fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) { // Apply the minimum function alignment here. This ensures that a function's alignment is // determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate // it happens to be codegen'd (or const-eval'd) in. @@ -390,15 +404,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); - let inline_span; - (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) = - find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span)) - { - (inline_attr, Some(span)) - } else { - (InlineAttr::None, None) - }; - // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, // but not for the code generation backend because at that point the naked function will just be // a declaration, with a definition provided in global assembly. @@ -406,9 +411,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = - find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default); - // #73631: closures inherit `#[target_feature]` annotations // // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`. @@ -431,6 +433,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } + // When `no_builtins` is applied at the crate level, we should add the + // `no-builtins` attribute to each function to ensure it takes effect in LTO. + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); + let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); + if no_builtins { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; + } + + // inherit track-caller properly + if tcx.should_inherit_track_caller(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } +} + +fn check_result( + tcx: TyCtxt<'_>, + did: LocalDefId, + interesting_spans: InterestingAttributeDiagnosticSpans, + codegen_fn_attrs: &CodegenFnAttrs, +) { // If a function uses `#[target_feature]` it can't be inlined into general // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid `#[inline(always)]` as it can't be @@ -446,14 +468,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) - && let Some(span) = inline_span + && let Some(span) = interesting_spans.inline { tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); } + // warn that inline has no effect when no_sanitize is present if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() - && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) + && let (Some(no_sanitize_span), Some(inline_span)) = + (interesting_spans.no_sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { @@ -462,6 +486,47 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }) } + // error when specifying link_name together with link_ordinal + if let Some(_) = codegen_fn_attrs.link_name + && let Some(_) = codegen_fn_attrs.link_ordinal + { + let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; + if let Some(span) = interesting_spans.link_ordinal { + tcx.dcx().span_err(span, msg); + } else { + tcx.dcx().err(msg); + } + } + + if let Some(features) = check_tied_features( + tcx.sess, + &codegen_fn_attrs + .target_features + .iter() + .map(|features| (features.name.as_str(), true)) + .collect(), + ) { + let span = + find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span) + .unwrap_or_else(|| tcx.def_span(did)); + + tcx.dcx() + .create_err(errors::TargetFeatureDisableOrEnable { + features, + span: Some(span), + missing_features: Some(errors::MissingFeatures), + }) + .emit(); + } +} + +fn handle_lang_items( + tcx: TyCtxt<'_>, + did: LocalDefId, + interesting_spans: &InterestingAttributeDiagnosticSpans, + attrs: &[Attribute], + codegen_fn_attrs: &mut CodegenFnAttrs, +) { // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only // strippable by the linker. @@ -478,68 +543,59 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.link_name = Some(link_name); } } - check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); + // error when using no_mangle on a lang item item if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - let no_mangle_span = - find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span) - .unwrap_or_default(); let lang_item = lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name)); let mut err = tcx .dcx() .struct_span_err( - no_mangle_span, + interesting_spans.no_mangle.unwrap_or_default(), "`#[no_mangle]` cannot be used on internal language items", ) .with_note("Rustc requires this item to have a specific mangled name.") .with_span_label(tcx.def_span(did), "should be the internal language item"); - if let Some(lang_item) = lang_item { - if let Some(link_name) = lang_item.link_name() { - err = err - .with_note("If you are trying to prevent mangling to ease debugging, many") - .with_note(format!( - "debuggers support a command such as `rbreak {link_name}` to" - )) - .with_note(format!( - "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" - )) - } + if let Some(lang_item) = lang_item + && let Some(link_name) = lang_item.link_name() + { + err = err + .with_note("If you are trying to prevent mangling to ease debugging, many") + .with_note(format!("debuggers support a command such as `rbreak {link_name}` to")) + .with_note(format!( + "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" + )) } err.emit(); } +} - // Any linkage to LLVM intrinsics for now forcibly marks them all as never - // unwinds since LLVM sometimes can't handle codegen which `invoke`s - // intrinsic functions. - if let Some(name) = &codegen_fn_attrs.link_name - && name.as_str().starts_with("llvm.") - { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; +/// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]). +/// +/// This happens in 4 stages: +/// - apply built-in attributes that directly translate to codegen attributes. +/// - handle lang items. These have special codegen attrs applied to them. +/// - apply overrides, like minimum requirements for alignment and other settings that don't rely directly the built-in attrs on the item. +/// overrides come after applying built-in attributes since they may only apply when certain attributes were already set in the stage before. +/// - check that the result is valid. There's various ways in which this may not be the case, such as certain combinations of attrs. +fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { + if cfg!(debug_assertions) { + let def_kind = tcx.def_kind(did); + assert!( + def_kind.has_codegen_attrs(), + "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", + ); } - if let Some(features) = check_tied_features( - tcx.sess, - &codegen_fn_attrs - .target_features - .iter() - .map(|features| (features.name.as_str(), true)) - .collect(), - ) { - let span = - find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span) - .unwrap_or_else(|| tcx.def_span(did)); + let mut codegen_fn_attrs = CodegenFnAttrs::new(); + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)); - tcx.dcx() - .create_err(errors::TargetFeatureDisableOrEnable { - features, - span: Some(span), - missing_features: Some(errors::MissingFeatures), - }) - .emit(); - } + let interesting_spans = process_builtin_attrs(tcx, did, attrs, &mut codegen_fn_attrs); + handle_lang_items(tcx, did, &interesting_spans, attrs, &mut codegen_fn_attrs); + apply_overrides(tcx, did, &mut codegen_fn_attrs); + check_result(tcx, did, interesting_spans, &codegen_fn_attrs); codegen_fn_attrs } @@ -566,27 +622,12 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> { tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment } -fn check_link_name_xor_ordinal( - tcx: TyCtxt<'_>, - codegen_fn_attrs: &CodegenFnAttrs, - inline_span: Option<Span>, -) { - if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { - return; - } - let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; - if let Some(span) = inline_span { - tcx.dcx().span_err(span, msg); - } else { - tcx.dcx().err(msg); - } -} - /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] /// macros. There are two forms. The pure one without args to mark primal functions (the functions /// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the /// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never /// panic, unless we introduced a bug when parsing the autodiff macro. +//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed. fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { let attrs = tcx.get_attrs(id, sym::rustc_autodiff); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 48565e0b4de..a6fd6c763ed 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -148,7 +148,7 @@ pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn asm_const_to_str<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, - const_value: mir::ConstValue<'tcx>, + const_value: mir::ConstValue, ty_and_layout: TyAndLayout<'tcx>, ) -> String { let mir::ConstValue::Scalar(scalar) = const_value else { diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 9040915b6af..3d787d8bdbd 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1294,3 +1294,20 @@ pub(crate) struct FeatureNotValid<'a> { #[help] pub plus_hint: bool, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_disallowed)] +pub(crate) struct LtoDisallowed; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_dylib)] +pub(crate) struct LtoDylib; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_lto_proc_macro)] +pub(crate) struct LtoProcMacro; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_dynamic_linking_with_lto)] +#[note] +pub(crate) struct DynamicLinkingWithLTO; diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 6d6465dd798..c2c023af090 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -1,12 +1,13 @@ //! An analysis to determine which locals require allocas and //! which do not. +use rustc_abi as abi; use rustc_data_structures::graph::dominators::Dominators; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal}; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::{bug, span_bug}; use tracing::debug; @@ -99,63 +100,75 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx> context: PlaceContext, location: Location, ) { - let cx = self.fx.cx; - - if let Some((place_base, elem)) = place_ref.last_projection() { - let mut base_context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; - - // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = matches!( - context, - PlaceContext::NonMutatingUse( - NonMutatingUseContext::Copy | NonMutatingUseContext::Move, - ) - ); - if is_consume { - let base_ty = place_base.ty(self.fx.mir, cx.tcx()); - let base_ty = self.fx.monomorphize(base_ty); - - // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty; - let span = self.fx.mir.local_decls[place_ref.local].source_info.span; - if cx.spanned_layout_of(elem_ty, span).is_zst() { - return; + if !place_ref.projection.is_empty() { + const COPY_CONTEXT: PlaceContext = + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy); + + // `PlaceElem::Index` is the only variant that can mention other `Local`s, + // so check for those up-front before any potential short-circuits. + for elem in place_ref.projection { + if let mir::PlaceElem::Index(index_local) = *elem { + self.visit_local(index_local, COPY_CONTEXT, location); } + } - if let mir::ProjectionElem::Field(..) = elem { - let layout = cx.spanned_layout_of(base_ty.ty, span); - if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) { - // Recurse with the same context, instead of `Projection`, - // potentially stopping at non-operand projections, - // which would trigger `not_ssa` on locals. - base_context = context; - } - } + // If our local is already memory, nothing can make it *more* memory + // so we don't need to bother checking the projections further. + if self.locals[place_ref.local] == LocalKind::Memory { + return; } - if let mir::ProjectionElem::Deref = elem { - // Deref projections typically only read the pointer. - base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy); + if place_ref.is_indirect_first_projection() { + // If this starts with a `Deref`, we only need to record a read of the + // pointer being dereferenced, as all the subsequent projections are + // working on a place which is always supported. (And because we're + // looking at codegen MIR, it can only happen as the first projection.) + self.visit_local(place_ref.local, COPY_CONTEXT, location); + return; } - self.process_place(&place_base, base_context, location); - // HACK(eddyb) this emulates the old `visit_projection_elem`, this - // entire `visit_place`-like `process_place` method should be rewritten, - // now that we have moved to the "slice of projections" representation. - if let mir::ProjectionElem::Index(local) = elem { - self.visit_local( - local, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), - location, - ); + if context.is_mutating_use() { + // If it's a mutating use it doesn't matter what the projections are, + // if there are *any* then we need a place to write. (For example, + // `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.) + let mut_projection = PlaceContext::MutatingUse(MutatingUseContext::Projection); + self.visit_local(place_ref.local, mut_projection, location); + return; } - } else { - self.visit_local(place_ref.local, context, location); + + // Scan through to ensure the only projections are those which + // `FunctionCx::maybe_codegen_consume_direct` can handle. + let base_ty = self.fx.monomorphized_place_ty(mir::PlaceRef::from(place_ref.local)); + let mut layout = self.fx.cx.layout_of(base_ty); + for elem in place_ref.projection { + layout = match *elem { + mir::PlaceElem::Field(fidx, ..) => layout.field(self.fx.cx, fidx.as_usize()), + mir::PlaceElem::Downcast(_, vidx) + if let abi::Variants::Single { index: single_variant } = + layout.variants + && vidx == single_variant => + { + layout.for_variant(self.fx.cx, vidx) + } + mir::PlaceElem::Subtype(subtype_ty) => { + let subtype_ty = self.fx.monomorphize(subtype_ty); + self.fx.cx.layout_of(subtype_ty) + } + _ => { + self.locals[place_ref.local] = LocalKind::Memory; + return; + } + } + } + debug_assert!( + !self.fx.cx.is_backend_ref(layout), + "Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}", + ); } + + // Even with supported projections, we still need to have `visit_local` + // check for things that can't be done in SSA (like `SharedBorrow`). + self.visit_local(place_ref.local, context, location); } } @@ -170,11 +183,6 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer if let Some(local) = place.as_local() { self.define(local, DefLocation::Assignment(location)); - if self.locals[local] != LocalKind::Memory { - if !self.fx.rvalue_creates_operand(rvalue) { - self.locals[local] = LocalKind::Memory; - } - } } else { self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location); } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 68a56044a07..11b6ab3cdf1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef::from_const(bx, val, ty) } - pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> { + pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue { // `MirUsedCollector` visited all required_consts before codegen began, so if we got here // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 06bedaaa4a2..5459f95c186 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -140,7 +140,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { pub(crate) fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>( bx: &mut Bx, - val: mir::ConstValue<'tcx>, + val: mir::ConstValue, ty: Ty<'tcx>, ) -> Self { let layout = bx.layout_of(ty); @@ -154,14 +154,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandValue::Immediate(llval) } ConstValue::ZeroSized => return OperandRef::zero_sized(layout), - ConstValue::Slice { data, meta } => { + ConstValue::Slice { alloc_id, meta } => { let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else { bug!("from_const: invalid ScalarPair layout: {:#?}", layout); }; - let a = Scalar::from_pointer( - Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data).into(), Size::ZERO), - &bx.tcx(), - ); + let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx()); let a_llval = bx.scalar_to_backend( a, a_scalar, @@ -338,13 +335,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let val = if field.is_zst() { OperandValue::ZeroSized - } else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr { - // codegen_transmute_operand doesn't support SIMD, but since the previous - // check handled ZSTs, the only possible field access into something SIMD - // is to the `non_1zst_field` that's the same SIMD. (Other things, even - // just padding, would change the wrapper's representation type.) - assert_eq!(field.size, self.layout.size); - self.val } else if field.size == self.layout.size { assert_eq!(offset.bytes(), 0); fx.codegen_transmute_operand(bx, *self, field) @@ -931,9 +921,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match self.locals[place_ref.local] { LocalRef::Operand(mut o) => { - // Moves out of scalar and scalar pair fields are trivial. - for elem in place_ref.projection.iter() { - match elem { + // We only need to handle the projections that + // `LocalAnalyzer::process_place` let make it here. + for elem in place_ref.projection { + match *elem { mir::ProjectionElem::Field(f, _) => { assert!( !o.layout.ty.is_any_ptr(), @@ -942,17 +933,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ); o = o.extract_field(self, bx, f.index()); } - mir::ProjectionElem::Index(_) - | mir::ProjectionElem::ConstantIndex { .. } => { - // ZSTs don't require any actual memory access. - // FIXME(eddyb) deduplicate this with the identical - // checks in `codegen_consume` and `extract_field`. - let elem = o.layout.field(bx.cx(), 0); - if elem.is_zst() { - o = OperandRef::zero_sized(elem); - } else { - return None; - } + mir::PlaceElem::Downcast(_, vidx) => { + debug_assert_eq!( + o.layout.variants, + abi::Variants::Single { index: vidx }, + ); + let layout = o.layout.for_variant(bx.cx(), vidx); + o = OperandRef { val: o.val, layout } + } + mir::PlaceElem::Subtype(subtype_ty) => { + let subtype_ty = self.monomorphize(subtype_ty); + let layout = self.cx.layout_of(subtype_ty); + o = OperandRef { val: o.val, layout } } _ => return None, } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 610e2fd2311..8a67b8d6e5f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -2,12 +2,12 @@ use rustc_abi::{self as abi, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_middle::{bug, mir}; +use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use tracing::{debug, instrument}; use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; -use super::place::{PlaceRef, codegen_tag_value}; +use super::place::{PlaceRef, PlaceValue, codegen_tag_value}; use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; @@ -180,7 +180,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue)); let temp = self.codegen_rvalue_operand(bx, rvalue); temp.val.store(bx, dest); } @@ -218,17 +217,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Transmutes an `OperandValue` to another `OperandValue`. /// - /// This is supported only for cases where [`Self::rvalue_creates_operand`] - /// returns `true`, and will ICE otherwise. (In particular, anything that - /// would need to `alloca` in order to return a `PlaceValue` will ICE, - /// expecting those to go via [`Self::codegen_transmute`] instead where - /// the destination place is already allocated.) + /// This is supported for all cases where the `cast` type is SSA, + /// but for non-ZSTs with [`abi::BackendRepr::Memory`] it ICEs. pub(crate) fn codegen_transmute_operand( &mut self, bx: &mut Bx, operand: OperandRef<'tcx, Bx::Value>, cast: TyAndLayout<'tcx>, ) -> OperandValue<Bx::Value> { + if let abi::BackendRepr::Memory { .. } = cast.backend_repr + && !cast.is_zst() + { + span_bug!(self.mir.span, "Use `codegen_transmute` to transmute to {cast:?}"); + } + + // `Layout` is interned, so we can do a cheap check for things that are + // exactly the same and thus don't need any handling. + if abi::Layout::eq(&operand.layout.layout, &cast.layout) { + return operand.val; + } + // Check for transmutes that are always UB. if operand.layout.size != cast.size || operand.layout.is_uninhabited() @@ -241,11 +249,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return OperandValue::poison(bx, cast); } + // To or from pointers takes different methods, so we use this to restrict + // the SimdVector case to types which can be `bitcast` between each other. + #[inline] + fn vector_can_bitcast(x: abi::Scalar) -> bool { + matches!( + x, + abi::Scalar::Initialized { + value: abi::Primitive::Int(..) | abi::Primitive::Float(..), + .. + } + ) + } + + let cx = bx.cx(); match (operand.val, operand.layout.backend_repr, cast.backend_repr) { _ if cast.is_zst() => OperandValue::ZeroSized, - (_, _, abi::BackendRepr::Memory { .. }) => { - bug!("Cannot `codegen_transmute_operand` to non-ZST memory-ABI output {cast:?}"); - } (OperandValue::Ref(source_place_val), abi::BackendRepr::Memory { .. }, _) => { assert_eq!(source_place_val.llextra, None); // The existing alignment is part of `source_place_val`, @@ -256,16 +275,46 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate(imm), abi::BackendRepr::Scalar(from_scalar), abi::BackendRepr::Scalar(to_scalar), - ) => OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)), + ) if from_scalar.size(cx) == to_scalar.size(cx) => { + OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)) + } + ( + OperandValue::Immediate(imm), + abi::BackendRepr::SimdVector { element: from_scalar, .. }, + abi::BackendRepr::SimdVector { element: to_scalar, .. }, + ) if vector_can_bitcast(from_scalar) && vector_can_bitcast(to_scalar) => { + let to_backend_ty = bx.cx().immediate_backend_type(cast); + OperandValue::Immediate(bx.bitcast(imm, to_backend_ty)) + } ( OperandValue::Pair(imm_a, imm_b), abi::BackendRepr::ScalarPair(in_a, in_b), abi::BackendRepr::ScalarPair(out_a, out_b), - ) => OperandValue::Pair( - transmute_scalar(bx, imm_a, in_a, out_a), - transmute_scalar(bx, imm_b, in_b, out_b), - ), - _ => bug!("Cannot `codegen_transmute_operand` {operand:?} to {cast:?}"), + ) if in_a.size(cx) == out_a.size(cx) && in_b.size(cx) == out_b.size(cx) => { + OperandValue::Pair( + transmute_scalar(bx, imm_a, in_a, out_a), + transmute_scalar(bx, imm_b, in_b, out_b), + ) + } + _ => { + // For any other potentially-tricky cases, make a temporary instead. + // If anything else wants the target local to be in memory this won't + // be hit, as `codegen_transmute` will get called directly. Thus this + // is only for places where everything else wants the operand form, + // and thus it's not worth making those places get it from memory. + // + // Notably, Scalar ⇌ ScalarPair cases go here to avoid padding + // and endianness issues, as do SimdVector ones to avoid worrying + // about things like f32x8 ⇌ ptrx4 that would need multiple steps. + let align = Ord::max(operand.layout.align.abi, cast.align.abi); + let size = Ord::max(operand.layout.size, cast.size); + let temp = PlaceValue::alloca(bx, size, align); + bx.lifetime_start(temp.llval, size); + operand.val.store(bx, temp.with_type(operand.layout)); + let val = bx.load_operand(temp.with_type(cast)).val; + bx.lifetime_end(temp.llval, size); + val + } } } @@ -288,7 +337,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // valid ranges. For example, `char`s are passed as just `i32`, with no // way for LLVM to know that they're 0x10FFFF at most. Thus we assume // the range of the input value too, not just the output range. - assume_scalar_range(bx, imm, from_scalar, from_backend_ty); + assume_scalar_range(bx, imm, from_scalar, from_backend_ty, None); imm = match (from_scalar.primitive(), to_scalar.primitive()) { (Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed), @@ -326,8 +375,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, rvalue: &mir::Rvalue<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",); - match *rvalue { mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { let operand = self.codegen_operand(bx, source); @@ -653,8 +700,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = self.monomorphize(ty); let layout = self.cx.layout_of(ty); - // `rvalue_creates_operand` has arranged that we only get here if - // we can build the aggregate immediate from the field immediates. let mut builder = OperandRefBuilder::new(layout); for (field_idx, field) in fields.iter_enumerated() { let op = self.codegen_operand(bx, field); @@ -955,69 +1000,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Pair(val, of) } - - /// Returns `true` if the `rvalue` can be computed into an [`OperandRef`], - /// rather than needing a full `PlaceRef` for the assignment destination. - /// - /// This is used by the [`super::analyze`] code to decide which MIR locals - /// can stay as SSA values (as opposed to generating `alloca` slots for them). - /// As such, some paths here return `true` even where the specific rvalue - /// will not actually take the operand path because the result type is such - /// that it always gets an `alloca`, but where it's not worth re-checking the - /// layout in this code when the right thing will happen anyway. - pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { - match *rvalue { - mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => { - let operand_ty = operand.ty(self.mir, self.cx.tcx()); - let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty)); - let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty)); - match (operand_layout.backend_repr, cast_layout.backend_repr) { - // When the output will be in memory anyway, just use its place - // (instead of the operand path) unless it's the trivial ZST case. - (_, abi::BackendRepr::Memory { .. }) => cast_layout.is_zst(), - - // Otherwise (for a non-memory output) if the input is memory - // then we can just read the value from the place. - (abi::BackendRepr::Memory { .. }, _) => true, - - // When we have scalar immediates, we can only convert things - // where the sizes match, to avoid endianness questions. - (abi::BackendRepr::Scalar(a), abi::BackendRepr::Scalar(b)) => - a.size(self.cx) == b.size(self.cx), - (abi::BackendRepr::ScalarPair(a0, a1), abi::BackendRepr::ScalarPair(b0, b1)) => - a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx), - - // Mixing Scalars and ScalarPairs can get quite complicated when - // padding and undef get involved, so leave that to the memory path. - (abi::BackendRepr::Scalar(_), abi::BackendRepr::ScalarPair(_, _)) | - (abi::BackendRepr::ScalarPair(_, _), abi::BackendRepr::Scalar(_)) => false, - - // SIMD vectors aren't worth the trouble of dealing with complex - // cases like from vectors of f32 to vectors of pointers or - // from fat pointers to vectors of u16. (See #143194 #110021 ...) - (abi::BackendRepr::SimdVector { .. }, _) | (_, abi::BackendRepr::SimdVector { .. }) => false, - } - } - mir::Rvalue::Ref(..) | - mir::Rvalue::CopyForDeref(..) | - mir::Rvalue::RawPtr(..) | - mir::Rvalue::Len(..) | - mir::Rvalue::Cast(..) | // (*) - mir::Rvalue::ShallowInitBox(..) | // (*) - mir::Rvalue::BinaryOp(..) | - mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Discriminant(..) | - mir::Rvalue::NullaryOp(..) | - mir::Rvalue::ThreadLocalRef(_) | - mir::Rvalue::Use(..) | - mir::Rvalue::Repeat(..) | // (*) - mir::Rvalue::Aggregate(..) | // (*) - mir::Rvalue::WrapUnsafeBinder(..) => // (*) - true, - } - - // (*) this is only true if the type is suitable - } } /// Transmutes a single scalar value `imm` from `from_scalar` to `to_scalar`. @@ -1064,7 +1046,7 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // That said, last time we tried removing this, it didn't actually help // the rustc-perf results, so might as well keep doing it // <https://github.com/rust-lang/rust/pull/135610#issuecomment-2599275182> - assume_scalar_range(bx, imm, from_scalar, from_backend_ty); + assume_scalar_range(bx, imm, from_scalar, from_backend_ty, Some(&to_scalar)); imm = match (from_scalar.primitive(), to_scalar.primitive()) { (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), @@ -1092,22 +1074,42 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // since it's never passed to something with parameter metadata (especially // after MIR inlining) so the only way to tell the backend about the // constraint that the `transmute` introduced is to `assume` it. - assume_scalar_range(bx, imm, to_scalar, to_backend_ty); + assume_scalar_range(bx, imm, to_scalar, to_backend_ty, Some(&from_scalar)); imm = bx.to_immediate_scalar(imm, to_scalar); imm } +/// Emits an `assume` call that `imm`'s value is within the known range of `scalar`. +/// +/// If `known` is `Some`, only emits the assume if it's more specific than +/// whatever is already known from the range of *that* scalar. fn assume_scalar_range<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, imm: Bx::Value, scalar: abi::Scalar, backend_ty: Bx::Type, + known: Option<&abi::Scalar>, ) { - if matches!(bx.cx().sess().opts.optimize, OptLevel::No) || scalar.is_always_valid(bx.cx()) { + if matches!(bx.cx().sess().opts.optimize, OptLevel::No) { return; } + match (scalar, known) { + (abi::Scalar::Union { .. }, _) => return, + (_, None) => { + if scalar.is_always_valid(bx.cx()) { + return; + } + } + (abi::Scalar::Initialized { valid_range, .. }, Some(known)) => { + let known_range = known.valid_range(bx.cx()); + if valid_range.contains_range(known_range, scalar.size(bx.cx())) { + return; + } + } + } + match scalar.primitive() { abi::Primitive::Int(..) => { let range = scalar.valid_range(bx.cx()); diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 5e993640472..f391c198e1a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; @@ -14,18 +16,13 @@ pub trait WriteBackendMethods: Clone + 'static { type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; - /// Merge all modules into main_module and returning it - fn run_link( - cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, - modules: Vec<ModuleCodegen<Self::Module>>, - ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations fn run_and_optimize_fat_lto( cgcx: &CodegenContext<Self>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<FatLtoInput<Self>>, - cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, diff_fncs: Vec<AutoDiffItem>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two @@ -33,6 +30,8 @@ pub trait WriteBackendMethods: Clone + 'static { /// can simply be copied over from the incr. comp. cache. fn run_thin_lto( cgcx: &CodegenContext<Self>, + exported_symbols_for_lto: &[String], + each_linked_rlib_for_lto: &[PathBuf], modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError>; diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index aa0bc42d448..2985eafb63a 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -128,15 +128,15 @@ const_eval_frame_note_inner = inside {$where_ -> const_eval_frame_note_last = the failure occurred here +const_eval_incompatible_arg_types = + calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty} + const_eval_incompatible_calling_conventions = calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}" const_eval_incompatible_return_types = calling a function with return type {$callee_ty} passing return place of type {$caller_ty} -const_eval_incompatible_types = - calling a function with argument of type {$callee_ty} passing data of type {$caller_ty} - const_eval_interior_mutable_borrow_escaping = interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed .label = this borrow of an interior mutable value refers to such a temporary diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 0eb6f28bdb3..44e5d1d5ee7 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -714,10 +714,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { self.super_operand(op, location); - if let Operand::Constant(c) = op { - if let Some(def_id) = c.check_static_ptr(self.tcx) { - self.check_static(def_id, self.span); - } + if let Operand::Constant(c) = op + && let Some(def_id) = c.check_static_ptr(self.tcx) + { + self.check_static(def_id, self.span); } } @@ -784,7 +784,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.revalidate_conditional_constness(callee, fn_args, *fn_span); // Attempting to call a trait method? - if let Some(trait_did) = tcx.trait_of_item(callee) { + if let Some(trait_did) = tcx.trait_of_assoc(callee) { // We can't determine the actual callee here, so we have to do different checks // than usual. diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index b2e0577cc82..982e640fa92 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -295,21 +295,18 @@ fn build_error_for_const_call<'tcx>( } let deref = "*".repeat(num_refs); - if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = - call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) - { - let rhs_pos = - span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - sugg = Some(errors::ConsiderDereferencing { - deref, - span: span.shrink_to_lo(), - rhs_span, - }); - } - } + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) + && let Some(eq_idx) = call_str.find("==") + && let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + sugg = Some(errors::ConsiderDereferencing { + deref, + span: span.shrink_to_lo(), + rhs_span, + }); } } _ => {} diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 166491b47a1..faf41f1658b 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -369,7 +369,7 @@ where assert!(promoted.is_none() || Q::ALLOW_PROMOTED); // Don't peek inside trait associated constants. - if promoted.is_none() && cx.tcx.trait_of_item(def).is_none() { + if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() { let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def); if !Q::in_qualifs(&qualifs) { diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 9f7fcc509a5..d98e5027e4d 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -137,14 +137,14 @@ where // If a local with no projections is moved from (e.g. `x` in `y = x`), record that // it no longer needs to be dropped. - if let mir::Operand::Move(place) = operand { - if let Some(local) = place.as_local() { - // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier - // implementation we retain qualif if a local had been borrowed before. This might - // not be strictly necessary since the local is no longer initialized. - if !self.state.borrow.contains(local) { - self.state.qualif.remove(local); - } + if let mir::Operand::Move(place) = operand + && let Some(local) = place.as_local() + { + // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier + // implementation we retain qualif if a local had been borrowed before. This might + // not be strictly necessary since the local is no longer initialized. + if !self.state.borrow.contains(local) { + self.state.qualif.remove(local); } } } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index f584f6c948e..5835660e1c3 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -152,7 +152,7 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>( pub fn mk_eval_cx_for_const_val<'tcx>( tcx: TyCtxtAt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - val: mir::ConstValue<'tcx>, + val: mir::ConstValue, ty: Ty<'tcx>, ) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> { let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, typing_env, CanAccessMutGlobal::No); @@ -172,7 +172,7 @@ pub(super) fn op_to_const<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, op: &OpTy<'tcx>, for_diagnostics: bool, -) -> ConstValue<'tcx> { +) -> ConstValue { // Handle ZST consistently and early. if op.layout.is_zst() { return ConstValue::ZeroSized; @@ -241,10 +241,9 @@ pub(super) fn op_to_const<'tcx>( let (prov, offset) = ptr.into_pointer_or_addr().expect(msg).prov_and_relative_offset(); let alloc_id = prov.alloc_id(); - let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); assert!(offset == abi::Size::ZERO, "{}", msg); let meta = b.to_target_usize(ecx).expect(msg); - ConstValue::Slice { data, meta } + ConstValue::Slice { alloc_id, meta } } Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty), }, @@ -256,7 +255,7 @@ pub(crate) fn turn_into_const_value<'tcx>( tcx: TyCtxt<'tcx>, constant: ConstAlloc<'tcx>, key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>, -) -> ConstValue<'tcx> { +) -> ConstValue { let cid = key.value; let def_id = cid.instance.def.def_id(); let is_static = tcx.is_static(def_id); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 0082f90f3b8..624ca1dd2da 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -28,7 +28,7 @@ const VALTREE_MAX_NODES: usize = 100000; #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( tcx: TyCtxt<'tcx>, - val: mir::ConstValue<'tcx>, + val: mir::ConstValue, ty: Ty<'tcx>, ) -> Option<mir::DestructuredConstant<'tcx>> { let typing_env = ty::TypingEnv::fully_monomorphized(); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 5ab72c853c4..37c6c4a61d8 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -259,7 +259,7 @@ pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, cv: ty::Value<'tcx>, -) -> mir::ConstValue<'tcx> { +) -> mir::ConstValue { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s // (those for constants with type bool, int, uint, float or char). // For all other types we create an `MPlace` and fill that by walking diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b6a64035261..a4148cb145f 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -500,7 +500,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidNichedEnumVariantWritten { .. } => { const_eval_invalid_niched_enum_variant_written } - AbiMismatchArgument { .. } => const_eval_incompatible_types, + AbiMismatchArgument { .. } => const_eval_incompatible_arg_types, AbiMismatchReturn { .. } => const_eval_incompatible_return_types, } } @@ -625,12 +625,16 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("data_size", info.data_size); } InvalidNichedEnumVariantWritten { enum_ty } => { - diag.arg("ty", enum_ty.to_string()); + diag.arg("ty", enum_ty); } - AbiMismatchArgument { caller_ty, callee_ty } - | AbiMismatchReturn { caller_ty, callee_ty } => { - diag.arg("caller_ty", caller_ty.to_string()); - diag.arg("callee_ty", callee_ty.to_string()); + AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => { + diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output + diag.arg("caller_ty", caller_ty); + diag.arg("callee_ty", callee_ty); + } + AbiMismatchReturn { caller_ty, callee_ty } => { + diag.arg("caller_ty", caller_ty); + diag.arg("callee_ty", callee_ty); } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 1503f3bcd99..5b3adba0265 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -270,6 +270,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Item = (&'x FnArg<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>), >, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, + callee_arg_idx: usize, callee_arg: &mir::Place<'tcx>, callee_ty: Ty<'tcx>, already_live: bool, @@ -298,6 +299,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check compatibility if !self.check_argument_compat(caller_abi, callee_abi)? { throw_ub!(AbiMismatchArgument { + arg_idx: callee_arg_idx, caller_ty: caller_abi.layout.ty, callee_ty: callee_abi.layout.ty }); @@ -424,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this is a single iterator (that handles `spread_arg`), then // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter(); + let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); for local in body.args_iter() { // Construct the destination place for this argument. At this point all // locals are still dead, so we cannot construct a `PlaceTy`. @@ -445,10 +447,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)], *self.tcx, ); - let callee_abi = callee_args_abis.next().unwrap(); + let (idx, callee_abi) = callee_args_abis.next().unwrap(); self.pass_argument( &mut caller_args, callee_abi, + idx, &dest, field_ty, /* already_live */ true, @@ -456,10 +459,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } else { // Normal argument. Cannot mark it as live yet, it might be unsized! - let callee_abi = callee_args_abis.next().unwrap(); + let (idx, callee_abi) = callee_args_abis.next().unwrap(); self.pass_argument( &mut caller_args, callee_abi, + idx, &dest, ty, /* already_live */ false, @@ -721,7 +725,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) { let tcx = *self.tcx; - let trait_def_id = tcx.trait_of_item(def_id).unwrap(); + let trait_def_id = tcx.trait_of_assoc(def_id).unwrap(); let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args); let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 11e7706fe60..0a8591ca140 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -442,10 +442,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # First compute the dynamic alignment // Packed type alignment needs to be capped. - if let ty::Adt(def, _) = layout.ty.kind() { - if let Some(packed) = def.repr().pack { - unsized_align = unsized_align.min(packed); - } + if let ty::Adt(def, _) = layout.ty.kind() + && let Some(packed) = def.repr().pack + { + unsized_align = unsized_align.min(packed); } // Choose max of two known alignments (combined value must @@ -582,8 +582,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { span: Span, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { - let const_val = val.eval(*ecx.tcx, ecx.typing_env, span).map_err(|err| { + let const_val = val.eval(*self.tcx, self.typing_env, span).map_err(|err| { if M::ALL_CONSTS_ARE_PRECHECKED { match err { ErrorHandled::TooGeneric(..) => {}, @@ -599,11 +598,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } } - err.emit_note(*ecx.tcx); + err.emit_note(*self.tcx); err })?; - ecx.const_val_to_op(const_val, val.ty(), layout) - }) + self.const_val_to_op(const_val, val.ty(), layout) } #[must_use] diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index e24a355891d..5e3d0a15d8b 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,7 +6,7 @@ use std::assert_matches::assert_matches; use rustc_abi::{FieldIdx, HasDataLayout, Size}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_middle::mir::interpret::{read_target_uint, write_target_uint}; +use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -17,17 +17,18 @@ use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ - Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy, - PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, - interp_ok, throw_inval, throw_ub_custom, throw_ub_format, + AllocId, CheckInAllocMsg, ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Pointer, + PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format, interp_ok, throw_inval, + throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; /// Directly returns an `Allocation` containing an absolute path representation of the given type. -pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { +pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) { let path = crate::util::type_name(tcx, ty); - let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ()); - tcx.mk_const_alloc(alloc) + let bytes = path.into_bytes(); + let len = bytes.len().try_into().unwrap(); + (tcx.allocate_bytes_dedup(bytes, CTFE_ALLOC_SALT), len) } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Generates a value of `TypeId` for `ty` in-place. @@ -126,8 +127,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::type_name => { let tp_ty = instance.args.type_at(0); ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc = alloc_type_name(tcx, tp_ty); - let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }; + let (alloc_id, meta) = alloc_type_name(tcx, tp_ty); + let val = ConstValue::Slice { alloc_id, meta }; let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e981f3973ae..e22629993fb 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -12,7 +12,6 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{mir, ty}; -use rustc_span::Span; use rustc_span::def_id::DefId; use rustc_target::callconv::FnAbi; @@ -587,27 +586,6 @@ pub trait Machine<'tcx>: Sized { interp_ok(()) } - /// Evaluate the given constant. The `eval` function will do all the required evaluation, - /// but this hook has the chance to do some pre/postprocessing. - #[inline(always)] - fn eval_mir_constant<F>( - ecx: &InterpCx<'tcx, Self>, - val: mir::Const<'tcx>, - span: Span, - layout: Option<TyAndLayout<'tcx>>, - eval: F, - ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> - where - F: Fn( - &InterpCx<'tcx, Self>, - mir::Const<'tcx>, - Span, - Option<TyAndLayout<'tcx>>, - ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>, - { - eval(ecx, val, span, layout) - } - /// Returns the salt to be used for a deduplicated global alloation. /// If the allocation is for a function, the instance is provided as well /// (this lets Miri ensure unique addresses for some functions). diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 34297a61648..47bebf5371a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1000,7 +1000,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer<Option<M::Provenance>>, ) -> InterpResult<'tcx, (Ty<'tcx>, u64)> { let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?; - let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else { + let Some(GlobalAlloc::TypeId { ty }) = self.tcx.try_get_global_alloc(alloc_id) else { throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata") }; interp_ok((ty, offset.bytes())) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 62cbbae24a8..21afd082a05 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -836,7 +836,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub(crate) fn const_val_to_op( &self, - val_val: mir::ConstValue<'tcx>, + val_val: mir::ConstValue, ty: Ty<'tcx>, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { @@ -860,9 +860,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(), mir::ConstValue::ZeroSized => Immediate::Uninit, - mir::ConstValue::Slice { data, meta } => { + mir::ConstValue::Slice { alloc_id, meta } => { // This is const data, no mutation allowed. - let alloc_id = self.tcx.reserve_and_set_memory_alloc(data); let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO); Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 693b3782960..ed48f53c310 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -320,10 +320,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // for a coroutine). let var_hir_id = captured_place.get_root_variable(); let node = self.ecx.tcx.hir_node(var_hir_id); - if let hir::Node::Pat(pat) = node { - if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { - name = Some(ident.name); - } + if let hir::Node::Pat(pat) = node + && let hir::PatKind::Binding(_, _, ident, _) = pat.kind + { + name = Some(ident.name); } } } diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index f489b05fbbd..c437934eaab 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -57,7 +57,7 @@ pub(crate) fn const_caller_location_provider( file: Symbol, line: u32, col: u32, -) -> mir::ConstValue<'_> { +) -> mir::ConstValue { trace!("const_caller_location: {}:{}:{}", file, line, col); let mut ecx = mk_eval_cx_to_read_const_val( tcx, diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 881aa679156..4a9551a60cf 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -551,6 +551,11 @@ impl SelfProfilerRef { pub fn get_self_profiler(&self) -> Option<Arc<SelfProfiler>> { self.profiler.clone() } + + /// Is expensive recording of query keys and/or function arguments enabled? + pub fn is_args_recording_enabled(&self) -> bool { + self.enabled() && self.event_filter_mask.intersects(EventFilter::ARGS) + } } /// A helper for recording costly arguments to self-profiling events. Used with diff --git a/compiler/rustc_error_codes/src/error_codes/E0466.md b/compiler/rustc_error_codes/src/error_codes/E0466.md index 7aefedbc087..28016eb395e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0466.md +++ b/compiler/rustc_error_codes/src/error_codes/E0466.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + Macro import declaration was malformed. Erroneous code examples: -```compile_fail,E0466 +```compile_fail #[macro_use(a_macro(another_macro))] // error: invalid import declaration extern crate core as some_crate; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 95400ac2ca3..46a4a186824 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -713,8 +713,7 @@ impl HumanEmitter { Style::LineNumber, ); } - buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); - + self.draw_line_num(buffer, line_index, line_offset, width_offset - 3); self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); left } @@ -2128,11 +2127,11 @@ impl HumanEmitter { // Account for a suggestion to completely remove a line(s) with whitespace (#94192). let line_end = sm.lookup_char_pos(parts[0].span.hi()).line; for line in line_start..=line_end { - buffer.puts( + self.draw_line_num( + &mut buffer, + line, row_num - 1 + line - line_start, - 0, - &self.maybe_anonymized(line), - Style::LineNumber, + max_line_num_len, ); buffer.puts( row_num - 1 + line - line_start, @@ -2612,12 +2611,7 @@ impl HumanEmitter { // For more info: https://github.com/rust-lang/rust/issues/92741 let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1); for (index, line_to_remove) in lines_to_remove.enumerate() { - buffer.puts( - *row_num - 1, - 0, - &self.maybe_anonymized(line_num + index), - Style::LineNumber, - ); + self.draw_line_num(buffer, line_num + index, *row_num - 1, max_line_num_len); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); let line = normalize_whitespace( &file_lines.file.get_line(line_to_remove.line_index).unwrap(), @@ -2634,11 +2628,11 @@ impl HumanEmitter { let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index; let last_line = &file_lines.file.get_line(last_line_index).unwrap(); if last_line != line_to_add { - buffer.puts( + self.draw_line_num( + buffer, + line_num + file_lines.lines.len() - 1, *row_num - 1, - 0, - &self.maybe_anonymized(line_num + file_lines.lines.len() - 1), - Style::LineNumber, + max_line_num_len, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); buffer.puts( @@ -2661,7 +2655,7 @@ impl HumanEmitter { // 2 - .await // | // *row_num -= 1; - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { @@ -2671,7 +2665,7 @@ impl HumanEmitter { *row_num -= 2; } } else if is_multiline { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); match &highlight_parts { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); @@ -2702,11 +2696,11 @@ impl HumanEmitter { Style::NoStyle, ); } else if let DisplaySuggestion::Add = show_code_change { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); self.draw_col_separator(buffer, *row_num, max_line_num_len + 1); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } @@ -3016,6 +3010,22 @@ impl HumanEmitter { OutputTheme::Unicode => "…", } } + + fn draw_line_num( + &self, + buffer: &mut StyledBuffer, + line_num: usize, + line_offset: usize, + max_line_num_len: usize, + ) { + let line_num = self.maybe_anonymized(line_num); + buffer.puts( + line_offset, + max_line_num_len.saturating_sub(str_width(&line_num)), + &line_num, + Style::LineNumber, + ); + } } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs index 579e00b8b85..fe1d80bdbe8 100644 --- a/compiler/rustc_errors/src/markdown/term.rs +++ b/compiler/rustc_errors/src/markdown/term.rs @@ -18,7 +18,7 @@ thread_local! { pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> { #[cfg(not(test))] if let Some((w, _)) = termize::dimensions() { - WIDTH.with(|c| c.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH))); + WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)); } write_stream(stream, buf, None, 0)?; buf.write_all(b"\n") @@ -84,7 +84,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()> reset_cursor(); } MdTree::HorizontalRule => { - (0..WIDTH.with(Cell::get)).for_each(|_| buf.write_all(b"-").unwrap()); + (0..WIDTH.get()).for_each(|_| buf.write_all(b"-").unwrap()); reset_cursor(); } MdTree::Heading(n, stream) => { @@ -121,7 +121,7 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()> /// End of that block, just wrap the line fn reset_cursor() { - CURSOR.with(|cur| cur.set(0)); + CURSOR.set(0); } /// Change to be generic on Write for testing. If we have a link URL, we don't @@ -144,7 +144,7 @@ fn write_wrapping<B: io::Write>( buf.write_all(ind_ws)?; cur.set(indent); } - let ch_count = WIDTH.with(Cell::get) - cur.get(); + let ch_count = WIDTH.get() - cur.get(); let mut iter = to_write.char_indices(); let Some((end_idx, _ch)) = iter.nth(ch_count) else { // Write entire line diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index 790efd0286e..095502e80aa 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -153,12 +153,11 @@ impl StyledBuffer { /// 1. That line and column exist in `StyledBuffer` /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation` fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) { - if let Some(ref mut line) = self.lines.get_mut(line) { - if let Some(StyledChar { style: s, .. }) = line.get_mut(col) { - if overwrite || matches!(s, Style::NoStyle | Style::Quotation) { - *s = style; - } - } + if let Some(ref mut line) = self.lines.get_mut(line) + && let Some(StyledChar { style: s, .. }) = line.get_mut(col) + && (overwrite || matches!(s, Style::NoStyle | Style::Quotation)) + { + *s = style; } } } diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 89d6e62834d..5a53670c865 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -1,26 +1,8 @@ -expand_arg_not_attributes = - second argument must be `attributes` - -expand_attr_no_arguments = - attribute must have either one or two arguments - -expand_attribute_meta_item = - attribute must be a meta item, not a literal - -expand_attribute_single_word = - attribute must only be a single word - expand_attributes_on_expressions_experimental = attributes on expressions are experimental .help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//` .help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !` -expand_attributes_wrong_form = - attribute must be of form: `attributes(foo, bar)` - -expand_cannot_be_name_of_macro = - `{$trait_ident}` cannot be a name of {$macro_type} macro - expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] @@ -71,9 +53,6 @@ expand_glob_delegation_outside_impls = expand_glob_delegation_traitless_qpath = qualified path without a trait in glob delegation -expand_helper_attribute_name_invalid = - `{$name}` cannot be a name of derive helper attribute - expand_incomplete_parse = macro expansion ignores {$descr} and any tokens following .label = caused by the macro expansion here @@ -165,12 +144,6 @@ expand_mve_unrecognized_var = expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable -expand_not_a_meta_item = - not a meta item - -expand_only_one_word = - must only be one word - expand_proc_macro_back_compat = using an old version of `{$crate_name}` .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 1928cfd9048..c01320fc644 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::default::Default; use std::iter; use std::path::Component::Prefix; @@ -361,17 +362,13 @@ where } /// Represents a thing that maps token trees to Macro Results -pub trait TTMacroExpander { +pub trait TTMacroExpander: Any { fn expand<'cx>( &self, ecx: &'cx mut ExtCtxt<'_>, span: Span, input: TokenStream, ) -> MacroExpanderResult<'cx>; - - fn get_unused_rule(&self, _rule_i: usize) -> Option<(&Ident, Span)> { - None - } } pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>; @@ -379,7 +376,7 @@ pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>; pub type MacroExpanderFn = for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>; -impl<F> TTMacroExpander for F +impl<F: 'static> TTMacroExpander for F where F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>, { @@ -864,7 +861,7 @@ impl SyntaxExtension { /// | (unspecified) | no | if-ext | if-ext | yes | /// | external | no | if-ext | if-ext | yes | /// | yes | yes | yes | yes | yes | - fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool { + fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool { let flag = sess.opts.cg.collapse_macro_debuginfo; let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo) .and_then(|attr| { @@ -875,7 +872,7 @@ impl SyntaxExtension { .ok() }) .unwrap_or_else(|| { - if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) { + if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) { CollapseMacroDebuginfo::Yes } else { CollapseMacroDebuginfo::Unspecified @@ -918,16 +915,18 @@ impl SyntaxExtension { let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); - let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro) - .map(|attr| { - // Override `helper_attrs` passed above if it's a built-in macro, - // marking `proc_macro_derive` macros as built-in is not a realistic use case. - parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else( - || (Some(name), Vec::new()), - |(name, helper_attrs)| (Some(name), helper_attrs), - ) - }) - .unwrap_or_else(|| (None, helper_attrs)); + let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs)) + { + // Override `helper_attrs` passed above if it's a built-in macro, + // marking `proc_macro_derive` macros as built-in is not a realistic use case. + Some((Some(name), helper_attrs)) => { + (Some(*name), helper_attrs.iter().copied().collect()) + } + Some((None, _)) => (Some(name), Vec::new()), + + // Not a built-in macro + None => (None, helper_attrs), + }; let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); @@ -1144,7 +1143,7 @@ pub trait ResolverExpand { /// Names of specific methods to which glob delegation expands. fn glob_delegation_suffixes( - &mut self, + &self, trait_def_id: DefId, impl_def_id: LocalDefId, ) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>; @@ -1227,6 +1226,7 @@ pub struct ExtCtxt<'a> { pub(super) expanded_inert_attrs: MarkedAttrs, /// `-Zmacro-stats` data. pub macro_stats: FxHashMap<(Symbol, MacroKind), MacroStat>, + pub nb_macro_errors: usize, } impl<'a> ExtCtxt<'a> { @@ -1257,6 +1257,7 @@ impl<'a> ExtCtxt<'a> { expanded_inert_attrs: MarkedAttrs::new(), buffered_early_lint: vec![], macro_stats: Default::default(), + nb_macro_errors: 0, } } @@ -1318,6 +1319,12 @@ impl<'a> ExtCtxt<'a> { self.current_expansion.id.expansion_cause() } + /// This method increases the internal macro errors count and then call `trace_macros_diag`. + pub fn macro_error_and_trace_macros_diag(&mut self) { + self.nb_macro_errors += 1; + self.trace_macros_diag(); + } + pub fn trace_macros_diag(&mut self) { for (span, notes) in self.expansions.iter() { let mut db = self.dcx().create_note(errors::TraceMacro { span: *span }); @@ -1385,80 +1392,6 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe } } -pub fn parse_macro_name_and_helper_attrs( - dcx: DiagCtxtHandle<'_>, - attr: &impl AttributeExt, - macro_type: &str, -) -> Option<(Symbol, Vec<Symbol>)> { - // Once we've located the `#[proc_macro_derive]` attribute, verify - // that it's of the form `#[proc_macro_derive(Foo)]` or - // `#[proc_macro_derive(Foo, attributes(A, ..))]` - let list = attr.meta_item_list()?; - let ([trait_attr] | [trait_attr, _]) = list.as_slice() else { - dcx.emit_err(errors::AttrNoArguments { span: attr.span() }); - return None; - }; - let Some(trait_attr) = trait_attr.meta_item() else { - dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() }); - return None; - }; - let trait_ident = match trait_attr.ident() { - Some(trait_ident) if trait_attr.is_word() => trait_ident, - _ => { - dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span }); - return None; - } - }; - - if !trait_ident.name.can_be_raw() { - dcx.emit_err(errors::CannotBeNameOfMacro { - span: trait_attr.span, - trait_ident, - macro_type, - }); - } - - let attributes_attr = list.get(1); - let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { - if !attr.has_name(sym::attributes) { - dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() }); - } - attr.meta_item_list() - .unwrap_or_else(|| { - dcx.emit_err(errors::AttributesWrongForm { span: attr.span() }); - &[] - }) - .iter() - .filter_map(|attr| { - let Some(attr) = attr.meta_item() else { - dcx.emit_err(errors::AttributeMetaItem { span: attr.span() }); - return None; - }; - - let ident = match attr.ident() { - Some(ident) if attr.is_word() => ident, - _ => { - dcx.emit_err(errors::AttributeSingleWord { span: attr.span }); - return None; - } - }; - if !ident.name.can_be_raw() { - dcx.emit_err(errors::HelperAttributeNameInvalid { - span: attr.span, - name: ident, - }); - } - - Some(ident.name) - }) - .collect() - } else { - Vec::new() - }; - - Some((trait_ident.name, proc_attrs)) -} - /// If this item looks like a specific enums from `rental`, emit a fatal error. /// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 3ac5d213053..fd1391a554a 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -79,72 +79,6 @@ pub(crate) struct MacroBodyStability { } #[derive(Diagnostic)] -#[diag(expand_attr_no_arguments)] -pub(crate) struct AttrNoArguments { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_not_a_meta_item)] -pub(crate) struct NotAMetaItem { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_only_one_word)] -pub(crate) struct OnlyOneWord { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_cannot_be_name_of_macro)] -pub(crate) struct CannotBeNameOfMacro<'a> { - #[primary_span] - pub span: Span, - pub trait_ident: Ident, - pub macro_type: &'a str, -} - -#[derive(Diagnostic)] -#[diag(expand_arg_not_attributes)] -pub(crate) struct ArgumentNotAttributes { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attributes_wrong_form)] -pub(crate) struct AttributesWrongForm { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attribute_meta_item)] -pub(crate) struct AttributeMetaItem { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attribute_single_word)] -pub(crate) struct AttributeSingleWord { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_helper_attribute_name_invalid)] -pub(crate) struct HelperAttributeNameInvalid { - #[primary_span] - pub span: Span, - pub name: Ident, -} - -#[derive(Diagnostic)] #[diag(expand_feature_removed, code = E0557)] #[note] pub(crate) struct FeatureRemoved<'a> { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 79ec79a2fdf..0517fd0419d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -693,7 +693,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { crate_name: self.cx.ecfg.crate_name, }); - self.cx.trace_macros_diag(); + self.cx.macro_error_and_trace_macros_diag(); guar } @@ -707,7 +707,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ) -> ErrorGuaranteed { let guar = self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path }); - self.cx.trace_macros_diag(); + self.cx.macro_error_and_trace_macros_diag(); guar } @@ -1048,7 +1048,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } annotate_err_with_kind(&mut err, kind, span); let guar = err.emit(); - self.cx.trace_macros_diag(); + self.cx.macro_error_and_trace_macros_diag(); kind.dummy(span, guar) } } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 64be7649775..b54dabbb8e2 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -22,7 +22,7 @@ mod placeholders; mod proc_macro_server; mod stats; -pub use mbe::macro_rules::compile_declarative_macro; +pub use mbe::macro_rules::{MacroRulesMacroExpander, compile_declarative_macro}; pub mod base; pub mod config; pub mod expand; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 3f1fc841ea3..0324057e331 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -299,6 +299,7 @@ enum EofMatcherPositions { } /// Represents the possible results of an attempted parse. +#[derive(Debug)] pub(crate) enum ParseResult<T, F> { /// Parsed successfully. Success(T), diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 2d792355b27..febe6f8b88c 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -128,7 +128,7 @@ pub(super) struct MacroRule { rhs: mbe::TokenTree, } -struct MacroRulesMacroExpander { +pub struct MacroRulesMacroExpander { node_id: NodeId, name: Ident, span: Span, @@ -136,6 +136,14 @@ struct MacroRulesMacroExpander { rules: Vec<MacroRule>, } +impl MacroRulesMacroExpander { + pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> { + // If the rhs contains an invocation like `compile_error!`, don't report it as unused. + let rule = &self.rules[rule_i]; + if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) } + } +} + impl TTMacroExpander for MacroRulesMacroExpander { fn expand<'cx>( &self, @@ -154,12 +162,6 @@ impl TTMacroExpander for MacroRulesMacroExpander { &self.rules, )) } - - fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, Span)> { - // If the rhs contains an invocation like `compile_error!`, don't report it as unused. - let rule = &self.rules[rule_i]; - if has_compile_error_macro(&rule.rhs) { None } else { Some((&self.name, rule.lhs_span)) } - } } struct DummyExpander(ErrorGuaranteed); @@ -278,7 +280,7 @@ fn expand_macro<'cx>( // Retry and emit a better error. let (span, guar) = diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, rules); - cx.trace_macros_diag(); + cx.macro_error_and_trace_macros_diag(); DummyResult::any(span, guar) } } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index ca57c4f3164..3fee9af01b3 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -295,6 +295,10 @@ impl DefKind { } } + pub fn is_assoc(self) -> bool { + matches!(self, DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy) + } + /// This is a "module" in name resolution sense. #[inline] pub fn is_module_like(self) -> bool { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e7898648c2b..4ccc2e5a97c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -148,6 +148,11 @@ impl From<Ident> for LifetimeSyntax { /// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing` /// — there's no way to "elide" these lifetimes. #[derive(Debug, Copy, Clone, HashStable_Generic)] +// Raise the aligement to at least 4 bytes - this is relied on in other parts of the compiler(for pointer tagging): +// https://github.com/rust-lang/rust/blob/ce5fdd7d42aba9a2925692e11af2bd39cf37798a/compiler/rustc_data_structures/src/tagged_ptr.rs#L163 +// Removing this `repr(4)` will cause the compiler to not build on platforms like `m68k` Linux, where the aligement of u32 and usize is only 2. +// Since `repr(align)` may only raise aligement, this has no effect on platforms where the aligement is already sufficient. +#[repr(align(4))] pub struct Lifetime { #[stable_hasher(ignore)] pub hir_id: HirId, @@ -1302,6 +1307,7 @@ impl AttributeExt for Attribute { // FIXME: should not be needed anymore when all attrs are parsed Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span, Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, + Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span, Attribute::Parsed(AttributeKind::MayDangle(span)) => *span, Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span, Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span, @@ -1362,6 +1368,17 @@ impl AttributeExt for Attribute { _ => None, } } + + fn is_proc_macro_attr(&self) -> bool { + matches!( + self, + Attribute::Parsed( + AttributeKind::ProcMacro(..) + | AttributeKind::ProcMacroAttribute(..) + | AttributeKind::ProcMacroDerive { .. } + ) + ) + } } // FIXME(fn_delegation): use function delegation instead of manually forwarding diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 03c026cd6c8..6e63ce31024 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -767,7 +767,10 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), DefKind::Static { .. } => { check_static_inhabited(tcx, def_id); check_static_linkage(tcx, def_id); - res = res.and(wfcheck::check_static_item(tcx, def_id)); + let ty = tcx.type_of(def_id).instantiate_identity(); + res = res.and(wfcheck::check_static_item( + tcx, def_id, ty, /* should_check_for_sync */ true, + )); } DefKind::Const => res = res.and(wfcheck::check_const_item(tcx, def_id)), _ => unreachable!(), @@ -1642,20 +1645,40 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { if def.repr().int.is_none() { let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const)); - let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_)); + let get_disr = |var: &ty::VariantDef| match var.discr { + ty::VariantDiscr::Explicit(disr) => Some(disr), + ty::VariantDiscr::Relative(_) => None, + }; - let has_non_units = def.variants().iter().any(|var| !is_unit(var)); - let disr_units = def.variants().iter().any(|var| is_unit(var) && has_disr(var)); - let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var)); + let non_unit = def.variants().iter().find(|var| !is_unit(var)); + let disr_unit = + def.variants().iter().filter(|var| is_unit(var)).find_map(|var| get_disr(var)); + let disr_non_unit = + def.variants().iter().filter(|var| !is_unit(var)).find_map(|var| get_disr(var)); - if disr_non_unit || (disr_units && has_non_units) { - struct_span_code_err!( + if disr_non_unit.is_some() || (disr_unit.is_some() && non_unit.is_some()) { + let mut err = struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0732, - "`#[repr(inttype)]` must be specified" - ) - .emit(); + "`#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants" + ); + if let Some(disr_non_unit) = disr_non_unit { + err.span_label( + tcx.def_span(disr_non_unit), + "explicit discriminant on non-unit variant specified here", + ); + } else { + err.span_label( + tcx.def_span(disr_unit.unwrap()), + "explicit discriminant specified here", + ); + err.span_label( + tcx.def_span(non_unit.unwrap().def_id), + "non-unit discriminant declared here", + ); + } + err.emit(); } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 14ec82ede1c..a62efed13bc 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1180,12 +1180,13 @@ fn check_item_fn( } #[instrument(level = "debug", skip(tcx))] -pub(super) fn check_static_item( - tcx: TyCtxt<'_>, +pub(crate) fn check_static_item<'tcx>( + tcx: TyCtxt<'tcx>, item_id: LocalDefId, + ty: Ty<'tcx>, + should_check_for_sync: bool, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, item_id, |wfcx| { - let ty = tcx.type_of(item_id).instantiate_identity(); let span = tcx.ty_span(item_id); let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); @@ -1212,9 +1213,9 @@ pub(super) fn check_static_item( } // Ensure that the end result is `Sync` in a non-thread local `static`. - let should_check_for_sync = tcx.static_mutability(item_id.to_def_id()) - == Some(hir::Mutability::Not) + let should_check_for_sync = should_check_for_sync && !is_foreign_item + && tcx.static_mutability(item_id.to_def_id()) == Some(hir::Mutability::Not) && !tcx.is_thread_local_static(item_id.to_def_id()); if should_check_for_sync { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 0728b24eb14..afe79bed851 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -530,13 +530,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .iter() .enumerate() .map(|(i, a)| { - if let hir::TyKind::Infer(()) = a.kind { - if let Some(suggested_ty) = + if let hir::TyKind::Infer(()) = a.kind + && let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) - { - infer_replacements.push((a.span, suggested_ty.to_string())); - return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string()); - } + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string()); } self.lowerer().lower_ty(a) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 902a2e15dff..22fb02714dd 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span}; use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder}; +use crate::check::wfcheck::check_static_item; use crate::errors::TypeofReservedKeywordUsed; use crate::hir_ty_lowering::HirTyLowerer; @@ -217,7 +218,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ "static variable", ) } else { - icx.lower_ty(ty) + let ty = icx.lower_ty(ty); + // MIR relies on references to statics being scalars. + // Verify that here to avoid ill-formed MIR. + // We skip the `Sync` check to avoid cycles for type-alias-impl-trait, + // relying on the fact that non-Sync statics don't ICE the rest of the compiler. + match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) { + Ok(()) => ty, + Err(guar) => Ty::new_error(tcx, guar), + } } } ItemKind::Const(ident, _, ty, body_id) => { @@ -275,7 +284,17 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_fn_def(tcx, def_id.to_def_id(), args) } - ForeignItemKind::Static(t, _, _) => icx.lower_ty(t), + ForeignItemKind::Static(ty, _, _) => { + let ty = icx.lower_ty(ty); + // MIR relies on references to statics being scalars. + // Verify that here to avoid ill-formed MIR. + // We skip the `Sync` check to avoid cycles for type-alias-impl-trait, + // relying on the fact that non-Sync statics don't ICE the rest of the compiler. + match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) { + Ok(()) => ty, + Err(guar) => Ty::new_error(tcx, guar), + } + } ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()), }, diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 2d60c9561a9..835f8e8cdae 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -759,7 +759,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, err: &mut Diag<'_, impl EmissionGuarantee>, ) { - let trait_ = match self.tcx.trait_of_item(self.def_id) { + let trait_ = match self.tcx.trait_of_assoc(self.def_id) { Some(def_id) => def_id, None => return, }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d7a827c649d..7760642d8fb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -617,18 +617,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let Some(const_arg) = constraint.ct() { - if let hir::ConstArgKind::Anon(anon_const) = const_arg.kind { - let ty = alias_term - .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); - let ty = check_assoc_const_binding_type( - self, - constraint.ident, - ty, - constraint.hir_id, - ); - tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); - } + if let Some(const_arg) = constraint.ct() + && let hir::ConstArgKind::Anon(anon_const) = const_arg.kind + { + let ty = alias_term + .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); + let ty = + check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } alias_term diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 287a5532f01..f73442fdebd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -755,7 +755,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let limit = if candidates.len() == 5 { 5 } else { 4 }; for (index, &item) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_method(item).unwrap(); + let impl_ = tcx.impl_of_assoc(item).unwrap(); let note_span = if item.is_local() { Some(tcx.def_span(item)) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 9abae33ffdb..646ff3ca08d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -447,17 +447,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id); - if let Some((_, hir::Node::AssocItemConstraint(constraint))) = parents.next() + if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() && let Some(obj_ty) = constraint.ty() + && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() { - if let Some((_, hir::Node::TraitRef(..))) = parents.next() - && let Some((_, hir::Node::Ty(ty))) = parents.next() + if let Some((_, hir::Node::Ty(ty))) = parents.next() && let hir::TyKind::TraitObject(..) = ty.kind { // Assoc ty bounds aren't permitted inside trait object types. return; } + if trait_ref + .path + .segments + .iter() + .find_map(|seg| { + seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) + }) + .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) + { + // Only consider angle-bracketed args (where we have a `=` to replace with `:`). + return; + } + let lo = if constraint.gen_args.span_ext.is_dummy() { constraint.ident.span } else { diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index bf539dfab42..3fddaee8cef 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -103,16 +103,15 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( // over less-specific types (e.g. `Option<MyStruct<u8>>`) if self.depth >= self.cause_depth { self.cause = Some(error.obligation.cause); - if let hir::TyKind::TraitObject(..) = ty.kind { - if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = + if let hir::TyKind::TraitObject(..) = ty.kind + && let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = self.tcx.def_kind(self.def_id) - { - self.cause = Some(ObligationCause::new( - ty.span, - self.def_id, - ObligationCauseCode::DynCompatible(ty.span), - )); - } + { + self.cause = Some(ObligationCause::new( + ty.span, + self.def_id, + ObligationCauseCode::DynCompatible(ty.span), + )); } self.cause_depth = self.depth } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 759b5d9550c..9406697dfed 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -694,7 +694,7 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { // i.e. changing `Default::default()` to `<() as Default>::default()`. if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Def(DefKind::AssocFn, def_id) = path.res - && self.fcx.tcx.trait_of_item(def_id).is_some() + && self.fcx.tcx.trait_of_assoc(def_id).is_some() && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id) && let self_ty = args.type_at(0) && let Some(vid) = self.fcx.root_vid(self_ty) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index eb8d671c939..719989d5793 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2136,10 +2136,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { - if let Some(rslt) = check_in_progress(el) { - return rslt; - } + if let hir::ExprKind::If(_, _, Some(el)) = expr.kind + && let Some(rslt) = check_in_progress(el) + { + return rslt; } if let hir::ExprKind::Match(_, arms, _) = expr.kind { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index dd6eb73a3a0..2345cdab208 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -615,31 +615,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { - if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { - // Report upto four upvars being captured to reduce the amount error messages - // reported back to the user. - let spans_and_labels = upvars - .iter() - .take(4) - .map(|(var_hir_id, upvar)| { - let var_name = self.tcx.hir_name(*var_hir_id).to_string(); - let msg = format!("`{var_name}` captured here"); - (upvar.span, msg) - }) - .collect::<Vec<_>>(); - - let mut multi_span: MultiSpan = - spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); - for (sp, label) in spans_and_labels { - multi_span.push_span_label(sp, label); - } - err.span_note( - multi_span, - "closures can only be coerced to `fn` types if they do not capture any variables" - ); - return true; + if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) + && let Some(upvars) = self.tcx.upvars_mentioned(*def_id) + { + // Report upto four upvars being captured to reduce the amount error messages + // reported back to the user. + let spans_and_labels = upvars + .iter() + .take(4) + .map(|(var_hir_id, upvar)| { + let var_name = self.tcx.hir_name(*var_hir_id).to_string(); + let msg = format!("`{var_name}` captured here"); + (upvar.span, msg) + }) + .collect::<Vec<_>>(); + + let mut multi_span: MultiSpan = + spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); + for (sp, label) in spans_and_labels { + multi_span.push_span_label(sp, label); } + err.span_note( + multi_span, + "closures can only be coerced to `fn` types if they do not capture any variables", + ); + return true; } false } @@ -1302,8 +1302,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => ".clone()".to_string(), }; + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span).shrink_to_hi(); + diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), + span, "consider using clone here", suggestion, Applicability::MachineApplicable, @@ -1393,7 +1395,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .macro_backtrace() .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..))) { - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr + .span + .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map()) + .unwrap_or(expr.span); let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] @@ -2060,7 +2065,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => sugg.to_string(), }; - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr + .span + .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map()) + .unwrap_or(expr.span); err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders); true } @@ -3006,13 +3014,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns whether the given expression is an `else if`. fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::If(..) = expr.kind { - if let Node::Expr(hir::Expr { - kind: hir::ExprKind::If(_, _, Some(else_expr)), .. - }) = self.tcx.parent_hir_node(expr.hir_id) - { - return else_expr.hir_id == expr.hir_id; - } + if let hir::ExprKind::If(..) = expr.kind + && let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) = + self.tcx.parent_hir_node(expr.hir_id) + { + return else_expr.hir_id == expr.hir_id; } false } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9e324286fa1..b395aa8162c 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -259,6 +259,21 @@ fn typeck_with_inspect<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); + // Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`. + if let None = fcx.infcx.tainted_by_errors() { + for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() { + let obligation = fcx.resolve_vars_if_possible(obligation); + if obligation.has_non_region_infer() { + bug!("unexpected inference variable after writeback: {obligation:?}"); + } + fcx.register_predicate(obligation); + } + fcx.select_obligations_where_possible(|_| {}); + if let None = fcx.infcx.tainted_by_errors() { + fcx.report_ambiguity_errors(); + } + } + fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 4c343bb7c22..8d9f7eaf177 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -669,17 +669,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) { // Disallow calls to the method `drop` defined in the `Drop` trait. - if let Some(trait_def_id) = pick.item.trait_container(self.tcx) { - if let Err(e) = callee::check_legal_trait_for_method_call( + if let Some(trait_def_id) = pick.item.trait_container(self.tcx) + && let Err(e) = callee::check_legal_trait_for_method_call( self.tcx, self.span, Some(self.self_expr.span), self.call_expr.span, trait_def_id, self.body_id.to_def_id(), - ) { - self.set_tainted_by_errors(e); - } + ) + { + self.set_tainted_by_errors(e); } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index dbfa7e6273c..e64af8fb7b3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -264,6 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(within_macro_span, "due to this macro variable"); } self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); + self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_name); err.emit() } diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 9f4ab8ca5d4..560f8ceb55f 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; - let infcx = - tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id)); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .in_hir_typeck() + .build(TypingMode::typeck_for_body(tcx, def_id)); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx)); @@ -165,13 +168,12 @@ impl<'tcx> TypeckRootCtxt<'tcx> { if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) = obligation.predicate.kind().skip_binder() - { // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, // we need to make it into one. - if let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) { - debug!("infer_var_info: {:?}.output = true", vid); - infer_var_info.entry(vid).or_default().output = true; - } + && let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) + { + debug!("infer_var_info: {:?}.output = true", vid); + infer_var_info.entry(vid).or_default().output = true; } } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index be774106abf..df38c3a1214 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1047,7 +1047,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); + lint.note("for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); let diagnostic_msg = format!( "add a dummy let to cause {migrated_variables_concat} to be fully captured" diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index b2497cb0de1..093de950d63 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -227,21 +227,19 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); self.typeck_results.node_args_mut().remove(e.hir_id); - if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) { + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) // Discard the need for a mutable borrow - // Extra adjustment made when indexing causes a drop // of size information - we need to get rid of it // Since this is "after" the other adjustment to be // discarded, we do an extra `pop()` - if let Some(Adjustment { + && let Some(Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), .. }) = a.pop() - { - // So the borrow discard actually happens here - a.pop(); - } + { + // So the borrow discard actually happens here + a.pop(); } } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 5fe795bd23a..ad19cdef4e7 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode: self.typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), @@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index bb9c8850093..21e999b080d 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.next_trait_solver } + fn in_hir_typeck(&self) -> bool { + self.in_hir_typeck + } + fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2d269e320b6..5bbd8a84b7f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -37,10 +37,11 @@ use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; -use crate::infer::region_constraints::UndoLog; +use crate::infer::snapshot::undo_log::UndoLog; use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ - self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, + self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations, + TraitEngine, }; pub mod at; @@ -156,6 +157,12 @@ pub struct InferCtxtInner<'tcx> { /// which may cause types to no longer be considered well-formed. region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, + /// `-Znext-solver`: Successfully proven goals during HIR typeck which + /// reference inference variables and get reproven after writeback. + /// + /// See the documentation of `InferCtxt::in_hir_typeck` for more details. + hir_typeck_potentially_region_dependent_goals: Vec<PredicateObligation<'tcx>>, + /// Caches for opaque type inference. opaque_type_storage: OpaqueTypeStorage<'tcx>, } @@ -173,6 +180,7 @@ impl<'tcx> InferCtxtInner<'tcx> { region_constraint_storage: Some(Default::default()), region_obligations: Default::default(), region_assumptions: Default::default(), + hir_typeck_potentially_region_dependent_goals: Default::default(), opaque_type_storage: Default::default(), } } @@ -244,9 +252,29 @@ pub struct InferCtxt<'tcx> { typing_mode: TypingMode<'tcx>, /// Whether this inference context should care about region obligations in - /// the root universe. Most notably, this is used during hir typeck as region + /// the root universe. Most notably, this is used during HIR typeck as region /// solving is left to borrowck instead. pub considering_regions: bool, + /// `-Znext-solver`: Whether this inference context is used by HIR typeck. If so, we + /// need to make sure we don't rely on region identity in the trait solver or when + /// relating types. This is necessary as borrowck starts by replacing each occurrence of a + /// free region with a unique inference variable. If HIR typeck ends up depending on two + /// regions being equal we'd get unexpected mismatches between HIR typeck and MIR typeck, + /// resulting in an ICE. + /// + /// The trait solver sometimes depends on regions being identical. As a concrete example + /// the trait solver ignores other candidates if one candidate exists without any constraints. + /// The goal `&'a u32: Equals<&'a u32>` has no constraints right now. If we replace each + /// occurrence of `'a` with a unique region the goal now equates these regions. See + /// the tests in trait-system-refactor-initiative#27 for concrete examples. + /// + /// We handle this by *uniquifying* region when canonicalizing root goals during HIR typeck. + /// This is still insufficient as inference variables may *hide* region variables, so e.g. + /// `dyn TwoSuper<?x, ?x>: Super<?x>` may hold but MIR typeck could end up having to prove + /// `dyn TwoSuper<&'0 (), &'1 ()>: Super<&'2 ()>` which is now ambiguous. Because of this we + /// stash all successfully proven goals which reference inference variables and then reprove + /// them after writeback. + pub in_hir_typeck: bool, /// If set, this flag causes us to skip the 'leak check' during /// higher-ranked subtyping operations. This flag is a temporary one used @@ -506,6 +534,7 @@ pub struct TypeOutlivesConstraint<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, considering_regions: bool, + in_hir_typeck: bool, skip_leak_check: bool, /// Whether we should use the new trait solver in the local inference context, /// which affects things like which solver is used in `predicate_may_hold`. @@ -518,6 +547,7 @@ impl<'tcx> TyCtxt<'tcx> { InferCtxtBuilder { tcx: self, considering_regions: true, + in_hir_typeck: false, skip_leak_check: false, next_trait_solver: self.next_trait_solver_globally(), } @@ -535,6 +565,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn in_hir_typeck(mut self) -> Self { + self.in_hir_typeck = true; + self + } + pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self { self.skip_leak_check = skip_leak_check; self @@ -568,12 +603,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> { - let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } = - *self; + let InferCtxtBuilder { + tcx, + considering_regions, + in_hir_typeck, + skip_leak_check, + next_trait_solver, + } = *self; InferCtxt { tcx, typing_mode, considering_regions, + in_hir_typeck, skip_leak_check, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), @@ -978,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn push_hir_typeck_potentially_region_dependent_goal( + &self, + goal: PredicateObligation<'tcx>, + ) { + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushHirTypeckPotentiallyRegionDependentGoal); + inner.hir_typeck_potentially_region_dependent_goals.push(goal); + } + + pub fn take_hir_typeck_potentially_region_dependent_goals( + &self, + ) -> Vec<PredicateObligation<'tcx>> { + assert!(!self.in_snapshot(), "cannot take goals in a snapshot"); + std::mem::take(&mut self.inner.borrow_mut().hir_typeck_potentially_region_dependent_goals) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a8520c0e71d..bb3b51c0ab2 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -177,6 +177,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> { + assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot"); std::mem::take(&mut self.inner.borrow_mut().region_assumptions) } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 40e4c329446..fcc0ab3af41 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -29,6 +29,7 @@ pub(crate) enum UndoLog<'tcx> { ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, PushRegionAssumption, + PushHirTypeckPotentiallyRegionDependentGoal, } macro_rules! impl_from { @@ -79,7 +80,12 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> { assert_matches!(popped, Some(_), "pushed region constraint but could not pop it"); } UndoLog::PushRegionAssumption => { - self.region_assumptions.pop(); + let popped = self.region_assumptions.pop(); + assert_matches!(popped, Some(_), "pushed region assumption but could not pop it"); + } + UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => { + let popped = self.hir_typeck_potentially_region_dependent_goals.pop(); + assert_matches!(popped, Some(_), "pushed goal but could not pop it"); } } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index fb6897c7d89..057fbe2fc4e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -208,6 +208,10 @@ fn configure_and_expand( // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); + if ecx.nb_macro_errors > 0 { + sess.dcx().abort_if_errors(); + } + // The rest is error reporting and stats sess.psess.buffered_lints.with_lock(|buffered_lints: &mut Vec<BufferedEarlyLint>| { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 1a1cfc9fa6f..69fe7fe83ff 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -593,7 +593,7 @@ lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case nam lint_non_fmt_panic = panic message is not a string literal .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 - .more_info_note = for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html> + .more_info_note = for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/panic-macro-consistency.html> .supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here .supports_fmt_suggestion = remove the `format!(..)` macro call .display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 73e68834232..152971c4ed6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1654,7 +1654,7 @@ declare_lint! { "`...` range patterns are deprecated", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>", }; } @@ -1835,7 +1835,7 @@ declare_lint! { "detects edition keywords being used as an identifier", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/gen-keyword.html>", }; } @@ -2446,16 +2446,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { /// Determine if this expression is a "dangerous initialization". fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> { - if let hir::ExprKind::Call(path_expr, args) = expr.kind { + if let hir::ExprKind::Call(path_expr, args) = expr.kind // Find calls to `mem::{uninitialized,zeroed}` methods. - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_zeroed) => return Some(InitKind::Zeroed), - Some(sym::mem_uninitialized) => return Some(InitKind::Uninit), - Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed), - _ => {} - } + && let hir::ExprKind::Path(ref qpath) = path_expr.kind + { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::mem_zeroed) => return Some(InitKind::Zeroed), + Some(sym::mem_uninitialized) => return Some(InitKind::Uninit), + Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed), + _ => {} } } else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind { // Find problematic calls to `MaybeUninit::assume_init`. @@ -2463,14 +2463,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { // This is a call to *some* method named `assume_init`. // See if the `self` parameter is one of the dangerous constructors. - if let hir::ExprKind::Call(path_expr, _) = receiver.kind { - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed), - Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit), - _ => {} - } + if let hir::ExprKind::Call(path_expr, _) = receiver.kind + && let hir::ExprKind::Path(ref qpath) = path_expr.kind + { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed), + Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit), + _ => {} } } } @@ -2724,13 +2724,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { } // check for call to `core::ptr::null` or `core::ptr::null_mut` hir::ExprKind::Call(path, _) => { - if let hir::ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { - return matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_null | sym::ptr_null_mut) - ); - } + if let hir::ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + { + return matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_null | sym::ptr_null_mut) + ); } } _ => {} @@ -2870,7 +2870,7 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { if let hir::Expr { kind: hir::ExprKind::InlineAsm(hir::InlineAsm { - asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm, + asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm), template_strs, options, .. @@ -2878,6 +2878,15 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { .. } = expr { + // Non-generic naked functions are allowed to define arbitrary + // labels. + if *asm_macro == AsmMacro::NakedAsm { + let def_id = expr.hir_id.owner.def_id; + if !cx.tcx.generics_of(def_id).requires_monomorphization(cx.tcx) { + return; + } + } + // asm with `options(raw)` does not do replacement with `{` and `}`. let raw = options.contains(InlineAsmOptions::RAW); diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 263ea6fa070..ff67ed1bc55 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -87,7 +87,7 @@ declare_lint! { rewriting in `match` is an option to preserve the semantics up to Edition 2021", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html>", }; } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index c17281deff4..b9afb62cf1c 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -71,7 +71,7 @@ declare_lint! { "`impl Trait` will capture more lifetimes than possibly intended in edition 2024", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/rpit-lifetime-capture.html>", }; } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index d8fc46aa9ab..7dafcc199a3 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -411,22 +411,21 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]); impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind { - if let Some(last) = lint_pass.path.segments.last() { - if last.ident.name == sym::LintPass { - let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); - let call_site = expn_data.call_site; - if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) - && call_site.ctxt().outer_expn_data().kind - != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) - { - cx.emit_span_lint( - LINT_PASS_IMPL_WITHOUT_MACRO, - lint_pass.path.span, - LintPassByHand, - ); - } - } + if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind + && let Some(last) = lint_pass.path.segments.last() + && last.ident.name == sym::LintPass + { + let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); + let call_site = expn_data.call_site; + if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) + && call_site.ctxt().outer_expn_data().kind + != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) + { + cx.emit_span_lint( + LINT_PASS_IMPL_WITHOUT_MACRO, + lint_pass.path.span, + LintPassByHand, + ); } } } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index c681deea779..ccfba715a1b 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -356,7 +356,16 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( let store = unerased_lint_store(tcx.sess); if store.late_module_passes.is_empty() { - late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); + // If all builtin lints can be skipped, there is no point in running `late_lint_mod_inner` + // at all. This happens often for dependencies built with `--cap-lints=allow`. + let dont_need_to_run = tcx.lints_that_dont_need_to_run(()); + let can_skip_lints = builtin_lints + .get_lints() + .iter() + .all(|lint| dont_need_to_run.contains(&LintId::of(lint))); + if !can_skip_lints { + late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); + } } else { let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>; let mut binding = store diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 16eeb89207b..ac47897b568 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -933,6 +933,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { let feature = if let Some(feature) = lint_id.lint.feature_gate && !self.features.enabled(feature) + && !span.allows_unstable(feature) { // Lint is behind a feature that is not enabled; eventually return false. feature diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs index ce280fef8b6..7de6fbd941b 100644 --- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs +++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs @@ -65,7 +65,7 @@ declare_lint! { /// to ensure the macros implement the desired behavior. /// /// [editions]: https://doc.rust-lang.org/edition-guide/ - /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html + /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER, Allow, @@ -73,7 +73,7 @@ declare_lint! { To keep the existing behavior, use the `expr_2021` fragment specifier.", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>", + reference: "Migration Guide <https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html>", }; } diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index af509cb786d..34210137bde 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -43,56 +43,50 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { return; } - if let StmtKind::Semi(expr) = stmt.kind { - if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind { - if path.ident.name.as_str() == "map" { - if receiver.span.from_expansion() - || args.iter().any(|e| e.span.from_expansion()) - || !is_impl_slice(cx, receiver) - || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap") - { - return; + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(path, receiver, args, span) = expr.kind + { + if path.ident.name.as_str() == "map" { + if receiver.span.from_expansion() + || args.iter().any(|e| e.span.from_expansion()) + || !is_impl_slice(cx, receiver) + || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap") + { + return; + } + let arg_ty = cx.typeck_results().expr_ty(&args[0]); + let default_span = args[0].span; + if let ty::FnDef(id, _) = arg_ty.kind() { + let fn_ty = cx.tcx.fn_sig(id).skip_binder(); + let ret_ty = fn_ty.output().skip_binder(); + if is_unit_type(ret_ty) { + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), + argument_label: args[0].span, + map_label: span, + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - let default_span = args[0].span; - if let ty::FnDef(id, _) = arg_ty.kind() { - let fn_ty = cx.tcx.fn_sig(id).skip_binder(); - let ret_ty = fn_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx - .tcx - .span_of_impl(*id) - .unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } - } else if let ty::Closure(id, subs) = arg_ty.kind() { - let cl_ty = subs.as_closure().sig(); - let ret_ty = cl_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx - .tcx - .span_of_impl(*id) - .unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } + } else if let ty::Closure(id, subs) = arg_ty.kind() { + let cl_ty = subs.as_closure().sig(); + let ret_ty = cl_ty.output().skip_binder(); + if is_unit_type(ret_ty) { + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), + argument_label: args[0].span, + map_label: span, + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } } } @@ -101,10 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { } fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if let Some(impl_id) = cx.tcx.impl_of_method(method_id) { - return cx.tcx.type_of(impl_id).skip_binder().is_slice(); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) + { + return cx.tcx.type_of(impl_id).skip_binder().is_slice(); } false } @@ -114,11 +108,11 @@ fn is_unit_type(ty: Ty<'_>) -> bool { } fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool { - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) { - if let Some(item) = cx.tcx.get_diagnostic_name(def_id) { - if item.as_str() == name { - return true; - } + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) + && let Some(item) = cx.tcx.get_diagnostic_name(def_id) + { + if item.as_str() == name { + return true; } } false diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 16c06100808..2eabeeaa88f 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -48,39 +48,39 @@ declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]); impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(f, [arg]) = &expr.kind { - if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() { - let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id); + if let hir::ExprKind::Call(f, [arg]) = &expr.kind + && let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() + { + let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id); - if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic) - || cx.tcx.is_lang_item(def_id, LangItem::Panic) - || f_diagnostic_name == Some(sym::panic_str_2015) - { - if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { - if matches!( - cx.tcx.get_diagnostic_name(id), - Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro) - ) { - check_panic(cx, f, arg); - } - } - } else if f_diagnostic_name == Some(sym::unreachable_display) { - if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { - if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) { - check_panic( - cx, - f, - // This is safe because we checked above that the callee is indeed - // unreachable_display - match &arg.kind { - // Get the borrowed arg not the borrow - hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg, - _ => bug!("call to unreachable_display without borrow"), - }, - ); - } + if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic) + || cx.tcx.is_lang_item(def_id, LangItem::Panic) + || f_diagnostic_name == Some(sym::panic_str_2015) + { + if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { + if matches!( + cx.tcx.get_diagnostic_name(id), + Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro) + ) { + check_panic(cx, f, arg); } } + } else if f_diagnostic_name == Some(sym::unreachable_display) { + if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id + && cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) + { + check_panic( + cx, + f, + // This is safe because we checked above that the callee is indeed + // unreachable_display + match &arg.kind { + // Get the borrowed arg not the borrow + hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg, + _ => bug!("call to unreachable_display without borrow"), + }, + ); + } } } } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index db89396d1dc..76e374deef6 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -623,15 +623,15 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { .. }) = p.kind { - if let Res::Def(DefKind::Const, _) = path.res { - if let [segment] = path.segments { - NonUpperCaseGlobals::check_upper_case( - cx, - "constant in pattern", - None, - &segment.ident, - ); - } + if let Res::Def(DefKind::Const, _) = path.res + && let [segment] = path.segments + { + NonUpperCaseGlobals::check_upper_case( + cx, + "constant in pattern", + None, + &segment.ident, + ); } } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index b7835e6c36a..24682c4562a 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { return; }; - let Some(trait_id) = cx.tcx.trait_of_item(did) else { return }; + let Some(trait_id) = cx.tcx.trait_of_assoc(did) else { return }; let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return }; diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index d3b3b55dd4c..a3cf3d568b1 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -24,7 +24,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { + if let Some(impl_did) = cx.tcx.impl_of_assoc(ty.hir_id.owner.to_def_id()) { if cx.tcx.impl_trait_ref(impl_did).is_some() { return; } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index affea1b80ec..191eb721b34 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -92,7 +92,7 @@ macro_rules! expand_combined_late_lint_pass_methods { /// Combines multiple lints passes into a single lint pass, at compile time, /// for maximum speed. Each `check_foo` method in `$methods` within this pass /// simply calls `check_foo` once per `$pass`. Compare with -/// `LateLintPassObjects`, which is similar, but combines lint passes at +/// `RuntimeCombinedLateLintPass`, which is similar, but combines lint passes at /// runtime. #[macro_export] macro_rules! declare_combined_late_lint_pass { @@ -123,10 +123,10 @@ macro_rules! declare_combined_late_lint_pass { #[allow(rustc::lint_pass_impl_without_macro)] impl $crate::LintPass for $name { fn name(&self) -> &'static str { - panic!() + stringify!($name) } fn get_lints(&self) -> LintVec { - panic!() + $name::get_lints() } } ) diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index 00fa0499556..d296ae46f43 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -32,7 +32,7 @@ declare_lint! { "detects calling `into_iter` on arrays in Rust 2015 and 2018", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html>", }; } @@ -61,7 +61,7 @@ declare_lint! { "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>" + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/intoiterator-box-slice.html>" }; } diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 4dda3c7951b..16e1fb0192b 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -54,7 +54,7 @@ declare_lint! { "creating a shared reference to mutable static", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>", explain_reason: false, }; @edition Edition2024 => Deny; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index fc9d795cb23..b0afc333ebe 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1904,7 +1904,7 @@ impl InvalidAtomicOrdering { if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind && recognized_names.contains(&method_path.ident.name) && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id) + && let Some(impl_did) = cx.tcx.impl_of_assoc(m_def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() // skip extension traits, only lint functions from the standard library && cx.tcx.trait_id_of_impl(impl_did).is_none() diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a9eb1739f7f..00e40b515a3 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut op_warned = false; if let Some(must_use_op) = must_use_op { - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); cx.emit_span_lint( UNUSED_MUST_USE, expr.span, @@ -511,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Def(span, def_id, reason) => { - let span = span.find_oldest_ancestor_in_same_ctxt(); + let span = span.find_ancestor_not_from_macro().unwrap_or(*span); cx.emit_span_lint( UNUSED_MUST_USE, span, @@ -562,20 +562,19 @@ declare_lint_pass!(PathStatements => [PATH_STATEMENTS]); impl<'tcx> LateLintPass<'tcx> for PathStatements { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - if let hir::StmtKind::Semi(expr) = s.kind { - if let hir::ExprKind::Path(_) = expr.kind { - let ty = cx.typeck_results().expr_ty(expr); - if ty.needs_drop(cx.tcx, cx.typing_env()) { - let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) - { - PathStatementDropSub::Suggestion { span: s.span, snippet } - } else { - PathStatementDropSub::Help { span: s.span } - }; - cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) + if let hir::StmtKind::Semi(expr) = s.kind + && let hir::ExprKind::Path(_) = expr.kind + { + let ty = cx.typeck_results().expr_ty(expr); + if ty.needs_drop(cx.tcx, cx.typing_env()) { + let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { + PathStatementDropSub::Suggestion { span: s.span, snippet } } else { - cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); - } + PathStatementDropSub::Help { span: s.span } + }; + cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) + } else { + cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); } } } @@ -1340,7 +1339,15 @@ impl EarlyLintPass for UnusedParens { self.with_self_ty_parens = false; } ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => { - self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound); + // If this type itself appears in no-bounds position, we propagate its + // potentially tighter constraint or risk a false posive (issue 143653). + let own_constraint = self.in_no_bounds_pos.get(&ty.id); + let constraint = match own_constraint { + Some(NoBoundsException::None) => NoBoundsException::None, + Some(NoBoundsException::OneBound) => NoBoundsException::OneBound, + None => NoBoundsException::OneBound, + }; + self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint); } ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => { for i in 0..bounds.len() { @@ -1509,21 +1516,19 @@ impl UnusedDelimLint for UnusedBraces { // let _: A<{produces_literal!()}>; // ``` // FIXME(const_generics): handle paths when #67075 is fixed. - if let [stmt] = inner.stmts.as_slice() { - if let ast::StmtKind::Expr(ref expr) = stmt.kind { - if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block) - && (ctx != UnusedDelimsCtx::AnonConst - || (matches!(expr.kind, ast::ExprKind::Lit(_)) - && !expr.span.from_expansion())) - && ctx != UnusedDelimsCtx::ClosureBody - && !cx.sess().source_map().is_multiline(value.span) - && value.attrs.is_empty() - && !value.span.from_expansion() - && !inner.span.from_expansion() - { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) - } - } + if let [stmt] = inner.stmts.as_slice() + && let ast::StmtKind::Expr(ref expr) = stmt.kind + && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block) + && (ctx != UnusedDelimsCtx::AnonConst + || (matches!(expr.kind, ast::ExprKind::Lit(_)) + && !expr.span.from_expansion())) + && ctx != UnusedDelimsCtx::ClosureBody + && !cx.sess().source_map().is_multiline(value.span) + && value.attrs.is_empty() + && !value.span.from_expansion() + && !inner.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } ast::ExprKind::Let(_, ref expr, _, _) => { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a08d68e2d15..b1edb5c3044 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1814,7 +1814,7 @@ declare_lint! { "suggest using `dyn Trait` for trait objects", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>", }; } @@ -2472,7 +2472,7 @@ declare_lint! { "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>", explain_reason: false }; @edition Edition2024 => Warn; @@ -3445,7 +3445,7 @@ declare_lint! { "detects usage of old versions of or-patterns", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/or-patterns-macro-rules.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html>", }; } @@ -3494,7 +3494,7 @@ declare_lint! { prelude in future editions", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/prelude.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/prelude.html>", }; } @@ -3534,7 +3534,7 @@ declare_lint! { prelude in future editions", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/prelude.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/prelude.html>", }; } @@ -3571,7 +3571,7 @@ declare_lint! { "identifiers that will be parsed as a prefix in Rust 2021", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/reserving-syntax.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html>", }; crate_level_only } @@ -4100,7 +4100,7 @@ declare_lint! { "never type fallback affecting unsafe function calls", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>", report_in_deps: true, }; @edition Edition2024 => Deny; @@ -4155,7 +4155,7 @@ declare_lint! { "never type fallback affecting unsafe function calls", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionAndFutureReleaseError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/never-type-fallback.html>", report_in_deps: true, }; report_in_external_macro @@ -4740,7 +4740,7 @@ declare_lint! { "detects unsafe functions being used as safe functions", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/newly-unsafe-functions.html>", }; } @@ -4776,7 +4776,7 @@ declare_lint! { "detects missing unsafe keyword on extern declarations", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-extern.html>", }; } @@ -4817,7 +4817,7 @@ declare_lint! { "detects unsafe attributes outside of unsafe", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-attributes.html>", }; } @@ -5014,7 +5014,7 @@ declare_lint! { "Detect and warn on significant change in drop order in tail expression location", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-tail-expr-scope.html>", }; } @@ -5053,7 +5053,7 @@ declare_lint! { "will be parsed as a guarded string in Rust 2024", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html>", + reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/reserved-syntax.html>", }; crate_level_only } diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 39de4783238..85a2a9c09f0 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,8 +10,8 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index a2e4d7306cb..8c34052770e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1650,40 +1650,6 @@ extern "C" LLVMModuleRef LLVMRustParseBitcodeForLTO(LLVMContextRef Context, return wrap(std::move(*SrcOrError).release()); } -// Find a section of an object file by name. Fail if the section is missing or -// empty. -extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data, - size_t len, - const char *name, - size_t name_len, - size_t *out_len) { - *out_len = 0; - auto Name = StringRef(name, name_len); - auto Data = StringRef(data, len); - auto Buffer = MemoryBufferRef(Data, ""); // The id is unused. - file_magic Type = identify_magic(Buffer.getBuffer()); - Expected<std::unique_ptr<object::ObjectFile>> ObjFileOrError = - object::ObjectFile::createObjectFile(Buffer, Type); - if (!ObjFileOrError) { - LLVMRustSetLastError(toString(ObjFileOrError.takeError()).c_str()); - return nullptr; - } - for (const object::SectionRef &Sec : (*ObjFileOrError)->sections()) { - Expected<StringRef> SecName = Sec.getName(); - if (SecName && *SecName == Name) { - Expected<StringRef> SectionOrError = Sec.getContents(); - if (!SectionOrError) { - LLVMRustSetLastError(toString(SectionOrError.takeError()).c_str()); - return nullptr; - } - *out_len = SectionOrError->size(); - return SectionOrError->data(); - } - } - LLVMRustSetLastError("could not find requested section"); - return nullptr; -} - // Computes the LTO cache key for the provided 'ModId' in the given 'Data', // storing the result in 'KeyOut'. // Currently, this cache key is a SHA-1 hash of anything that could affect diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 82568ed4ae1..c9814beedd6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1610,7 +1610,7 @@ extern "C" void LLVMRustPositionBefore(LLVMBuilderRef B, LLVMValueRef Instr) { extern "C" void LLVMRustPositionAfter(LLVMBuilderRef B, LLVMValueRef Instr) { if (auto I = dyn_cast<Instruction>(unwrap<Value>(Instr))) { - auto J = I->getNextNonDebugInstruction(); + auto J = I->getNextNode(); unwrap(B)->SetInsertPoint(J); } } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 3bef5ca114b..4d3e879a098 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -330,3 +330,6 @@ metadata_wasm_import_form = metadata_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind + +metadata_raw_dylib_malformed = + link name must be well-formed if link kind is `raw-dylib` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 438eff33054..6bfb3769f24 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; +use rustc_data_structures::unord::UnordMap; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; use rustc_hir as hir; @@ -69,6 +70,9 @@ pub struct CStore { /// This crate has a `#[alloc_error_handler]` item. has_alloc_error_handler: bool, + /// Names that were used to load the crates via `extern crate` or paths. + resolved_externs: UnordMap<Symbol, CrateNum>, + /// Unused externs of the crate unused_externs: Vec<Symbol>, @@ -249,6 +253,22 @@ impl CStore { self.metas[cnum] = Some(Box::new(data)); } + /// Save the name used to resolve the extern crate in the local crate + /// + /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name. + /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`. + pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) { + self.resolved_externs.insert(name, extern_crate); + } + + /// Crate resolved and loaded via the given extern name + /// (corresponds to names in `sess.opts.externs`) + /// + /// May be `None` if the crate wasn't used + pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option<CrateNum> { + self.resolved_externs.get(&externs_name).copied() + } + pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> { self.metas .iter_enumerated() @@ -475,6 +495,7 @@ impl CStore { alloc_error_handler_kind: None, has_global_allocator: false, has_alloc_error_handler: false, + resolved_externs: UnordMap::default(), unused_externs: Vec::new(), used_extern_options: Default::default(), } @@ -511,7 +532,7 @@ impl CStore { // We're also sure to compare *paths*, not actual byte slices. The // `source` stores paths which are normalized which may be different // from the strings on the command line. - let source = self.get_crate_data(cnum).cdata.source(); + let source = data.source(); if let Some(entry) = externs.get(name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { @@ -1308,6 +1329,7 @@ impl CStore { let path_len = definitions.def_path(def_id).data.len(); self.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Extern(def_id.to_def_id()), span: item.span, @@ -1332,6 +1354,7 @@ impl CStore { self.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Path, span, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 4a3b43167cf..0332dba1077 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -815,3 +815,10 @@ pub struct AsyncDropTypesInDependency { pub extern_crate: Symbol, pub local_crate: Symbol, } + +#[derive(Diagnostic)] +#[diag(metadata_raw_dylib_malformed)] +pub struct RawDylibMalformed { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c30cfd1fcf7..9fef22f9558 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -370,12 +370,11 @@ impl<'a> CrateLocator<'a> { return self.find_commandline_library(crate_rejections); } let mut seen_paths = FxHashSet::default(); - if let Some(extra_filename) = self.extra_filename { - if let library @ Some(_) = + if let Some(extra_filename) = self.extra_filename + && let library @ Some(_) = self.find_library_crate(crate_rejections, extra_filename, &mut seen_paths)? - { - return Ok(library); - } + { + return Ok(library); } self.find_library_crate(crate_rejections, "", &mut seen_paths) } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 4d276f814ef..ed0f084ea83 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -700,8 +700,21 @@ impl<'tcx> Collector<'tcx> { .link_ordinal .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); + let name = codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)); + + if self.tcx.sess.target.binary_format == BinaryFormat::Elf { + let name = name.as_str(); + if name.contains('\0') { + self.tcx.dcx().emit_err(errors::RawDylibMalformed { span }); + } else if let Some((left, right)) = name.split_once('@') + && (left.is_empty() || right.is_empty() || right.contains('@')) + { + self.tcx.dcx().emit_err(errors::RawDylibMalformed { span }); + } + } + DllImport { - name: codegen_fn_attrs.link_name.unwrap_or_else(|| self.tcx.item_name(item)), + name, import_name_type, calling_convention, span, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e6aedc61338..00c97a2f738 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1937,9 +1937,13 @@ impl CrateMetadata { self.root.decode_target_modifiers(&self.blob).collect() } - pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool { + /// Keep `new_extern_crate` if it looks better in diagnostics + pub(crate) fn update_extern_crate_diagnostics( + &mut self, + new_extern_crate: ExternCrate, + ) -> bool { let update = - Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); + self.extern_crate.as_ref().is_none_or(|old| old.rank() < new_extern_crate.rank()); if update { self.extern_crate = Some(new_extern_crate); } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 57a672c45f7..9415e420eed 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -627,14 +627,37 @@ impl CStore { } } - pub(crate) fn update_extern_crate(&mut self, cnum: CrateNum, extern_crate: ExternCrate) { + /// Track how an extern crate has been loaded. Called after resolving an import in the local crate. + /// + /// * the `name` is for [`Self::set_resolved_extern_crate_name`] saving `--extern name=` + /// * `extern_crate` is for diagnostics + pub(crate) fn update_extern_crate( + &mut self, + cnum: CrateNum, + name: Symbol, + extern_crate: ExternCrate, + ) { + debug_assert_eq!( + extern_crate.dependency_of, LOCAL_CRATE, + "this function should not be called on transitive dependencies" + ); + self.set_resolved_extern_crate_name(name, cnum); + self.update_transitive_extern_crate_diagnostics(cnum, extern_crate); + } + + /// `CrateMetadata` uses `ExternCrate` only for diagnostics + fn update_transitive_extern_crate_diagnostics( + &mut self, + cnum: CrateNum, + extern_crate: ExternCrate, + ) { let cmeta = self.get_crate_data_mut(cnum); - if cmeta.update_extern_crate(extern_crate) { + if cmeta.update_extern_crate_diagnostics(extern_crate) { // Propagate the extern crate info to dependencies if it was updated. let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate }; let dependencies = mem::take(&mut cmeta.dependencies); for &dep_cnum in &dependencies { - self.update_extern_crate(dep_cnum, extern_crate); + self.update_transitive_extern_crate_diagnostics(dep_cnum, extern_crate); } self.get_crate_data_mut(cnum).dependencies = dependencies; } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5cd98038fc6..4075bee707c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; -use rustc_attr_data_structures::EncodeCrossCrate; +use rustc_attr_data_structures::{AttributeKind, EncodeCrossCrate, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{join, par_for_each_in}; @@ -1965,18 +1965,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = tcx.hir_attrs(proc_macro); - let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { + let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) { MacroKind::Bang - } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { + } else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) { MacroKind::Attr - } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) { - // This unwrap chain should have been checked by the proc-macro harness. - name = attr.meta_item_list().unwrap()[0] - .meta_item() - .unwrap() - .ident() - .unwrap() - .name; + } else if let Some(trait_name) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, ..} => trait_name) + { + name = *trait_name; MacroKind::Derive } else { bug!("Unknown proc-macro type for item {:?}", id); @@ -2124,11 +2119,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; let def_id = id.owner_id.to_def_id(); - self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id)); - if of_trait && let Some(header) = tcx.impl_trait_header(def_id) { record!(self.tables.impl_trait_header[def_id] <- header); + self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id)); + let trait_ref = header.trait_ref.instantiate_identity(); let simplified_self_ty = fast_reject::simplify_type( self.tcx, @@ -2141,10 +2136,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .push((id.owner_id.def_id.local_def_index, simplified_self_ty)); let trait_def = tcx.trait_def(trait_ref.def_id); - if let Ok(mut an) = trait_def.ancestors(tcx, def_id) { - if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); - } + if let Ok(mut an) = trait_def.ancestors(tcx, def_id) + && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) + { + self.tables.impl_parent.set_some(def_id.index, parent.into()); } // if this is an impl of `CoerceUnsized`, create its diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index c5ce6efcb81..9d2f0a45237 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -50,10 +50,10 @@ macro_rules! declare_hooks { declare_hooks! { /// Tries to destructure an `mir::Const` ADT or array into its variant index /// and its field values. This should only be used for pretty printing. - hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue<'tcx>, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>; + hook try_destructure_mir_constant_for_user_output(val: mir::ConstValue, ty: Ty<'tcx>) -> Option<mir::DestructuredConstant<'tcx>>; /// Getting a &core::panic::Location referring to a span. - hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>; + hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue; /// Returns `true` if this def is a function-like thing that is eligible for /// coverage instrumentation under `-Cinstrument-coverage`. diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 03549091d62..785ddd1ee29 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -182,7 +182,7 @@ impl EffectiveVisibilities { // don't check this condition for them. let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. }); let is_associated_item_in_trait_impl = tcx - .impl_of_method(def_id.to_def_id()) + .impl_of_assoc(def_id.to_def_id()) .and_then(|impl_id| tcx.trait_id_of_impl(impl_id)) .is_some(); if !is_impl && !is_associated_item_in_trait_impl { diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 0f5b63f5c1d..800c1af660a 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -175,23 +175,22 @@ impl Scope { return DUMMY_SP; }; let span = tcx.hir_span(hir_id); - if let ScopeData::Remainder(first_statement_index) = self.data { - if let Node::Block(blk) = tcx.hir_node(hir_id) { - // Want span for scope starting after the - // indexed statement and ending at end of - // `blk`; reuse span of `blk` and shift `lo` - // forward to end of indexed statement. - // - // (This is the special case alluded to in the - // doc-comment for this method) - - let stmt_span = blk.stmts[first_statement_index.index()].span; - - // To avoid issues with macro-generated spans, the span - // of the statement must be nested in that of the block. - if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { - return span.with_lo(stmt_span.lo()); - } + if let ScopeData::Remainder(first_statement_index) = self.data + // Want span for scope starting after the + // indexed statement and ending at end of + // `blk`; reuse span of `blk` and shift `lo` + // forward to end of indexed statement. + // + // (This is the special case alluded to in the + // doc-comment for this method) + && let Node::Block(blk) = tcx.hir_node(hir_id) + { + let stmt_span = blk.stmts[first_statement_index.index()].span; + + // To avoid issues with macro-generated spans, the span + // of the statement must be nested in that of the block. + if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { + return span.with_lo(stmt_span.lo()); } } span diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index fb941977528..96131d47a17 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -9,9 +9,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::TypeVisitableExt; use super::interpret::ReportedErrorInfo; -use crate::mir::interpret::{ - AllocId, AllocRange, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar, alloc_range, -}; +use crate::mir::interpret::{AllocId, AllocRange, ErrorHandled, GlobalAlloc, Scalar, alloc_range}; use crate::mir::{Promoted, pretty_print_const_value}; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt}; @@ -33,8 +31,8 @@ pub struct ConstAlloc<'tcx> { /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for /// array length computations, enum discriminants and the pattern matching logic. #[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)] -#[derive(HashStable, Lift)] -pub enum ConstValue<'tcx> { +#[derive(HashStable)] +pub enum ConstValue { /// Used for types with `layout::abi::Scalar` ABI. /// /// Not using the enum `Value` to encode that this must not be `Uninit`. @@ -52,7 +50,7 @@ pub enum ConstValue<'tcx> { Slice { /// The allocation storing the slice contents. /// This always points to the beginning of the allocation. - data: ConstAllocation<'tcx>, + alloc_id: AllocId, /// The metadata field of the reference. /// This is a "target usize", so we use `u64` as in the interpreter. meta: u64, @@ -75,9 +73,9 @@ pub enum ConstValue<'tcx> { } #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(ConstValue<'_>, 24); +rustc_data_structures::static_assert_size!(ConstValue, 24); -impl<'tcx> ConstValue<'tcx> { +impl ConstValue { #[inline] pub fn try_to_scalar(&self) -> Option<Scalar> { match *self { @@ -98,11 +96,11 @@ impl<'tcx> ConstValue<'tcx> { self.try_to_scalar_int()?.try_into().ok() } - pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> { + pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Option<u64> { Some(self.try_to_scalar_int()?.to_target_usize(tcx)) } - pub fn try_to_bits_for_ty( + pub fn try_to_bits_for_ty<'tcx>( &self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, @@ -132,12 +130,15 @@ impl<'tcx> ConstValue<'tcx> { } /// Must only be called on constants of type `&str` or `&[u8]`! - pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> { - let (data, start, end) = match self { + pub fn try_get_slice_bytes_for_diagnostics<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx [u8]> { + let (alloc_id, start, len) = match self { ConstValue::Scalar(_) | ConstValue::ZeroSized => { bug!("`try_get_slice_bytes` on non-slice constant") } - &ConstValue::Slice { data, meta } => (data, 0, meta), + &ConstValue::Slice { alloc_id, meta } => (alloc_id, 0, meta), &ConstValue::Indirect { alloc_id, offset } => { // The reference itself is stored behind an indirection. // Load the reference, and then load the actual slice contents. @@ -170,26 +171,29 @@ impl<'tcx> ConstValue<'tcx> { // Non-empty slice, must have memory. We know this is a relative pointer. let (inner_prov, offset) = ptr.into_pointer_or_addr().ok()?.prov_and_relative_offset(); - let data = tcx.global_alloc(inner_prov.alloc_id()).unwrap_memory(); - (data, offset.bytes(), offset.bytes() + len) + (inner_prov.alloc_id(), offset.bytes(), len) } }; + let data = tcx.global_alloc(alloc_id).unwrap_memory(); + // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`. let start = start.try_into().unwrap(); - let end = end.try_into().unwrap(); + let end = start + usize::try_from(len).unwrap(); Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end)) } /// Check if a constant may contain provenance information. This is used by MIR opts. /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool { + pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool { match *self { ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, ConstValue::Scalar(Scalar::Ptr(..)) => return true, // It's hard to find out the part of the allocation we point to; // just conservatively check everything. - ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(), + ConstValue::Slice { alloc_id, meta: _ } => { + !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() + } ConstValue::Indirect { alloc_id, offset } => !tcx .global_alloc(alloc_id) .unwrap_memory() @@ -200,7 +204,7 @@ impl<'tcx> ConstValue<'tcx> { } /// Check if a constant only contains uninitialized bytes. - pub fn all_bytes_uninit(&self, tcx: TyCtxt<'tcx>) -> bool { + pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool { let ConstValue::Indirect { alloc_id, .. } = self else { return false; }; @@ -247,7 +251,7 @@ pub enum Const<'tcx> { /// This constant cannot go back into the type system, as it represents /// something the type system cannot handle (e.g. pointers). - Val(ConstValue<'tcx>, Ty<'tcx>), + Val(ConstValue, Ty<'tcx>), } impl<'tcx> Const<'tcx> { @@ -343,7 +347,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, span: Span, - ) -> Result<ConstValue<'tcx>, ErrorHandled> { + ) -> Result<ConstValue, ErrorHandled> { match self { Const::Ty(_, c) => { if c.has_non_region_param() { @@ -440,7 +444,7 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self { + pub fn from_value(val: ConstValue, ty: Ty<'tcx>) -> Self { Self::Val(val, ty) } @@ -487,9 +491,8 @@ impl<'tcx> Const<'tcx> { /// taking into account even pointer identity tests. pub fn is_deterministic(&self) -> bool { // Some constants may generate fresh allocations for pointers they contain, - // so using the same constant twice can yield two different results: - // - valtrees purposefully generate new allocations - // - ConstValue::Slice also generate new allocations + // so using the same constant twice can yield two different results. + // Notably, valtrees purposefully generate new allocations. match self { Const::Ty(_, c) => match c.kind() { ty::ConstKind::Param(..) => true, @@ -507,11 +510,11 @@ impl<'tcx> Const<'tcx> { | ty::ConstKind::Placeholder(..) => bug!(), }, Const::Unevaluated(..) => false, - // If the same slice appears twice in the MIR, we cannot guarantee that we will - // give the same `AllocId` to the data. - Const::Val(ConstValue::Slice { .. }, _) => false, Const::Val( - ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. }, + ConstValue::Slice { .. } + | ConstValue::ZeroSized + | ConstValue::Scalar(_) + | ConstValue::Indirect { .. }, _, ) => true, } @@ -574,7 +577,7 @@ impl<'tcx> Display for Const<'tcx> { /// Const-related utilities impl<'tcx> TyCtxt<'tcx> { - pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> { + pub fn span_as_caller_location(self, span: Span) -> ConstValue { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); self.const_caller_location( diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 3e68afbfabd..3e895c6b280 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -137,7 +137,7 @@ impl<'tcx> ValTreeCreationError<'tcx> { pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>; -pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; +pub type EvalToConstValueResult<'tcx> = Result<ConstValue, ErrorHandled>; pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>; #[cfg(target_pointer_width = "64")] @@ -426,7 +426,12 @@ pub enum UndefinedBehaviorInfo<'tcx> { /// Trying to set discriminant to the niched variant, but the value does not match. InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> }, /// ABI-incompatible argument types. - AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, + AbiMismatchArgument { + /// The index of the argument whose type is wrong. + arg_idx: usize, + caller_ty: Ty<'tcx>, + callee_ty: Ty<'tcx>, + }, /// ABI-incompatible return types. AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 2d7ddd105bd..105736b9e24 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -143,10 +143,8 @@ impl<'tcx> MonoItem<'tcx> { }; // Similarly, the executable entrypoint must be instantiated exactly once. - if let Some((entry_def_id, _)) = tcx.entry_fn(()) { - if instance.def_id() == entry_def_id { - return InstantiationMode::GloballyShared { may_conflict: false }; - } + if tcx.is_entrypoint(instance.def_id()) { + return InstantiationMode::GloballyShared { may_conflict: false }; } // If the function is #[naked] or contains any other attribute that requires exactly-once diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8e403dfddae..809cdb329f7 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1465,7 +1465,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.push(&format!("+ user_ty: {user_ty:?}")); } - let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| { + let fmt_val = |val: ConstValue, ty: Ty<'tcx>| { let tcx = self.tcx; rustc_data_structures::make_display(move |fmt| { pretty_print_const_value_tcx(tcx, val, ty, fmt) @@ -1562,16 +1562,12 @@ pub fn write_allocations<'tcx>( alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id()) } - fn alloc_id_from_const_val(val: ConstValue<'_>) -> Option<AllocId> { + fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> { match val { ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()), ConstValue::Scalar(interpret::Scalar::Int { .. }) => None, ConstValue::ZeroSized => None, - ConstValue::Slice { .. } => { - // `u8`/`str` slices, shouldn't contain pointers that we want to print. - None - } - ConstValue::Indirect { alloc_id, .. } => { + ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => { // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR. // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor. Some(alloc_id) @@ -1885,7 +1881,7 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul fn comma_sep<'tcx>( tcx: TyCtxt<'tcx>, fmt: &mut Formatter<'_>, - elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>, + elems: Vec<(ConstValue, Ty<'tcx>)>, ) -> fmt::Result { let mut first = true; for (ct, ty) in elems { @@ -1900,7 +1896,7 @@ fn comma_sep<'tcx>( fn pretty_print_const_value_tcx<'tcx>( tcx: TyCtxt<'tcx>, - ct: ConstValue<'tcx>, + ct: ConstValue, ty: Ty<'tcx>, fmt: &mut Formatter<'_>, ) -> fmt::Result { @@ -1947,7 +1943,7 @@ fn pretty_print_const_value_tcx<'tcx>( let ct = tcx.lift(ct).unwrap(); let ty = tcx.lift(ty).unwrap(); if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) { - let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec(); + let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec(); match *ty.kind() { ty::Array(..) => { fmt.write_str("[")?; @@ -2028,7 +2024,7 @@ fn pretty_print_const_value_tcx<'tcx>( } pub(crate) fn pretty_print_const_value<'tcx>( - ct: ConstValue<'tcx>, + ct: ConstValue, ty: Ty<'tcx>, fmt: &mut Formatter<'_>, ) -> fmt::Result { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 3fc05f2caf2..a8a95c699d8 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -173,5 +173,5 @@ pub enum AnnotationSource { #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConstant<'tcx> { pub variant: Option<VariantIdx>, - pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)], + pub fields: &'tcx [(ConstValue, Ty<'tcx>)], } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index f138c5ca039..dab5900b4ab 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -153,8 +153,8 @@ impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> { type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()]; } -impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> { - type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()]; +impl EraseType for Result<mir::ConstValue, mir::interpret::ErrorHandled> { + type Result = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()]; } impl EraseType for EvalToValTreeResult<'_> { @@ -301,6 +301,7 @@ trivial! { rustc_middle::middle::resolve_bound_vars::ResolvedArg, rustc_middle::middle::stability::DeprecationEntry, rustc_middle::mir::ConstQualifs, + rustc_middle::mir::ConstValue, rustc_middle::mir::interpret::AllocId, rustc_middle::mir::interpret::CtfeProvenance, rustc_middle::mir::interpret::ErrorHandled, @@ -362,7 +363,6 @@ tcx_lifetime! { rustc_middle::mir::Const, rustc_middle::mir::DestructuredConstant, rustc_middle::mir::ConstAlloc, - rustc_middle::mir::ConstValue, rustc_middle::mir::interpret::GlobalId, rustc_middle::mir::interpret::LitToConstInput, rustc_middle::mir::interpret::EvalStaticInitializerRawResult, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index ad7f4973e23..587349d4cf4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1363,7 +1363,7 @@ rustc_queries! { } /// Converts a type-level constant value into a MIR constant value. - query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> { + query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue { desc { "converting type-level constant value to MIR constant value"} } @@ -1391,7 +1391,6 @@ rustc_queries! { desc { "checking effective visibilities" } } query check_private_in_public(_: ()) { - eval_always desc { "checking for private elements in public interfaces" } } @@ -2152,9 +2151,6 @@ rustc_queries! { desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } separate_provide_extern } - query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option<CrateNum> { - desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) } - } /// Gets the number of definitions in a foreign crate. /// diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 730c1147684..3dd6d2c8928 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -838,6 +838,8 @@ pub enum PatKind<'tcx> { /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. + /// * raw pointers derived from integers, other raw pointers will have already resulted in an + // error. /// * `String`, if `string_deref_patterns` is enabled. Constant { value: mir::Const<'tcx>, diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 275458fc85f..3bf80d37e65 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -566,10 +566,10 @@ impl<'tcx> AdtDef<'tcx> { let mut prev_discr = None::<Discr<'tcx>>; self.variants().iter_enumerated().map(move |(i, v)| { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) { - discr = new_discr; - } + if let VariantDiscr::Explicit(expr_did) = v.discr + && let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) + { + discr = new_discr; } prev_discr = Some(discr); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 51db92ecd78..6f21160d1f6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -5,7 +5,7 @@ pub mod tls; use std::assert_matches::debug_assert_matches; -use std::borrow::Borrow; +use std::borrow::{Borrow, Cow}; use std::cmp::Ordering; use std::env::VarError; use std::ffi::OsStr; @@ -1041,11 +1041,13 @@ const NUM_PREINTERNED_TY_VARS: u32 = 100; const NUM_PREINTERNED_FRESH_TYS: u32 = 20; const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3; const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3; +const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3; +const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20; // This number may seem high, but it is reached in all but the smallest crates. const NUM_PREINTERNED_RE_VARS: u32 = 500; -const NUM_PREINTERNED_RE_LATE_BOUNDS_I: u32 = 2; -const NUM_PREINTERNED_RE_LATE_BOUNDS_V: u32 = 20; +const NUM_PREINTERNED_ANON_RE_BOUNDS_I: u32 = 3; +const NUM_PREINTERNED_ANON_RE_BOUNDS_V: u32 = 20; pub struct CommonTypes<'tcx> { pub unit: Ty<'tcx>, @@ -1088,6 +1090,11 @@ pub struct CommonTypes<'tcx> { /// Pre-interned `Infer(ty::FreshFloatTy(n))` for small values of `n`. pub fresh_float_tys: Vec<Ty<'tcx>>, + + /// Pre-interned values of the form: + /// `Bound(DebruijnIndex(i), BoundTy { var: v, kind: BoundTyKind::Anon})` + /// for small values of `i` and `v`. + pub anon_bound_tys: Vec<Vec<Ty<'tcx>>>, } pub struct CommonLifetimes<'tcx> { @@ -1101,9 +1108,9 @@ pub struct CommonLifetimes<'tcx> { pub re_vars: Vec<Region<'tcx>>, /// Pre-interned values of the form: - /// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon })` + /// `ReBound(DebruijnIndex(i), BoundRegion { var: v, kind: BoundRegionKind::Anon })` /// for small values of `i` and `v`. - pub re_late_bounds: Vec<Vec<Region<'tcx>>>, + pub anon_re_bounds: Vec<Vec<Region<'tcx>>>, } pub struct CommonConsts<'tcx> { @@ -1131,6 +1138,19 @@ impl<'tcx> CommonTypes<'tcx> { let fresh_float_tys: Vec<_> = (0..NUM_PREINTERNED_FRESH_FLOAT_TYS).map(|n| mk(Infer(ty::FreshFloatTy(n)))).collect(); + let anon_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_I) + .map(|i| { + (0..NUM_PREINTERNED_ANON_BOUND_TYS_V) + .map(|v| { + mk(ty::Bound( + ty::DebruijnIndex::from(i), + ty::BoundTy { var: ty::BoundVar::from(v), kind: ty::BoundTyKind::Anon }, + )) + }) + .collect() + }) + .collect(); + CommonTypes { unit: mk(Tuple(List::empty())), bool: mk(Bool), @@ -1161,6 +1181,7 @@ impl<'tcx> CommonTypes<'tcx> { fresh_tys, fresh_int_tys, fresh_float_tys, + anon_bound_tys, } } } @@ -1176,9 +1197,9 @@ impl<'tcx> CommonLifetimes<'tcx> { let re_vars = (0..NUM_PREINTERNED_RE_VARS).map(|n| mk(ty::ReVar(ty::RegionVid::from(n)))).collect(); - let re_late_bounds = (0..NUM_PREINTERNED_RE_LATE_BOUNDS_I) + let anon_re_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_I) .map(|i| { - (0..NUM_PREINTERNED_RE_LATE_BOUNDS_V) + (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V) .map(|v| { mk(ty::ReBound( ty::DebruijnIndex::from(i), @@ -1196,7 +1217,7 @@ impl<'tcx> CommonLifetimes<'tcx> { re_static: mk(ty::ReStatic), re_erased: mk(ty::ReErased), re_vars, - re_late_bounds, + anon_re_bounds, } } } @@ -1625,7 +1646,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Allocates a read-only byte or string literal for `mir::interpret` with alignment 1. /// Returns the same `AllocId` if called again with the same bytes. - pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId { + pub fn allocate_bytes_dedup<'a>( + self, + bytes: impl Into<Cow<'a, [u8]>>, + salt: usize, + ) -> interpret::AllocId { // Create an allocation that just contains these bytes. let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes, ()); let alloc = self.mk_const_alloc(alloc); @@ -3373,6 +3398,11 @@ impl<'tcx> TyCtxt<'tcx> { self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..]) } + /// Return the crate imported by given use item. + pub fn extern_mod_stmt_cnum(self, def_id: LocalDefId) -> Option<CrateNum> { + self.resolutions(()).extern_crate_map.get(&def_id).copied() + } + pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc<ast::Crate>)> { self.resolver_for_lowering_raw(()).0 } @@ -3410,6 +3440,20 @@ impl<'tcx> TyCtxt<'tcx> { pub fn do_not_recommend_impl(self, def_id: DefId) -> bool { self.get_diagnostic_attr(def_id, sym::do_not_recommend).is_some() } + + /// Whether this def is one of the special bin crate entrypoint functions that must have a + /// monomorphization and also not be internalized in the bin crate. + pub fn is_entrypoint(self, def_id: DefId) -> bool { + if self.is_lang_item(def_id, LangItem::Start) { + return true; + } + if let Some((entry_def_id, _)) = self.entry_fn(()) + && entry_def_id == def_id + { + return true; + } + false + } } /// Parameter attributes that can only be determined by examining the body of a function instead @@ -3428,8 +3472,6 @@ pub struct DeducedParamAttrs { } pub fn provide(providers: &mut Providers) { - providers.extern_mod_stmt_cnum = - |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned(); providers.is_panic_runtime = |tcx, LocalCrate| contains_name(tcx.hir_krate_attrs(), sym::panic_runtime); providers.is_compiler_builtins = diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 809717513c7..aed94f9aa04 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -107,8 +107,8 @@ impl abi::Integer { abi::Integer::I8 }; - // If there are no negative values, we can use the unsigned fit. - if min >= 0 { + // Pick the smallest fit. + if unsigned_fit <= signed_fit { (cmp::max(unsigned_fit, at_least), false) } else { (cmp::max(signed_fit, at_least), true) @@ -1055,11 +1055,11 @@ where _ => Some(this), }; - if let Some(variant) = data_variant { + if let Some(variant) = data_variant // We're not interested in any unions. - if let FieldsShape::Union(_) = variant.fields { - data_variant = None; - } + && let FieldsShape::Union(_) = variant.fields + { + data_variant = None; } let mut result = None; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a7cde2ad485..bb70c61cd14 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1988,29 +1988,21 @@ impl<'tcx> TyCtxt<'tcx> { self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id) } - /// If the given `DefId` describes an item belonging to a trait, - /// returns the `DefId` of the trait that the trait item belongs to; - /// otherwise, returns `None`. - pub fn trait_of_item(self, def_id: DefId) -> Option<DefId> { - if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { - let parent = self.parent(def_id); - if let DefKind::Trait | DefKind::TraitAlias = self.def_kind(parent) { - return Some(parent); - } - } - None + /// If the given `DefId` is an associated item, returns the `DefId` of the parent trait or impl. + pub fn assoc_parent(self, def_id: DefId) -> Option<DefId> { + self.def_kind(def_id).is_assoc().then(|| self.parent(def_id)) } - /// If the given `DefId` describes a method belonging to an impl, returns the - /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. - pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> { - if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { - let parent = self.parent(def_id); - if let DefKind::Impl { .. } = self.def_kind(parent) { - return Some(parent); - } - } - None + /// If the given `DefId` is an associated item of a trait, + /// returns the `DefId` of the trait; otherwise, returns `None`. + pub fn trait_of_assoc(self, def_id: DefId) -> Option<DefId> { + self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) + } + + /// If the given `DefId` is an associated item of an impl, + /// returns the `DefId` of the impl; otherwise returns `None`. + pub fn impl_of_assoc(self, def_id: DefId) -> Option<DefId> { + self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) } pub fn is_exportable(self, def_id: DefId) -> bool { @@ -2024,7 +2016,10 @@ impl<'tcx> TyCtxt<'tcx> { && let Some(def_id) = def_id.as_local() && let outer = self.def_span(def_id).ctxt().outer_expn_data() && matches!(outer.kind, ExpnKind::Macro(MacroKind::Derive, _)) - && self.has_attr(outer.macro_def_id.unwrap(), sym::rustc_builtin_macro) + && find_attr!( + self.get_all_attrs(outer.macro_def_id.unwrap()), + AttributeKind::RustcBuiltinMacro { .. } + ) { true } else { @@ -2178,7 +2173,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn is_const_default_method(self, def_id: DefId) -> bool { - matches!(self.trait_of_item(def_id), Some(trait_id) if self.is_const_trait(trait_id)) + matches!(self.trait_of_assoc(def_id), Some(trait_id) if self.is_const_trait(trait_id)) } pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 9ee64df0ad0..5c44b10ba71 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -86,7 +86,7 @@ macro_rules! define_helper { impl $helper { pub fn new() -> $helper { - $helper($tl.with(|c| c.replace(true))) + $helper($tl.replace(true)) } } @@ -100,12 +100,12 @@ macro_rules! define_helper { impl Drop for $helper { fn drop(&mut self) { - $tl.with(|c| c.set(self.0)) + $tl.set(self.0) } } pub fn $name() -> bool { - $tl.with(|c| c.get()) + $tl.get() } )+ } @@ -225,10 +225,10 @@ impl<'tcx> RegionHighlightMode<'tcx> { region: Option<ty::Region<'tcx>>, number: Option<usize>, ) { - if let Some(k) = region { - if let Some(n) = number { - self.highlighting_region(k, n); - } + if let Some(k) = region + && let Some(n) = number + { + self.highlighting_region(k, n); } } diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 51be93d9a72..5cf96072177 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -54,7 +54,7 @@ impl<'tcx> Region<'tcx> { ) -> Region<'tcx> { // Use a pre-interned one when possible. if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region - && let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize()) + && let Some(inner) = tcx.lifetimes.anon_re_bounds.get(debruijn.as_usize()) && let Some(re) = inner.get(var.as_usize()).copied() { re diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index ab31d943408..a5fdce93e4b 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -4,6 +4,7 @@ //! to help with the tedium. use std::fmt::{self, Debug}; +use std::marker::PhantomData; use rustc_abi::TyAndLayout; use rustc_hir::def::Namespace; @@ -234,6 +235,7 @@ TrivialLiftImpls! { rustc_abi::ExternAbi, rustc_abi::Size, rustc_hir::Safety, + rustc_middle::mir::ConstValue, rustc_type_ir::BoundConstness, rustc_type_ir::PredicatePolarity, // tidy-alphabetical-end @@ -250,7 +252,7 @@ TrivialTypeTraversalImpls! { crate::mir::BlockTailInfo, crate::mir::BorrowKind, crate::mir::CastKind, - crate::mir::ConstValue<'tcx>, + crate::mir::ConstValue, crate::mir::CoroutineSavedLocal, crate::mir::FakeReadCause, crate::mir::Local, @@ -311,6 +313,13 @@ TrivialTypeTraversalAndLiftImpls! { /////////////////////////////////////////////////////////////////////////// // Lift implementations +impl<'tcx> Lift<TyCtxt<'tcx>> for PhantomData<&()> { + type Lifted = PhantomData<&'tcx ()>; + fn lift_to_interner(self, _: TyCtxt<'tcx>) -> Option<Self::Lifted> { + Some(PhantomData) + } +} + impl<'tcx, T: Lift<TyCtxt<'tcx>>> Lift<TyCtxt<'tcx>> for Option<T> { type Lifted = Option<T::Lifted>; fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 8bb3b3f1263..4569596cfbe 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -485,7 +485,15 @@ impl<'tcx> Ty<'tcx> { index: ty::DebruijnIndex, bound_ty: ty::BoundTy, ) -> Ty<'tcx> { - Ty::new(tcx, Bound(index, bound_ty)) + // Use a pre-interned one when possible. + if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty + && let Some(inner) = tcx.types.anon_bound_tys.get(index.as_usize()) + && let Some(ty) = inner.get(var.as_usize()).copied() + { + ty + } else { + Ty::new(tcx, Bound(index, bound_ty)) + } } #[inline] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 174892c6f4d..a7d07adf78f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -32,7 +32,7 @@ use crate::ty::{ #[derive(Copy, Clone, Debug)] pub struct Discr<'tcx> { - /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + /// Bit representation of the discriminant (e.g., `-1i8` is `0xFF_u128`). pub val: u128, pub ty: Ty<'tcx>, } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index e339520cd86..abfe8eb66dd 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -86,10 +86,16 @@ mir_build_confused = missing patterns are not covered because `{$variable}` is i mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]` .label = this value is too generic - .note = the value must be a literal or a monomorphic const mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value +mir_build_const_continue_not_const = could not determine the target branch for this `#[const_continue]` + .help = try extracting the expression into a `const` item + +mir_build_const_continue_not_const_const_block = `const` blocks may use generics, and are not evaluated early enough +mir_build_const_continue_not_const_const_other = this value must be a literal or a monomorphic const +mir_build_const_continue_not_const_constant_parameter = constant parameters may use generics, and are not evaluated early enough + mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known .label = this value must be a literal or a monomorphic const diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index d0d0c21463f..0e0c7a7fa4f 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -3,7 +3,7 @@ use rustc_abi::Size; use rustc_ast as ast; use rustc_hir::LangItem; -use rustc_middle::mir::interpret::{Allocation, CTFE_ALLOC_SALT, LitToConstInput, Scalar}; +use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{ @@ -120,17 +120,18 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> let value = match (lit, lit_ty.kind()) { (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { - let s = s.as_str(); - let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes(), ()); - let allocation = tcx.mk_const_alloc(allocation); - ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } + let s = s.as_str().as_bytes(); + let len = s.len(); + let allocation = tcx.allocate_bytes_dedup(s, CTFE_ALLOC_SALT); + ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() } } - (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_bytes_byte_aligned_immutable(data.as_byte_str(), ()); - let allocation = tcx.mk_const_alloc(allocation); - ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } + let data = byte_sym.as_byte_str(); + let len = data.len(); + let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT); + ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() } } (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let id = tcx.allocate_bytes_dedup(byte_sym.as_byte_str(), CTFE_ALLOC_SALT); @@ -138,10 +139,10 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> } (ast::LitKind::CStr(byte_sym, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { - let allocation = - Allocation::from_bytes_byte_aligned_immutable(byte_sym.as_byte_str(), ()); - let allocation = tcx.mk_const_alloc(allocation); - ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() } + let data = byte_sym.as_byte_str(); + let len = data.len(); + let allocation = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT); + ConstValue::Slice { alloc_id: allocation, meta: len.try_into().unwrap() } } (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(n, Size::from_bytes(1))) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index daf8fa5f19e..a4ef6e92739 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -1,6 +1,6 @@ //! See docs in `build/expr/mod.rs`. -use rustc_abi::{BackendRepr, FieldIdx, Primitive}; +use rustc_abi::FieldIdx; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; @@ -9,7 +9,6 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::cast::{CastTy, mir_cast_kind}; -use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; use rustc_span::source_map::Spanned; @@ -200,8 +199,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx); let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not)); - let layout = - this.tcx.layout_of(this.typing_env().as_query_input(source_expr.ty)); let discr = this.temp(discr_ty, source_expr.span); this.cfg.push_assign( block, @@ -209,80 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { discr, Rvalue::Discriminant(temp.into()), ); - let (op, ty) = (Operand::Move(discr), discr_ty); - - if let BackendRepr::Scalar(scalar) = layout.unwrap().backend_repr - && !scalar.is_always_valid(&this.tcx) - && let Primitive::Int(int_width, _signed) = scalar.primitive() - { - let unsigned_ty = int_width.to_ty(this.tcx, false); - let unsigned_place = this.temp(unsigned_ty, expr_span); - this.cfg.push_assign( - block, - source_info, - unsigned_place, - Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty), - ); - - let bool_ty = this.tcx.types.bool; - let range = scalar.valid_range(&this.tcx); - let merge_op = - if range.start <= range.end { BinOp::BitAnd } else { BinOp::BitOr }; - - let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> { - // We can use `ty::TypingEnv::fully_monomorphized()` here - // as we only need it to compute the layout of a primitive. - let range_val = Const::from_bits( - this.tcx, - range, - ty::TypingEnv::fully_monomorphized(), - unsigned_ty, - ); - let lit_op = this.literal_operand(expr.span, range_val); - let is_bin_op = this.temp(bool_ty, expr_span); - this.cfg.push_assign( - block, - source_info, - is_bin_op, - Rvalue::BinaryOp( - bin_op, - Box::new((Operand::Copy(unsigned_place), lit_op)), - ), - ); - is_bin_op - }; - let assert_place = if range.start == 0 { - comparer(range.end, BinOp::Le) - } else { - let start_place = comparer(range.start, BinOp::Ge); - let end_place = comparer(range.end, BinOp::Le); - let merge_place = this.temp(bool_ty, expr_span); - this.cfg.push_assign( - block, - source_info, - merge_place, - Rvalue::BinaryOp( - merge_op, - Box::new(( - Operand::Move(start_place), - Operand::Move(end_place), - )), - ), - ); - merge_place - }; - this.cfg.push( - block, - Statement::new( - source_info, - StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( - Operand::Move(assert_place), - ))), - ), - ); - } - - (op, ty) + (Operand::Move(discr), discr_ty) } else { let ty = source_expr.ty; let source = unpack!( diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 3d5f6f4cf45..855cd2f3bc0 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -1045,11 +1045,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -fn parse_float_into_constval<'tcx>( - num: Symbol, - float_ty: ty::FloatTy, - neg: bool, -) -> Option<ConstValue<'tcx>> { +fn parse_float_into_constval(num: Symbol, float_ty: ty::FloatTy, neg: bool) -> Option<ConstValue> { parse_float_into_scalar(num, float_ty, neg).map(|s| ConstValue::Scalar(s.into())) } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 12a56d7c5ea..1240b34cf9d 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -100,7 +100,9 @@ use tracing::{debug, instrument}; use super::matches::BuiltMatchTree; use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; -use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget}; +use crate::errors::{ + ConstContinueBadConst, ConstContinueNotMonomorphicConst, ConstContinueUnknownJumpTarget, +}; #[derive(Debug)] pub(crate) struct Scopes<'tcx> { @@ -867,7 +869,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(span, "break value must be a scope") }; - let constant = match &self.thir[value].kind { + let expr = &self.thir[value]; + let constant = match &expr.kind { ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => { assert!(matches!(base, AdtExprBase::None)); assert!(fields.is_empty()); @@ -887,7 +890,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ), } } - _ => self.as_constant(&self.thir[value]), + + ExprKind::Literal { .. } + | ExprKind::NonHirLiteral { .. } + | ExprKind::ZstLiteral { .. } + | ExprKind::NamedConst { .. } => self.as_constant(&self.thir[value]), + + other => { + use crate::errors::ConstContinueNotMonomorphicConstReason as Reason; + + let span = expr.span; + let reason = match other { + ExprKind::ConstParam { .. } => Reason::ConstantParameter { span }, + ExprKind::ConstBlock { .. } => Reason::ConstBlock { span }, + _ => Reason::Other { span }, + }; + + self.tcx + .dcx() + .emit_err(ConstContinueNotMonomorphicConst { span: expr.span, reason }); + return block.unit(); + } }; let break_index = self diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 16b49bf384c..f1fbd5c4a49 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1214,6 +1214,38 @@ pub(crate) struct LoopMatchArmWithGuard { } #[derive(Diagnostic)] +#[diag(mir_build_const_continue_not_const)] +#[help] +pub(crate) struct ConstContinueNotMonomorphicConst { + #[primary_span] + pub span: Span, + + #[subdiagnostic] + pub reason: ConstContinueNotMonomorphicConstReason, +} + +#[derive(Subdiagnostic)] +pub(crate) enum ConstContinueNotMonomorphicConstReason { + #[label(mir_build_const_continue_not_const_constant_parameter)] + ConstantParameter { + #[primary_span] + span: Span, + }, + + #[label(mir_build_const_continue_not_const_const_block)] + ConstBlock { + #[primary_span] + span: Span, + }, + + #[label(mir_build_const_continue_not_const_const_other)] + Other { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] #[diag(mir_build_const_continue_bad_const)] pub(crate) struct ConstContinueBadConst { #[primary_span] diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index e858b629ab1..57ddb8eddb8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -76,28 +76,24 @@ impl<'tcx> ThirBuildCx<'tcx> { let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); - if let Some(ty) = &local.ty { - if let Some(&user_ty) = + if let Some(ty) = &local.ty + && let Some(&user_ty) = self.typeck_results.user_provided_types().get(ty.hir_id) - { - debug!("mirror_stmts: user_ty={:?}", user_ty); - let annotation = CanonicalUserTypeAnnotation { - user_ty: Box::new(user_ty), - span: ty.span, - inferred_ty: self.typeck_results.node_type(ty.hir_id), - }; - pattern = Box::new(Pat { - ty: pattern.ty, - span: pattern.span, - kind: PatKind::AscribeUserType { - ascription: Ascription { - annotation, - variance: ty::Covariant, - }, - subpattern: pattern, - }, - }); - } + { + debug!("mirror_stmts: user_ty={:?}", user_ty); + let annotation = CanonicalUserTypeAnnotation { + user_ty: Box::new(user_ty), + span: ty.span, + inferred_ty: self.typeck_results.node_type(ty.hir_id), + }; + pattern = Box::new(Pat { + ty: pattern.ty, + span: pattern.span, + kind: PatKind::AscribeUserType { + ascription: Ascription { annotation, variance: ty::Covariant }, + subpattern: pattern, + }, + }); } let span = match local.init { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b694409f327..16df58cd76d 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -105,11 +105,11 @@ impl<'tcx> ThirBuildCx<'tcx> { // // ^ error message points at this expression. // } let mut adjust_span = |expr: &mut Expr<'tcx>| { - if let ExprKind::Block { block } = expr.kind { - if let Some(last_expr) = self.thir[block].expr { - span = self.thir[last_expr].span; - expr.span = span; - } + if let ExprKind::Block { block } = expr.kind + && let Some(last_expr) = self.thir[block].expr + { + span = self.thir[last_expr].span; + expr.span = span; } }; @@ -955,21 +955,25 @@ impl<'tcx> ThirBuildCx<'tcx> { dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span }) }; - fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> { - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind { - if let Res::Local(hir_id) = path.res { - return Some(hir_id); - } + fn local( + cx: &mut ThirBuildCx<'_>, + expr: &rustc_hir::Expr<'_>, + ) -> Option<hir::HirId> { + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind + && let Res::Local(hir_id) = path.res + && !cx.is_upvar(hir_id) + { + return Some(hir_id); } None } - let Some(scrutinee_hir_id) = local(scrutinee) else { + let Some(scrutinee_hir_id) = local(self, scrutinee) else { dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span }) }; - if local(state) != Some(scrutinee_hir_id) { + if local(self, state) != Some(scrutinee_hir_id) { dcx.emit_fatal(LoopMatchInvalidUpdate { scrutinee: scrutinee.span, lhs: state.span, @@ -1260,10 +1264,7 @@ impl<'tcx> ThirBuildCx<'tcx> { fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> { // We want upvars here not captures. // Captures will be handled in MIR. - let is_upvar = self - .tcx - .upvars_mentioned(self.body_owner) - .is_some_and(|upvars| upvars.contains_key(&var_hir_id)); + let is_upvar = self.is_upvar(var_hir_id); debug!( "convert_var({:?}): is_upvar={}, body_owner={:?}", @@ -1443,6 +1444,12 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn is_upvar(&mut self, var_hir_id: hir::HirId) -> bool { + self.tcx + .upvars_mentioned(self.body_owner) + .is_some_and(|upvars| upvars.contains_key(&var_hir_id)) + } + /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr. fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> { fields diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 7f47754f6bc..ae67bb5075e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1155,14 +1155,12 @@ fn find_fallback_pattern_typo<'tcx>( } hir::Node::Block(hir::Block { stmts, .. }) => { for stmt in *stmts { - if let hir::StmtKind::Let(let_stmt) = stmt.kind { - if let hir::PatKind::Binding(_, _, binding_name, _) = + if let hir::StmtKind::Let(let_stmt) = stmt.kind + && let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind - { - if name == binding_name.name { - lint.pattern_let_binding = Some(binding_name.span); - } - } + && name == binding_name.name + { + lint.pattern_let_binding = Some(binding_name.span); } } } diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index c9c7fddae5a..1402a1a8b91 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -124,10 +124,9 @@ pub fn drop_flag_effects_for_location<'tcx, F>( // Drop does not count as a move but we should still consider the variable uninitialized. if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) = body.stmt_at(loc).right() + && let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) { - if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) { - on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent)) - } + on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent)) } debug!("drop_flag_effects: assignment for location({:?})", loc); diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 085757f0fb6..117525eb777 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -637,16 +637,13 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { debug!("initializes move_indexes {:?}", init_loc_map[location]); state.gen_all(init_loc_map[location].iter().copied()); - if let mir::StatementKind::StorageDead(local) = stmt.kind { + if let mir::StatementKind::StorageDead(local) = stmt.kind // End inits for StorageDead, so that an immutable variable can // be reinitialized on the next iteration of the loop. - if let Some(move_path_index) = rev_lookup.find_local(local) { - debug!( - "clears the ever initialized status of {:?}", - init_path_map[move_path_index] - ); - state.kill_all(init_path_map[move_path_index].iter().copied()); - } + && let Some(move_path_index) = rev_lookup.find_local(local) + { + debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]); + state.kill_all(init_path_map[move_path_index].iter().copied()); } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 303fc767b9a..1682f332857 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -135,12 +135,11 @@ fn value_assigned_to_local<'a, 'tcx>( stmt: &'a mir::Statement<'tcx>, local: Local, ) -> Option<&'a mir::Rvalue<'tcx>> { - if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if let Some(l) = place.as_local() { - if local == l { - return Some(&*rvalue); - } - } + if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(l) = place.as_local() + && local == l + { + return Some(&*rvalue); } None @@ -178,31 +177,30 @@ impl PeekCall { let span = terminator.source_info.span; if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind + && let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() { - if let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() { - if tcx.intrinsic(def_id)?.name != sym::rustc_peek { - return None; - } + if tcx.intrinsic(def_id)?.name != sym::rustc_peek { + return None; + } - assert_eq!(fn_args.len(), 1); - let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0)); - let arg = match &args[0].node { - Operand::Copy(place) | Operand::Move(place) => { - if let Some(local) = place.as_local() { - local - } else { - tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); - return None; - } - } - _ => { + assert_eq!(fn_args.len(), 1); + let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0)); + let arg = match &args[0].node { + Operand::Copy(place) | Operand::Move(place) => { + if let Some(local) = place.as_local() { + local + } else { tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); return None; } - }; + } + _ => { + tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); + return None; + } + }; - return Some(PeekCall { arg, kind, span }); - } + return Some(PeekCall { arg, kind, span }); } None diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 83fd8ccba60..005e7973130 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -215,10 +215,10 @@ impl<V: Clone + HasBottom> State<V> { // If both places are tracked, we copy the value to the target. // If the target is tracked, but the source is not, we do nothing, as invalidation has // already been performed. - if let Some(target_value) = map.places[target].value_index { - if let Some(source_value) = map.places[source].value_index { - values.insert(target_value, values.get(source_value).clone()); - } + if let Some(target_value) = map.places[target].value_index + && let Some(source_value) = map.places[source].value_index + { + values.insert(target_value, values.get(source_value).clone()); } for target_child in map.children(target) { // Try to find corresponding child and recurse. Reasoning is similar as above. diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index cace4cd6bba..6d61ac2dd80 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -21,7 +21,7 @@ impl<'tcx> MirLint<'tcx> for CheckCallRecursion { if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { // If this is trait/impl method, extract the trait's args. - let trait_args = match tcx.trait_of_item(def_id.to_def_id()) { + let trait_args = match tcx.trait_of_assoc(def_id.to_def_id()) { Some(trait_def_id) => { let trait_args_count = tcx.generics_of(trait_def_id).count(); &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count] @@ -44,7 +44,7 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { // First check if `body` is an `fn drop()` of `Drop` if let DefKind::AssocFn = tcx.def_kind(def_id) && let Some(trait_ref) = - tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) + tcx.impl_of_assoc(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) && tcx.is_lang_item(trait_ref.instantiate_identity().def_id, LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity() diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index e9b85ba6e9d..dcb812c7899 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -40,7 +40,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { if context.is_borrow() && util::is_disaligned(self.tcx, self.body, self.typing_env, *place) { let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = self.tcx.impl_of_method(def_id) + if let Some(impl_def_id) = self.tcx.impl_of_assoc(def_id) && self.tcx.is_builtin_derived(impl_def_id) { // If we ever reach here it means that the generated derive diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 406575c4f43..1a314e029f4 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -23,10 +23,10 @@ impl<'tcx> MutVisitor<'tcx> for FixReturnPendingVisitor<'tcx> { } // Converting `_0 = Poll::<Rv>::Pending` to `_0 = Poll::<()>::Pending` - if let Rvalue::Aggregate(kind, _) = rvalue { - if let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind { - *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); - } + if let Rvalue::Aggregate(kind, _) = rvalue + && let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind + { + *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); } } } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 986c001de5e..73795e47d2e 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr}; +use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind}; @@ -32,16 +32,6 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return false; } - // Don't instrument functions with `#[automatically_derived]` on their - // enclosing impl block, on the assumption that most users won't care about - // coverage for derived impls. - if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) - && tcx.is_automatically_derived(impl_of) - { - trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); - return false; - } - if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) { trace!("InstrumentCoverage skipped for {def_id:?} (`#[naked]`)"); return false; @@ -57,20 +47,32 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Query implementation for `coverage_attr_on`. fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - // Check for annotations directly on this def. - if let Some(coverage_status) = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status) + // Check for a `#[coverage(..)]` attribute on this def. + if let Some(kind) = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_sp, kind) => kind) { - *coverage_status == CoverageStatus::On - } else { - match tcx.opt_local_parent(def_id) { - // Check the parent def (and so on recursively) until we find an - // enclosing attribute or reach the crate root. - Some(parent) => tcx.coverage_attr_on(parent), - // We reached the crate root without seeing a coverage attribute, so - // allow coverage instrumentation by default. - None => true, + match kind { + CoverageAttrKind::On => return true, + CoverageAttrKind::Off => return false, } + }; + + // Treat `#[automatically_derived]` as an implied `#[coverage(off)]`, on + // the assumption that most users won't want coverage for derived impls. + // + // This affects not just the associated items of an impl block, but also + // any closures and other nested functions within those associated items. + if tcx.is_automatically_derived(def_id.to_def_id()) { + return false; + } + + // Check the parent def (and so on recursively) until we find an + // enclosing attribute or reach the crate root. + match tcx.opt_local_parent(def_id) { + Some(parent) => tcx.coverage_attr_on(parent), + // We reached the crate root without seeing a coverage attribute, so + // allow coverage instrumentation by default. + None => true, } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 6657f89ceb5..dc99b67a1e8 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1542,7 +1542,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn op_to_prop_const<'tcx>( ecx: &mut InterpCx<'tcx, DummyMachine>, op: &OpTy<'tcx>, -) -> Option<ConstValue<'tcx>> { +) -> Option<ConstValue> { // Do not attempt to propagate unsized locals. if op.layout.is_unsized() { return None; diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index 86e2bf6cb3c..b03518de00a 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -27,7 +27,7 @@ //! it's usually never invoked in this way. use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; -use rustc_middle::ty::{TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt}; use rustc_trait_selection::traits; use tracing::trace; @@ -36,14 +36,23 @@ use crate::pass_manager::MirPass; pub(crate) struct ImpossiblePredicates; impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { + #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let predicates = tcx - .predicates_of(body.source.def_id()) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) { - trace!("found unsatisfiable predicates for {:?}", body.source); + tracing::trace!(def_id = ?body.source.def_id()); + let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx); + tracing::trace!(?predicates); + let predicates = predicates.predicates.into_iter().filter(|p| { + !p.has_type_flags( + // Only consider global clauses to simplify. + TypeFlags::HAS_FREE_LOCAL_NAMES + // Clauses that refer to unevaluated constants as they cause cycles. + | TypeFlags::HAS_CT_PROJECTION, + ) + }); + let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); + tracing::trace!(?predicates); + if predicates.references_error() || traits::impossible_predicates(tcx, predicates) { + trace!("found unsatisfiable predicates"); // Clear the body to only contain a single `unreachable` statement. let bbs = body.basic_blocks.as_mut(); bbs.raw.truncate(1); diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index dbcaed20953..c83bd25c663 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -55,6 +55,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { let terminator = block.terminator.as_mut().unwrap(); ctx.simplify_primitive_clone(terminator, &mut block.statements); + ctx.simplify_align_of_slice_val(terminator, &mut block.statements); ctx.simplify_intrinsic_assert(terminator); ctx.simplify_nounwind_call(terminator); simplify_duplicate_switch_targets(terminator); @@ -252,6 +253,36 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { terminator.kind = TerminatorKind::Goto { target: *destination_block }; } + // Convert `align_of_val::<[T]>(ptr)` to `align_of::<T>()`, since the + // alignment of a slice doesn't actually depend on metadata at all + // and the element type is always `Sized`. + // + // This is here so it can run after inlining, where it's more useful. + // (LowerIntrinsics is done in cleanup, before the optimization passes.) + fn simplify_align_of_slice_val( + &self, + terminator: &mut Terminator<'tcx>, + statements: &mut Vec<Statement<'tcx>>, + ) { + if let TerminatorKind::Call { + func, args, destination, target: Some(destination_block), .. + } = &terminator.kind + && args.len() == 1 + && let Some((fn_def_id, generics)) = func.const_fn_def() + && self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) + && let ty::Slice(elem_ty) = *generics.type_at(0).kind() + { + statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp(NullOp::AlignOf, elem_ty), + ))), + )); + terminator.kind = TerminatorKind::Goto { target: *destination_block }; + } + } + fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) { let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else { return; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 4e8f30e077b..462ddfa3dd3 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -997,12 +997,11 @@ fn promote_candidates<'tcx>( for candidate in candidates.into_iter().rev() { let Location { block, statement_index } = candidate.location; if let StatementKind::Assign(box (place, _)) = &body[block].statements[statement_index].kind + && let Some(local) = place.as_local() { - if let Some(local) = place.as_local() { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; - } + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; } } @@ -1066,11 +1065,11 @@ fn promote_candidates<'tcx>( _ => true, }); let terminator = block.terminator_mut(); - if let TerminatorKind::Drop { place, target, .. } = &terminator.kind { - if let Some(index) = place.as_local() { - if promoted(index) { - terminator.kind = TerminatorKind::Goto { target: *target }; - } + if let TerminatorKind::Drop { place, target, .. } = &terminator.kind + && let Some(index) = place.as_local() + { + if promoted(index) { + terminator.kind = TerminatorKind::Goto { target: *target }; } } } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 797056ad52d..5b6d7ffb511 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -48,14 +48,13 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); for bb in postorder { debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { - if let UnwindAction::Cleanup(unwind_bb) = *unwind { - if nop_landing_pads.contains(unwind_bb) { - debug!(" removing noop landing pad"); - landing_pads_removed += 1; - *unwind = UnwindAction::Continue; - } - } + if let Some(unwind) = body[bb].terminator_mut().unwind_mut() + && let UnwindAction::Cleanup(unwind_bb) = *unwind + && nop_landing_pads.contains(unwind_bb) + { + debug!(" removing noop landing pad"); + landing_pads_removed += 1; + *unwind = UnwindAction::Continue; } body[bb].terminator_mut().successors_mut(|target| { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6c65b072bec..c687036f544 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -75,7 +75,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id)) } ty::InstanceKind::FnPtrShim(def_id, ty) => { - let trait_ = tcx.trait_of_item(def_id).unwrap(); + let trait_ = tcx.trait_of_assoc(def_id).unwrap(); // Supports `Fn` or `async Fn` traits. let adjustment = match tcx .fn_trait_kind_from_def_id(trait_) diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 7c6ccc89c4f..80c4b58a574 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -1,4 +1,4 @@ -use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_abi::FieldIdx; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_hir::LangItem; use rustc_index::IndexVec; @@ -32,7 +32,7 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { let typing_env = body.typing_env(tcx); loop { debug!(?excluded); - let escaping = escaping_locals(tcx, typing_env, &excluded, body); + let escaping = escaping_locals(tcx, &excluded, body); debug!(?escaping); let replacements = compute_flattening(tcx, typing_env, body, escaping); debug!(?replacements); @@ -64,7 +64,6 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { /// client code. fn escaping_locals<'tcx>( tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, excluded: &DenseBitSet<Local>, body: &Body<'tcx>, ) -> DenseBitSet<Local> { @@ -72,31 +71,12 @@ fn escaping_locals<'tcx>( if ty.is_union() || ty.is_enum() { return true; } - if let ty::Adt(def, _args) = ty.kind() { - if def.repr().simd() { - // Exclude #[repr(simd)] types so that they are not de-optimized into an array - return true; - } - if tcx.is_lang_item(def.did(), LangItem::DynMetadata) { - // codegen wants to see the `DynMetadata<T>`, - // not the inner reference-to-opaque-type. - return true; - } - // We already excluded unions and enums, so this ADT must have one variant - let variant = def.variant(FIRST_VARIANT); - if variant.fields.len() > 1 { - // If this has more than one field, it cannot be a wrapper that only provides a - // niche, so we do not want to automatically exclude it. - return false; - } - let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { - // We can't get the layout - return true; - }; - if layout.layout.largest_niche().is_some() { - // This type has a niche - return true; - } + if let ty::Adt(def, _args) = ty.kind() + && tcx.is_lang_item(def.did(), LangItem::DynMetadata) + { + // codegen wants to see the `DynMetadata<T>`, + // not the inner reference-to-opaque-type. + return true; } // Default for non-ADTs false diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 5860072d541..98d12bf0a38 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -80,15 +80,14 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.fail(location, msg); } - if let MirPhase::Runtime(_) = body.phase { - if let ty::InstanceKind::Item(_) = body.source.instance { - if body.has_free_regions() { - cfg_checker.fail( - Location::START, - format!("Free regions in optimized {} MIR", body.phase.name()), - ); - } - } + if let MirPhase::Runtime(_) = body.phase + && let ty::InstanceKind::Item(_) = body.source.instance + && body.has_free_regions() + { + cfg_checker.fail( + Location::START, + format!("Free regions in optimized {} MIR", body.phase.name()), + ); } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 91c8e64ce9a..1bfd83d97ac 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -659,10 +659,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { } /// Evaluates a *not yet monomorphized* constant. - fn eval_constant( - &mut self, - constant: &mir::ConstOperand<'tcx>, - ) -> Option<mir::ConstValue<'tcx>> { + fn eval_constant(&mut self, constant: &mir::ConstOperand<'tcx>) -> Option<mir::ConstValue> { let const_ = self.monomorphize(constant.const_); // Evaluate the constant. This makes const eval failure a collection-time error (rather than // a codegen-time error). rustc stops after collection if there was an error, so this @@ -1355,19 +1352,15 @@ fn visit_mentioned_item<'tcx>( #[instrument(skip(tcx, output), level = "debug")] fn collect_const_value<'tcx>( tcx: TyCtxt<'tcx>, - value: mir::ConstValue<'tcx>, + value: mir::ConstValue, output: &mut MonoItems<'tcx>, ) { match value { mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { collect_alloc(tcx, ptr.provenance.alloc_id(), output) } - mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), - mir::ConstValue::Slice { data, meta: _ } => { - for &prov in data.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - } + mir::ConstValue::Indirect { alloc_id, .. } + | mir::ConstValue::Slice { alloc_id, meta: _ } => collect_alloc(tcx, alloc_id, output), _ => {} } } @@ -1582,6 +1575,15 @@ impl<'v> RootCollector<'_, 'v> { return; }; + let main_instance = Instance::mono(self.tcx, main_def_id); + if self.tcx.should_codegen_locally(main_instance) { + self.output.push(create_fn_mono_item( + self.tcx, + main_instance, + self.tcx.def_span(main_def_id), + )); + } + let Some(start_def_id) = self.tcx.lang_items().start_fn() else { self.tcx.dcx().emit_fatal(errors::StartNotFound); }; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 69851511fb1..cee15e0f696 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -223,11 +223,7 @@ where // So even if its mode is LocalCopy, we need to treat it like a root. match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} - InstantiationMode::LocalCopy => { - if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) { - continue; - } - } + InstantiationMode::LocalCopy => continue, } let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item); @@ -654,13 +650,13 @@ fn characteristic_def_id_of_mono_item<'tcx>( // its self-type. If the self-type does not provide a characteristic // DefId, we use the location of the impl after all. - if tcx.trait_of_item(def_id).is_some() { + if tcx.trait_of_assoc(def_id).is_some() { let self_ty = instance.args.type_at(0); // This is a default implementation of a trait method. return characteristic_def_id_of_type(self_ty).or(Some(def_id)); } - if let Some(impl_def_id) = tcx.impl_of_method(def_id) { + if let Some(impl_def_id) = tcx.impl_of_assoc(def_id) { if tcx.sess.opts.incremental.is_some() && tcx .trait_id_of_impl(impl_def_id) @@ -821,10 +817,9 @@ fn mono_item_visibility<'tcx>( | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, }; - // The `start_fn` lang item is actually a monomorphized instance of a - // function in the standard library, used for the `main` function. We don't - // want to export it so we tag it with `Hidden` visibility but this symbol - // is only referenced from the actual `main` symbol which we unfortunately + // Both the `start_fn` lang item and `main` itself should not be exported, + // so we give them with `Hidden` visibility but these symbols are + // only referenced from the actual `main` symbol which we unfortunately // don't know anything about during partitioning/collection. As a result we // forcibly keep this symbol out of the `internalization_candidates` set. // @@ -834,7 +829,7 @@ fn mono_item_visibility<'tcx>( // from the `main` symbol we'll generate later. // // This may be fixable with a new `InstanceKind` perhaps? Unsure! - if tcx.is_lang_item(def_id, LangItem::Start) { + if tcx.is_entrypoint(def_id) { *can_be_internalized = false; return Visibility::Hidden; } @@ -1183,12 +1178,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); // Output monomorphization stats per def_id - if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats { - if let Err(err) = + if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats + && let Err(err) = dump_mono_items_stats(tcx, codegen_units, path, tcx.crate_name(LOCAL_CRATE)) - { - tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() }); - } + { + tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() }); } if tcx.sess.opts.unstable_opts.print_mono_items { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index a418aa82100..1bc35e599c7 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -19,6 +19,20 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( ) .unwrap(); +#[derive(Debug, Clone, Copy)] +enum CanonicalizeInputKind { + /// When canonicalizing the `param_env`, we keep `'static` as merging + /// trait candidates relies on it when deciding whether a where-bound + /// is trivial. + ParamEnv, + /// When canonicalizing predicates, we don't keep `'static`. If we're + /// currently outside of the trait solver and canonicalize the root goal + /// during HIR typeck, we replace each occurance of a region with a + /// unique region variable. See the comment on `InferCtxt::in_hir_typeck` + /// for more details. + Predicate { is_hir_typeck_root_goal: bool }, +} + /// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller @@ -26,10 +40,7 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( /// query. #[derive(Debug, Clone, Copy)] enum CanonicalizeMode { - /// When canonicalizing the `param_env`, we keep `'static` as merging - /// trait candidates relies on it when deciding whether a where-bound - /// is trivial. - Input { keep_static: bool }, + Input(CanonicalizeInputKind), /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. @@ -122,7 +133,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { let mut variables = Vec::new(); let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables: &mut variables, variable_lookup_table: Default::default(), @@ -154,7 +165,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { } else { let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables, variable_lookup_table: Default::default(), @@ -180,6 +191,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { pub fn canonicalize_input<P: TypeFoldable<I>>( delegate: &'a D, variables: &'a mut Vec<I::GenericArg>, + is_hir_typeck_root_goal: bool, input: QueryInput<I, P>, ) -> ty::Canonical<I, QueryInput<I, P>> { // First canonicalize the `param_env` while keeping `'static` @@ -189,7 +201,9 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: false }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal, + }), variables, variable_lookup_table, @@ -296,7 +310,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { } } - fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { + fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -413,10 +427,10 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz // We don't canonicalize `ReStatic` in the `param_env` as we use it // when checking whether a `ParamEnv` candidate is global. ty::ReStatic => match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { CanonicalVarKind::Region(ty::UniverseIndex::ROOT) } - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { .. } => return r, }, @@ -428,12 +442,12 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { panic!("unexpected region in response: {r:?}") } @@ -441,7 +455,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz ty::RePlaceholder(placeholder) => match self.canonicalize_mode { // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { max_input_universe } => { // If we have a placeholder region inside of a query, it must be from // a new universe. @@ -459,9 +473,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: _ } => { - CanonicalVarKind::Region(ty::UniverseIndex::ROOT) - } + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) } @@ -469,16 +481,34 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz } }; - let var = self.get_or_insert_bound_var(r, kind); + let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + let var = ty::BoundVar::from(self.variables.len()); + self.variables.push(r.into()); + self.var_kinds.push(kind); + var + } else { + self.get_or_insert_bound_var(r, kind) + }; Region::new_anon_bound(self.cx(), self.binder_index, var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + // If we're canonicalizing a root goal during HIR typeck, we + // must not use the `cache` as we want to map each occurrence + // of a region to a unique existential variable. + self.inner_fold_ty(t) + } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { ty } else { - let res = self.cached_fold_ty(t); + let res = self.inner_fold_ty(t); let old = self.cache.insert((self.binder_index, t), res); assert_eq!(old, None); res @@ -541,9 +571,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { max_input_universe: _ } => {} - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { panic!("erasing 'static in env") } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index c281ef216c7..b2d40146348 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,8 +10,8 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, - TypeVisitor, TypingMode, Upcast as _, elaborate, + self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument}; @@ -132,6 +132,7 @@ where }) .enter(|ecx| { Self::match_assumption(ecx, goal, assumption, |ecx| { + ecx.try_evaluate_added_goals()?; source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -1069,8 +1070,10 @@ where } else { ControlFlow::Continue(()) } - } else { + } else if ty.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) { ty.super_visit_with(self) + } else { + ControlFlow::Continue(()) } } @@ -1086,8 +1089,10 @@ where } else { ControlFlow::Continue(()) } - } else { + } else if ct.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) { ct.super_visit_with(self) + } else { + ControlFlow::Continue(()) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 5ed316aa6b1..74c5b49ea92 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -53,20 +53,19 @@ where { /// Canonicalizes the goal remembering the original values /// for each bound variable. + /// + /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( &self, + is_hir_typeck_root_goal: bool, goal: Goal<I, I::Predicate>, + opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) { - // We only care about one entry per `OpaqueTypeKey` here, - // so we only canonicalize the lookup table and ignore - // duplicate entries. - let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let mut orig_values = Default::default(); let canonical = Canonicalizer::canonicalize_input( self.delegate, &mut orig_values, + is_hir_typeck_root_goal, QueryInput { goal, predefined_opaques_in_body: self diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index ce9b794d40d..8671cc7c3d3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -20,6 +20,7 @@ use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; +use crate::resolve::eager_resolve_vars; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; @@ -440,6 +441,7 @@ where return Ok(( NestedNormalizationGoals::empty(), GoalEvaluation { + goal, certainty: Certainty::Maybe(stalled_on.stalled_cause), has_changed: HasChanged::No, stalled_on: Some(stalled_on), @@ -447,7 +449,16 @@ where )); } - let (orig_values, canonical_goal) = self.canonicalize_goal(goal); + // We only care about one entry per `OpaqueTypeKey` here, + // so we only canonicalize the lookup table and ignore + // duplicate entries. + let opaque_types = self.delegate.clone_opaque_types_lookup_table(); + let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + + let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) + && self.delegate.in_hir_typeck(); + let (orig_values, canonical_goal) = + self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( @@ -525,7 +536,10 @@ where }, }; - Ok((normalization_nested_goals, GoalEvaluation { certainty, has_changed, stalled_on })) + Ok(( + normalization_nested_goals, + GoalEvaluation { goal, certainty, has_changed, stalled_on }, + )) } pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> { @@ -661,7 +675,7 @@ where let ( NestedNormalizationGoals(nested_goals), - GoalEvaluation { certainty, stalled_on, has_changed: _ }, + GoalEvaluation { goal, certainty, stalled_on, has_changed: _ }, ) = self.evaluate_goal_raw( GoalEvaluationKind::Nested, source, @@ -699,7 +713,15 @@ where // FIXME: Do we need to eagerly resolve here? Or should we check // if the cache key has any changed vars? let with_resolved_vars = self.resolve_vars_if_possible(goal); - if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + if pred.alias + != with_resolved_vars + .predicate + .as_normalizes_to() + .unwrap() + .no_bound_vars() + .unwrap() + .alias + { unchanged_certainty = None; } @@ -711,7 +733,7 @@ where } } } else { - let GoalEvaluation { certainty, has_changed, stalled_on } = + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; if has_changed == HasChanged::Yes { unchanged_certainty = None; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5ea3f0d1061..aec9594b834 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -252,8 +252,6 @@ where return None; } - // FIXME(-Znext-solver): Add support to merge region constraints in - // responses to deal with trait-system-refactor-initiative#27. let one = responses[0]; if responses[1..].iter().all(|&resp| resp == one) { return Some(one); @@ -388,6 +386,23 @@ fn response_no_constraints_raw<I: Interner>( /// The result of evaluating a goal. pub struct GoalEvaluation<I: Interner> { + /// The goal we've evaluated. This is the input goal, but potentially with its + /// inference variables resolved. This never applies any inference constraints + /// from evaluating the goal. + /// + /// We rely on this to check whether root goals in HIR typeck had an unresolved + /// type inference variable in the input. We must not resolve this after evaluating + /// the goal as even if the inference variable has been resolved by evaluating the + /// goal itself, this goal may still end up failing due to region uniquification + /// later on. + /// + /// This is used as a minor optimization to avoid re-resolving inference variables + /// when reevaluating ambiguous goals. E.g. if we've got a goal `?x: Trait` with `?x` + /// already being constrained to `Vec<?y>`, then the first evaluation resolves it to + /// `Vec<?y>: Trait`. If this goal is still ambiguous and we later resolve `?y` to `u32`, + /// then reevaluating this goal now only needs to resolve `?y` while it would otherwise + /// have to resolve both `?x` and `?y`, + pub goal: Goal<I, I::Predicate>, pub certainty: Certainty, pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index a92012f8329..88f93782de1 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -9,6 +9,7 @@ bitflags = "2.4.1" rustc-literal-escaper = "0.0.5" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 6de001fc998..947f3df179f 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -126,23 +126,29 @@ pub(super) fn report_suspicious_mismatch_block( } } -pub(crate) fn make_unclosed_delims_error( - unmatched: UnmatchedDelim, - psess: &ParseSess, -) -> Option<Diag<'_>> { - // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to - // `unmatched_delims` only for error recovery in the `Parser`. - let found_delim = unmatched.found_delim?; - let mut spans = vec![unmatched.found_span]; - if let Some(sp) = unmatched.unclosed_span { - spans.push(sp); - }; - let err = psess.dcx().create_err(MismatchedClosingDelimiter { - spans, - delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(), - unmatched: unmatched.found_span, - opening_candidate: unmatched.candidate_span, - unclosed: unmatched.unclosed_span, - }); - Some(err) +pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>( + unmatcheds: &[UnmatchedDelim], + psess: &'psess ParseSess, +) -> Vec<Diag<'psess>> { + unmatcheds + .iter() + .filter_map(|unmatched| { + // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to + // `unmatched_delims` only for error recovery in the `Parser`. + let found_delim = unmatched.found_delim?; + let mut spans = vec![unmatched.found_span]; + if let Some(sp) = unmatched.unclosed_span { + spans.push(sp); + }; + let err = psess.dcx().create_err(MismatchedClosingDelimiter { + spans, + delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()) + .to_string(), + unmatched: unmatched.found_span, + opening_candidate: unmatched.candidate_span, + unclosed: unmatched.unclosed_span, + }); + Some(err) + }) + .collect() } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 60d275bf2b4..85af5a615ae 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,4 +1,4 @@ -use diagnostics::make_unclosed_delims_error; +use diagnostics::make_errors_for_mismatched_closing_delims; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; @@ -71,27 +71,23 @@ pub(crate) fn lex_token_trees<'psess, 'src>( }; let res = lexer.lex_token_trees(/* is_delimited */ false); - let mut unmatched_delims: Vec<_> = lexer - .diag_info - .unmatched_delims - .into_iter() - .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess)) - .collect(); + let mut unmatched_closing_delims: Vec<_> = + make_errors_for_mismatched_closing_delims(&lexer.diag_info.unmatched_delims, psess); match res { Ok((_open_spacing, stream)) => { - if unmatched_delims.is_empty() { + if unmatched_closing_delims.is_empty() { Ok(stream) } else { // Return error if there are unmatched delimiters or unclosed delimiters. - Err(unmatched_delims) + Err(unmatched_closing_delims) } } Err(errs) => { // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch // because the delimiter mismatch is more likely to be the root cause of error - unmatched_delims.extend(errs); - Err(unmatched_delims) + unmatched_closing_delims.extend(errs); + Err(unmatched_closing_delims) } } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 64748199f28..634f4c30b26 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -51,45 +51,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } - fn eof_err(&mut self) -> Diag<'psess> { - let msg = "this file contains an unclosed delimiter"; - let mut err = self.dcx().struct_span_err(self.token.span, msg); - - let unclosed_delimiter_show_limit = 5; - let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len()); - for &(_, span) in &self.diag_info.open_delimiters[..len] { - err.span_label(span, "unclosed delimiter"); - self.diag_info.unmatched_delims.push(UnmatchedDelim { - found_delim: None, - found_span: self.token.span, - unclosed_span: Some(span), - candidate_span: None, - }); - } - - if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit) - && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2 - { - err.span_label( - *span, - format!( - "another {} unclosed delimiters begin from here", - self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit - ), - ); - } - - if let Some((delim, _)) = self.diag_info.open_delimiters.last() { - report_suspicious_mismatch_block( - &mut err, - &self.diag_info, - self.psess.source_map(), - *delim, - ) - } - err - } - fn lex_token_tree_open_delim( &mut self, open_delim: Delimiter, @@ -206,13 +167,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } else if let Some(glued) = self.token.glue(&next_tok) { self.token = glued; } else { - let this_spacing = if next_tok.is_punct() { - Spacing::Joint - } else if next_tok == token::Eof { - Spacing::Alone - } else { - Spacing::JointHidden - }; + let this_spacing = self.calculate_spacing(&next_tok); break (this_spacing, next_tok); } }; @@ -223,23 +178,64 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Cut-down version of `bump` used when the token kind is known in advance. fn bump_minimal(&mut self) -> Spacing { let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor(); - let this_spacing = if is_next_tok_preceded_by_whitespace { Spacing::Alone } else { - if next_tok.is_punct() { - Spacing::Joint - } else if next_tok == token::Eof { - Spacing::Alone - } else { - Spacing::JointHidden - } + self.calculate_spacing(&next_tok) }; - self.token = next_tok; this_spacing } + fn calculate_spacing(&self, next_tok: &Token) -> Spacing { + if next_tok.is_punct() { + Spacing::Joint + } else if *next_tok == token::Eof { + Spacing::Alone + } else { + Spacing::JointHidden + } + } + + fn eof_err(&mut self) -> Diag<'psess> { + const UNCLOSED_DELIMITER_SHOW_LIMIT: usize = 5; + let msg = "this file contains an unclosed delimiter"; + let mut err = self.dcx().struct_span_err(self.token.span, msg); + + let len = usize::min(UNCLOSED_DELIMITER_SHOW_LIMIT, self.diag_info.open_delimiters.len()); + for &(_, span) in &self.diag_info.open_delimiters[..len] { + err.span_label(span, "unclosed delimiter"); + self.diag_info.unmatched_delims.push(UnmatchedDelim { + found_delim: None, + found_span: self.token.span, + unclosed_span: Some(span), + candidate_span: None, + }); + } + + if let Some((_, span)) = self.diag_info.open_delimiters.get(UNCLOSED_DELIMITER_SHOW_LIMIT) + && self.diag_info.open_delimiters.len() >= UNCLOSED_DELIMITER_SHOW_LIMIT + 2 + { + err.span_label( + *span, + format!( + "another {} unclosed delimiters begin from here", + self.diag_info.open_delimiters.len() - UNCLOSED_DELIMITER_SHOW_LIMIT + ), + ); + } + + if let Some((delim, _)) = self.diag_info.open_delimiters.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + self.psess.source_map(), + *delim, + ) + } + err + } + fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> { // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3d89530f914..54d8a791025 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -785,9 +785,13 @@ impl<'a> Parser<'a> { ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", ExprKind::Use(_, _) => "`.use`", + ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), + _ => unreachable!( + "did not expect {:?} as an illegal postfix operator following cast", + with_postfix.kind + ), } ); let mut err = self.dcx().struct_span_err(span, msg); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index b767b0fcf99..65d84b3e3d9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1973,21 +1973,21 @@ impl<'a> Parser<'a> { format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)); // Try to recover extra trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { - if let Some(last_segment) = segments.last() { - let guar = self.check_trailing_angle_brackets( - last_segment, - &[exp!(Comma), exp!(CloseBrace)], - ); - if let Some(_guar) = guar { - // Handle a case like `Vec<u8>>,` where we can continue parsing fields - // after the comma - let _ = self.eat(exp!(Comma)); - - // `check_trailing_angle_brackets` already emitted a nicer error, as - // proven by the presence of `_guar`. We can continue parsing. - return Ok(a_var); - } + if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind + && let Some(last_segment) = segments.last() + { + let guar = self.check_trailing_angle_brackets( + last_segment, + &[exp!(Comma), exp!(CloseBrace)], + ); + if let Some(_guar) = guar { + // Handle a case like `Vec<u8>>,` where we can continue parsing fields + // after the comma + let _ = self.eat(exp!(Comma)); + + // `check_trailing_angle_brackets` already emitted a nicer error, as + // proven by the presence of `_guar`. We can continue parsing. + return Ok(a_var); } } @@ -3034,18 +3034,16 @@ impl<'a> Parser<'a> { if let Ok(t) = &ty { // Check for trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &t.kind { - if let Some(segment) = segments.last() { - if let Some(guar) = - this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) - { - return Ok(( - dummy_arg(segment.ident, guar), - Trailing::No, - UsePreAttrPos::No, - )); - } - } + if let TyKind::Path(_, Path { segments, .. }) = &t.kind + && let Some(segment) = segments.last() + && let Some(guar) = + this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) + { + return Ok(( + dummy_arg(segment.ident, guar), + Trailing::No, + UsePreAttrPos::No, + )); } if this.token != token::Comma && this.token != token::CloseParen { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 15679d23bc5..43a1d779a75 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -2114,15 +2114,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | X1 Y1 Z1 + 4 | | X1 Y1 Z1 | | ____^____- | ||____| | | `X` is a good letter -5 | | 1 -6 | | 2 -7 | | 3 + 5 | | 1 + 6 | | 2 + 7 | | 3 ... | 15 | | X2 Y2 Z2 16 | | X3 Y3 Z3 @@ -2133,15 +2133,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ X1 Y1 Z1 + 4 │ ┃ X1 Y1 Z1 │ ┃┌────╿────┘ │ ┗│━━━━┥ │ │ `X` is a good letter -5 │ │ 1 -6 │ │ 2 -7 │ │ 3 + 5 │ │ 1 + 6 │ │ 2 + 7 │ │ 3 ‡ │ 15 │ │ X2 Y2 Z2 16 │ │ X3 Y3 Z3 @@ -2189,15 +2189,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | 1 -5 | | 2 -6 | | 3 -7 | | X1 Y1 Z1 + 4 | | 1 + 5 | | 2 + 6 | | 3 + 7 | | X1 Y1 Z1 | | _________- -8 | || 4 -9 | || 5 + 8 | || 4 + 9 | || 5 10 | || 6 11 | || X2 Y2 Z2 | ||__________- `Z` is a good letter too @@ -2211,15 +2211,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ 1 -5 │ ┃ 2 -6 │ ┃ 3 -7 │ ┃ X1 Y1 Z1 + 4 │ ┃ 1 + 5 │ ┃ 2 + 6 │ ┃ 3 + 7 │ ┃ X1 Y1 Z1 │ ┃┌─────────┘ -8 │ ┃│ 4 -9 │ ┃│ 5 + 8 │ ┃│ 4 + 9 │ ┃│ 5 10 │ ┃│ 6 11 │ ┃│ X2 Y2 Z2 │ ┃└──────────┘ `Z` is a good letter too diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index cca621103b5..bc4c605afad 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -1,11 +1,14 @@ //! Meta-syntax validation logic of attributes for post-expansion. +use std::slice; + use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, Safety, }; +use rustc_attr_parsing::{AttributeParser, Late}; use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_session::errors::report_lit_error; @@ -266,62 +269,7 @@ pub fn check_builtin_meta_item( ) { if !is_attr_template_compatible(&template, &meta.kind) { // attrs with new parsers are locally validated so excluded here - if matches!( - name, - sym::inline - | sym::export_stable - | sym::ffi_const - | sym::ffi_pure - | sym::rustc_std_internal_symbol - | sym::may_dangle - | sym::rustc_as_ptr - | sym::rustc_pub_transparent - | sym::rustc_const_stable_indirect - | sym::rustc_force_inline - | sym::rustc_confusables - | sym::rustc_skip_during_method_dispatch - | sym::rustc_pass_by_value - | sym::rustc_deny_explicit_impl - | sym::rustc_do_not_implement_via_object - | sym::rustc_coinductive - | sym::const_trait - | sym::rustc_specialization_trait - | sym::rustc_unsafe_specialization_marker - | sym::rustc_allow_incoherent_impl - | sym::rustc_coherence_is_core - | sym::marker - | sym::fundamental - | sym::rustc_paren_sugar - | sym::type_const - | sym::repr - // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres - // ambiguity - | sym::rustc_align - | sym::deprecated - | sym::optimize - | sym::pointee - | sym::cold - | sym::target_feature - | sym::rustc_allow_const_fn_unstable - | sym::naked - | sym::no_mangle - | sym::non_exhaustive - | sym::omit_gdb_pretty_printer_section - | sym::path - | sym::ignore - | sym::must_use - | sym::track_caller - | sym::link_name - | sym::link_ordinal - | sym::export_name - | sym::rustc_macro_transparency - | sym::link_section - | sym::rustc_layout_scalar_valid_range_start - | sym::rustc_layout_scalar_valid_range_end - | sym::no_implicit_prelude - | sym::automatically_derived - | sym::coverage - ) { + if AttributeParser::<Late>::is_parsed_attribute(slice::from_ref(&name)) { return; } emit_malformed_attribute(psess, style, meta.span, name, template); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 96c895e71df..0b329cc38b0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -130,6 +130,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { let mut style = None; match attr { + Attribute::Parsed(AttributeKind::ProcMacro(_)) => { + self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + } + Attribute::Parsed(AttributeKind::ProcMacroAttribute(_)) => { + self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + } + Attribute::Parsed(AttributeKind::ProcMacroDerive { span: attr_span, .. }) => { + self.check_generic_attr( + hir_id, + sym::proc_macro_derive, + *attr_span, + target, + Target::Fn, + ); + self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + } Attribute::Parsed( AttributeKind::SkipDuringMethodDispatch { span: attr_span, .. } | AttributeKind::Coinductive(attr_span) @@ -227,6 +243,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => { self.check_link_section(hir_id, *attr_span, span, target) } + Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => { + self.check_macro_use(hir_id, sym::macro_use, *span, target) + } + Attribute::Parsed(AttributeKind::MacroEscape(span)) => { + self.check_macro_use(hir_id, sym::macro_escape, *span, target) + } Attribute::Parsed(AttributeKind::Naked(attr_span)) => { self.check_naked(hir_id, *attr_span, span, target) } @@ -269,6 +291,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MacroTransparency(_) | AttributeKind::Pointee(..) | AttributeKind::Dummy + | AttributeKind::RustcBuiltinMacro { .. } | AttributeKind::OmitGdbPrettyPrinterSection, ) => { /* do nothing */ } Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { @@ -362,24 +385,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), - [sym::macro_use, ..] | [sym::macro_escape, ..] => { - self.check_macro_use(hir_id, attr, target) - } [sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod), [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::should_panic, ..] => { self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn) } - [sym::proc_macro, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) - } - [sym::proc_macro_attribute, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); - } - [sym::proc_macro_derive, ..] => { - self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn); - self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) - } [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1255,7 +1265,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } - if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() { + if self.tcx.extern_mod_stmt_cnum(hir_id.owner.def_id).is_none() { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, @@ -2411,17 +2421,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { - let Some(name) = attr.name() else { - return; - }; + fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) { match target { Target::ExternCrate | Target::Mod => {} _ => { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MacroUse { name }, ); } @@ -2474,7 +2481,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // Warn on useless empty attributes. // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` let note = if attr.has_any_name(&[ - sym::macro_use, sym::allow, sym::expect, sym::warn, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d987041fe0e..a90d1af87ca 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -368,7 +368,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). fn should_ignore_item(&mut self, def_id: DefId) -> bool { - if let Some(impl_of) = self.tcx.impl_of_method(def_id) { + if let Some(impl_of) = self.tcx.impl_of_assoc(def_id) { if !self.tcx.is_automatically_derived(impl_of) { return false; } @@ -429,7 +429,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { Node::TraitItem(trait_item) => { // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); - if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { + if let Some(trait_id) = self.tcx.trait_of_assoc(trait_item_id) { self.check_def_id(trait_id); } intravisit::walk_trait_item(self, trait_item); diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index b49e8118fe3..2f78c22c748 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -104,10 +104,10 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { fn visit_inline_asm(&mut self, asm: &'tcx hir::InlineAsm<'tcx>, id: hir::HirId) { for (op, _) in asm.operands { - if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op { - if let Some(def_id) = def_id.as_local() { - self.reachable_symbols.insert(def_id); - } + if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op + && let Some(def_id) = def_id.as_local() + { + self.reachable_symbols.insert(def_id); } } intravisit::walk_inline_asm(self, asm, id); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 40999d622dc..9ed7293873f 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -929,10 +929,10 @@ struct CheckTraitImplStable<'tcx> { impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) { - if let Some(def_id) = path.res.opt_def_id() { - if let Some(stab) = self.tcx.lookup_stability(def_id) { - self.fully_stable &= stab.level.is_stable(); - } + if let Some(def_id) = path.res.opt_def_id() + && let Some(stab) = self.tcx.lookup_stability(def_id) + { + self.fully_stable &= stab.level.is_stable(); } intravisit::walk_path(self, path) } @@ -1055,10 +1055,10 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // implications from this crate. remaining_implications.remove(&feature); - if let FeatureStability::Unstable { old_name: Some(alias) } = stability { - if let Some(span) = remaining_lib_features.swap_remove(&alias) { - tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); - } + if let FeatureStability::Unstable { old_name: Some(alias) } = stability + && let Some(span) = remaining_lib_features.swap_remove(&alias) + { + tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); } if remaining_lib_features.is_empty() && remaining_implications.is_empty() { diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index fae88fbba36..88f202919bb 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -75,19 +75,19 @@ impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = expr.kind { - if let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) { - // Every capture of a closure expression is a local in scope, - // that is moved/copied/borrowed into the closure value, and - // for this analysis they are like any other access to a local. - // - // E.g. in `|b| |c| (a, b, c)`, the upvars of the inner closure - // are `a` and `b`, and while `a` is not directly used in the - // outer closure, it needs to be an upvar there too, so that - // the inner closure can take it (from the outer closure's env). - for (&var_id, upvar) in upvars { - self.visit_local_use(var_id, upvar.span); - } + if let hir::ExprKind::Closure(closure) = expr.kind + && let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) + { + // Every capture of a closure expression is a local in scope, + // that is moved/copied/borrowed into the closure value, and + // for this analysis they are like any other access to a local. + // + // E.g. in `|b| |c| (a, b, c)`, the upvars of the inner closure + // are `a` and `b`, and while `a` is not directly used in the + // outer closure, it needs to be an upvar there too, so that + // the inner closure can take it (from the outer closure's env). + for (&var_id, upvar) in upvars { + self.visit_local_use(var_id, upvar.span); } } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 09685640e50..9a9e0db964c 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -950,9 +950,7 @@ impl<Cx: PatCx> Constructor<Cx> { } } Never => write!(f, "!")?, - Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { - write!(f, "_ : {:?}", ty)? - } + Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?, } Ok(()) } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 66df35f9ee4..d9bb93a829b 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -57,6 +57,13 @@ pub trait PatCx: Sized + fmt::Debug { fn is_exhaustive_patterns_feature_on(&self) -> bool; + /// Whether to ensure the non-exhaustiveness witnesses we report for a complete set. This is + /// `false` by default to avoid some exponential blowup cases such as + /// <https://github.com/rust-lang/rust/issues/118437>. + fn exhaustive_witnesses(&self) -> bool { + false + } + /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index b1c646e9884..19446a1efe9 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -994,7 +994,8 @@ impl<Cx: PatCx> PlaceInfo<Cx> { if !missing_ctors.is_empty() && !report_individual_missing_ctors { // Report `_` as missing. missing_ctors = vec![Constructor::Wildcard]; - } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) { + } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) && !cx.exhaustive_witnesses() + { // We need to report a `_` anyway, so listing other constructors would be redundant. // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked // up by diagnostics to add a note about why `_` is required here. @@ -1747,7 +1748,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( // `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches // strictly fewer rows. In that case we can sometimes skip it. See the top of the file for // details. - let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty(); + let ctor_is_relevant = matches!(ctor, Constructor::Missing) + || missing_ctors.is_empty() + || mcx.tycx.exhaustive_witnesses(); let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant)?; let mut witnesses = ensure_sufficient_stack(|| { compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix) diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 0b939ef7816..94f2a127b9b 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code, unreachable_pub)] use rustc_pattern_analysis::constructor::{ Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, }; @@ -22,8 +23,10 @@ fn init_tracing() { .try_init(); } +pub(super) const UNIT: Ty = Ty::Tuple(&[]); +pub(super) const NEVER: Ty = Ty::Enum(&[]); + /// A simple set of types. -#[allow(dead_code)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(super) enum Ty { /// Booleans @@ -38,6 +41,8 @@ pub(super) enum Ty { BigStruct { arity: usize, ty: &'static Ty }, /// A enum with `arity` variants of type `ty`. BigEnum { arity: usize, ty: &'static Ty }, + /// Like `Enum` but non-exhaustive. + NonExhaustiveEnum(&'static [Ty]), } /// The important logic. @@ -47,7 +52,7 @@ impl Ty { match (ctor, *self) { (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(), (Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(), - (Variant(i), Ty::Enum(tys)) => vec![tys[*i]], + (Variant(i), Ty::Enum(tys) | Ty::NonExhaustiveEnum(tys)) => vec![tys[*i]], (Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty], (Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![], _ => panic!("Unexpected ctor {ctor:?} for type {self:?}"), @@ -61,6 +66,7 @@ impl Ty { Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()), Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(), Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(), + Ty::NonExhaustiveEnum(..) => false, } } @@ -90,6 +96,19 @@ impl Ty { .collect(), non_exhaustive: false, }, + Ty::NonExhaustiveEnum(tys) => ConstructorSet::Variants { + variants: tys + .iter() + .map(|ty| { + if ty.is_empty() { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + } + }) + .collect(), + non_exhaustive: true, + }, Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors, Ty::BigEnum { arity, ty } => { let vis = if ty.is_empty() { @@ -113,7 +132,9 @@ impl Ty { match (*self, ctor) { (Ty::Tuple(..), _) => Ok(()), (Ty::BigStruct { .. }, _) => write!(f, "BigStruct"), - (Ty::Enum(..), Constructor::Variant(i)) => write!(f, "Enum::Variant{i}"), + (Ty::Enum(..) | Ty::NonExhaustiveEnum(..), Constructor::Variant(i)) => { + write!(f, "Enum::Variant{i}") + } (Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"), _ => write!(f, "{:?}::{:?}", self, ctor), } @@ -126,10 +147,11 @@ pub(super) fn compute_match_usefulness<'p>( ty: Ty, scrut_validity: PlaceValidity, complexity_limit: usize, + exhaustive_witnesses: bool, ) -> Result<UsefulnessReport<'p, Cx>, ()> { init_tracing(); rustc_pattern_analysis::usefulness::compute_match_usefulness( - &Cx, + &Cx { exhaustive_witnesses }, arms, ty, scrut_validity, @@ -138,7 +160,9 @@ pub(super) fn compute_match_usefulness<'p>( } #[derive(Debug)] -pub(super) struct Cx; +pub(super) struct Cx { + exhaustive_witnesses: bool, +} /// The context for pattern analysis. Forwards anything interesting to `Ty` methods. impl PatCx for Cx { @@ -153,6 +177,10 @@ impl PatCx for Cx { false } + fn exhaustive_witnesses(&self) -> bool { + self.exhaustive_witnesses + } + fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize { ty.sub_tys(ctor).len() } @@ -219,16 +247,18 @@ macro_rules! pats { // Entrypoint // Parse `type; ..` ($ty:expr; $($rest:tt)*) => {{ - #[allow(unused_imports)] + #[allow(unused)] use rustc_pattern_analysis::{ constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd}, - pat::DeconstructedPat, + pat::{DeconstructedPat, IndexedPat}, }; let ty = $ty; // The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around // that. + #[allow(unused)] let sub_tys = ::std::iter::repeat(&ty); - let mut vec = Vec::new(); + #[allow(unused)] + let mut vec: Vec<IndexedPat<_>> = Vec::new(); pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*); vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>() }}; @@ -263,6 +293,8 @@ macro_rules! pats { let ctor = Constructor::Wildcard; pats!(@pat($($args)*, ctor:ctor) $($rest)*) }}; + // Nothing + (@ctor($($args:tt)*)) => {}; // Integers and int ranges (@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{ diff --git a/compiler/rustc_pattern_analysis/tests/complexity.rs b/compiler/rustc_pattern_analysis/tests/complexity.rs index 93aecafe48d..4754476f383 100644 --- a/compiler/rustc_pattern_analysis/tests/complexity.rs +++ b/compiler/rustc_pattern_analysis/tests/complexity.rs @@ -16,7 +16,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<( let ty = *patterns[0].ty(); let arms: Vec<_> = patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); - compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit) + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit, false) .map(|_report| ()) } diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs index 3b8f346ef19..14ca0d057f0 100644 --- a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -11,16 +11,30 @@ use rustc_pattern_analysis::usefulness::PlaceValidity; mod common; /// Analyze a match made of these patterns. -fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> { - let ty = *patterns[0].ty(); +fn run( + ty: Ty, + patterns: Vec<DeconstructedPat<Cx>>, + exhaustive_witnesses: bool, +) -> Vec<WitnessPat<Cx>> { let arms: Vec<_> = patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); - let report = - compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX) - .unwrap(); + let report = compute_match_usefulness( + arms.as_slice(), + ty, + PlaceValidity::ValidOnly, + usize::MAX, + exhaustive_witnesses, + ) + .unwrap(); report.non_exhaustiveness_witnesses } +/// Analyze a match made of these patterns. Panics if there are no patterns +fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> { + let ty = *patterns[0].ty(); + run(ty, patterns, true) +} + #[track_caller] fn assert_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) { let witnesses = check(patterns); @@ -35,6 +49,26 @@ fn assert_non_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) { assert!(!witnesses.is_empty()) } +use WhichWitnesses::*; +enum WhichWitnesses { + AllOfThem, + OnlySome, +} + +#[track_caller] +/// We take the type as input to support empty matches. +fn assert_witnesses( + which: WhichWitnesses, + ty: Ty, + patterns: Vec<DeconstructedPat<Cx>>, + expected: Vec<&str>, +) { + let exhaustive_wit = matches!(which, AllOfThem); + let witnesses = run(ty, patterns, exhaustive_wit); + let witnesses: Vec<_> = witnesses.iter().map(|w| format!("{w:?}")).collect(); + assert_eq!(witnesses, expected) +} + #[test] fn test_int_ranges() { let ty = Ty::U8; @@ -59,6 +93,8 @@ fn test_int_ranges() { #[test] fn test_nested() { + // enum E { A(bool), B(bool) } + // ty = (E, E) let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } }; assert_non_exhaustive(pats!(ty; Struct(Variant.0, _), @@ -79,9 +115,73 @@ fn test_nested() { } #[test] +fn test_witnesses() { + // TY = Option<bool> + const TY: Ty = Ty::Enum(&[Ty::Bool, UNIT]); + // ty = (Option<bool>, Option<bool>) + let ty = Ty::Tuple(&[TY, TY]); + assert_witnesses(AllOfThem, ty, vec![], vec!["(_, _)"]); + assert_witnesses( + OnlySome, + ty, + pats!(ty; + (Variant.0(false), Variant.0(false)), + ), + vec!["(Enum::Variant1(_), _)"], + ); + assert_witnesses( + AllOfThem, + ty, + pats!(ty; + (Variant.0(false), Variant.0(false)), + ), + vec![ + "(Enum::Variant0(false), Enum::Variant0(true))", + "(Enum::Variant0(false), Enum::Variant1(_))", + "(Enum::Variant0(true), _)", + "(Enum::Variant1(_), _)", + ], + ); + assert_witnesses( + OnlySome, + ty, + pats!(ty; + (_, Variant.0(false)), + ), + vec!["(_, Enum::Variant1(_))"], + ); + assert_witnesses( + AllOfThem, + ty, + pats!(ty; + (_, Variant.0(false)), + ), + vec!["(_, Enum::Variant0(true))", "(_, Enum::Variant1(_))"], + ); + + let ty = Ty::NonExhaustiveEnum(&[UNIT, UNIT, UNIT]); + assert_witnesses( + OnlySome, + ty, + pats!(ty; + Variant.0, + ), + vec!["_"], + ); + assert_witnesses( + AllOfThem, + ty, + pats!(ty; + Variant.0, + ), + vec!["Enum::Variant1(_)", "Enum::Variant2(_)", "_"], + ); +} + +#[test] fn test_empty() { // `TY = Result<bool, !>` - const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Enum(&[])]); + const TY: Ty = Ty::Enum(&[Ty::Bool, NEVER]); assert_exhaustive(pats!(TY; Variant.0, )); diff --git a/compiler/rustc_pattern_analysis/tests/intersection.rs b/compiler/rustc_pattern_analysis/tests/intersection.rs index 8e6f84dcbc8..d4d390517e2 100644 --- a/compiler/rustc_pattern_analysis/tests/intersection.rs +++ b/compiler/rustc_pattern_analysis/tests/intersection.rs @@ -16,7 +16,7 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> { let arms: Vec<_> = patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); let report = - compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX) + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX, false) .unwrap(); report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect() } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9dd80bc9964..b4fa11a8eb3 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -26,7 +26,7 @@ use rustc_errors::{MultiSpan, listify}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor}; -use rustc_hir::{AmbigArg, ForeignItemKind, ItemId, ItemKind, PatKind}; +use rustc_hir::{AmbigArg, ForeignItemId, ItemId, PatKind}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -204,12 +204,10 @@ where // Something like `fn() {my_method}` type of the method // `impl Pub<Priv> { pub fn my_method() {} }` is considered a private type, // so we need to visit the self type additionally. - if let Some(assoc_item) = tcx.opt_associated_item(def_id) { - if let Some(impl_def_id) = assoc_item.impl_container(tcx) { - try_visit!( - tcx.type_of(impl_def_id).instantiate_identity().visit_with(self) - ); - } + if let Some(assoc_item) = tcx.opt_associated_item(def_id) + && let Some(impl_def_id) = assoc_item.impl_container(tcx) + { + try_visit!(tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)); } } ty::Alias(kind @ (ty::Inherent | ty::Free | ty::Projection), data) => { @@ -599,18 +597,13 @@ impl<'tcx> EmbargoVisitor<'tcx> { DefKind::Struct | DefKind::Union => { // While structs and unions have type privacy, their fields do not. - let item = self.tcx.hir_expect_item(def_id); - if let hir::ItemKind::Struct(_, _, ref struct_def) - | hir::ItemKind::Union(_, _, ref struct_def) = item.kind - { - for field in struct_def.fields() { - let field_vis = self.tcx.local_visibility(field.def_id); - if field_vis.is_accessible_from(module, self.tcx) { - self.reach(field.def_id, macro_ev).ty(); - } + let struct_def = self.tcx.adt_def(def_id); + for field in struct_def.non_enum_variant().fields.iter() { + let def_id = field.did.expect_local(); + let field_vis = self.tcx.local_visibility(def_id); + if field_vis.is_accessible_from(module, self.tcx) { + self.reach(def_id, macro_ev).ty(); } - } else { - bug!("item {:?} with DefKind {:?}", item, def_kind); } } @@ -739,6 +732,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { if let Some(ctor_def_id) = variant.data.ctor_def_id() { self.update(ctor_def_id, variant_ev, Level::Reachable); } + for field in variant.data.fields() { self.update(field.def_id, variant_ev, Level::Reachable); self.reach(field.def_id, variant_ev).ty(); @@ -1644,66 +1638,29 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { self.check(def_id, item_visibility, effective_vis).generics().predicates(); } DefKind::Enum => { - let item = tcx.hir_item(id); - if let hir::ItemKind::Enum(_, _, ref def) = item.kind { - self.check_unnameable(item.owner_id.def_id, effective_vis); - - self.check(item.owner_id.def_id, item_visibility, effective_vis) - .generics() - .predicates(); - - for variant in def.variants { - for field in variant.data.fields() { - self.check(field.def_id, item_visibility, effective_vis).ty(); - } - } - } - } - // Subitems of foreign modules have their own publicity. - DefKind::ForeignMod => { - let item = tcx.hir_item(id); - if let hir::ItemKind::ForeignMod { items, .. } = item.kind { - for &foreign_item in items { - let foreign_item = tcx.hir_foreign_item(foreign_item); - - let ev = self.get(foreign_item.owner_id.def_id); - let vis = tcx.local_visibility(foreign_item.owner_id.def_id); - - if let ForeignItemKind::Type = foreign_item.kind { - self.check_unnameable(foreign_item.owner_id.def_id, ev); - } + self.check_unnameable(def_id, effective_vis); + self.check(def_id, item_visibility, effective_vis).generics().predicates(); - self.check(foreign_item.owner_id.def_id, vis, ev) - .generics() - .predicates() - .ty(); - } + let adt = tcx.adt_def(id.owner_id); + for field in adt.all_fields() { + self.check(field.did.expect_local(), item_visibility, effective_vis).ty(); } } // Subitems of structs and unions have their own publicity. DefKind::Struct | DefKind::Union => { - let item = tcx.hir_item(id); - if let hir::ItemKind::Struct(_, _, ref struct_def) - | hir::ItemKind::Union(_, _, ref struct_def) = item.kind - { - self.check_unnameable(item.owner_id.def_id, effective_vis); - self.check(item.owner_id.def_id, item_visibility, effective_vis) - .generics() - .predicates(); + self.check_unnameable(def_id, effective_vis); + self.check(def_id, item_visibility, effective_vis).generics().predicates(); - for field in struct_def.fields() { - let field_visibility = tcx.local_visibility(field.def_id); - let field_ev = self.get(field.def_id); + let adt = tcx.adt_def(id.owner_id); + for field in adt.all_fields() { + let visibility = min(item_visibility, field.vis.expect_local(), tcx); + let field_ev = self.get(field.did.expect_local()); - self.check( - field.def_id, - min(item_visibility, field_visibility, tcx), - field_ev, - ) - .ty(); - } + self.check(field.did.expect_local(), visibility, field_ev).ty(); } } + // Subitems of foreign modules have their own publicity. + DefKind::ForeignMod => {} // An inherent impl is public when its type is public // Subitems of inherent impls have their own publicity. // A trait impl is public when both its type and its trait are public @@ -1763,6 +1720,19 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { _ => {} } } + + fn check_foreign_item(&mut self, id: ForeignItemId) { + let tcx = self.tcx; + let def_id = id.owner_id.def_id; + let item_visibility = tcx.local_visibility(def_id); + let effective_vis = self.get(def_id); + + if let DefKind::ForeignTy = self.tcx.def_kind(def_id) { + self.check_unnameable(def_id, effective_vis); + } + + self.check(def_id, item_visibility, effective_vis).generics().predicates().ty(); + } } pub fn provide(providers: &mut Providers) { @@ -1791,20 +1761,13 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { if let Some(body_id) = tcx.hir_maybe_body_owned_by(def_id) { visitor.visit_nested_body(body_id.id()); } - } - for id in module.free_items() { - if let ItemKind::Impl(i) = tcx.hir_item(id).kind { - if let Some(item) = i.of_trait { - let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap(); - let trait_ref = trait_ref.instantiate_identity(); - visitor.span = item.path.span; - let _ = visitor.visit_def_id( - trait_ref.def_id, - "trait", - &trait_ref.print_only_trait_path(), - ); - } + if let DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) { + let trait_ref = tcx.impl_trait_ref(def_id).unwrap(); + let trait_ref = trait_ref.instantiate_identity(); + visitor.span = tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().path.span; + let _ = + visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path()); } } } @@ -1895,7 +1858,11 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) { // Check for private types in public interfaces. let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, effective_visibilities }; - for id in tcx.hir_free_items() { + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.free_items() { checker.check_item(id); } + for id in crate_items.foreign_items() { + checker.check_foreign_item(id); + } } diff --git a/compiler/rustc_public/src/alloc.rs b/compiler/rustc_public/src/alloc.rs index 75ad31022ff..0c35b3b25df 100644 --- a/compiler/rustc_public/src/alloc.rs +++ b/compiler/rustc_public/src/alloc.rs @@ -33,7 +33,7 @@ fn new_empty_allocation(align: Align) -> Allocation { #[allow(rustc::usage_of_qualified_ty)] pub(crate) fn new_allocation<'tcx>( ty: rustc_middle::ty::Ty<'tcx>, - const_value: ConstValue<'tcx>, + const_value: ConstValue, tables: &mut Tables<'tcx, BridgeTys>, cx: &CompilerCtxt<'tcx, BridgeTys>, ) -> Allocation { @@ -44,7 +44,7 @@ pub(crate) fn new_allocation<'tcx>( #[allow(rustc::usage_of_qualified_ty)] pub(crate) fn try_new_allocation<'tcx>( ty: rustc_middle::ty::Ty<'tcx>, - const_value: ConstValue<'tcx>, + const_value: ConstValue, tables: &mut Tables<'tcx, BridgeTys>, cx: &CompilerCtxt<'tcx, BridgeTys>, ) -> Result<Allocation, Error> { @@ -54,8 +54,8 @@ pub(crate) fn try_new_allocation<'tcx>( alloc::try_new_scalar(layout, scalar, cx).map(|alloc| alloc.stable(tables, cx)) } ConstValue::ZeroSized => Ok(new_empty_allocation(layout.align.abi)), - ConstValue::Slice { data, meta } => { - alloc::try_new_slice(layout, data, meta, cx).map(|alloc| alloc.stable(tables, cx)) + ConstValue::Slice { alloc_id, meta } => { + alloc::try_new_slice(layout, alloc_id, meta, cx).map(|alloc| alloc.stable(tables, cx)) } ConstValue::Indirect { alloc_id, offset } => { let alloc = alloc::try_new_indirect(alloc_id, cx); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 3320b98cd61..3d595286041 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -654,9 +654,7 @@ impl Rvalue { )), AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), - AggregateKind::Coroutine(def, ref args, mov) => { - Ok(Ty::new_coroutine(def, args.clone(), mov)) - } + AggregateKind::Coroutine(def, ref args) => Ok(Ty::new_coroutine(def, args.clone())), AggregateKind::CoroutineClosure(def, ref args) => { Ok(Ty::new_coroutine_closure(def, args.clone())) } @@ -674,8 +672,7 @@ pub enum AggregateKind { Tuple, Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>), Closure(ClosureDef, GenericArgs), - // FIXME(rustc_public): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), + Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), RawPtr(Ty, Mutability), } diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index a433df2dba1..3183c020772 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -428,7 +428,7 @@ fn pretty_aggregate<W: Write>( write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; ")" } - AggregateKind::Coroutine(def, _, _) => { + AggregateKind::Coroutine(def, _) => { write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; ")" } diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index bc67a2f987d..de4b21b1764 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -60,8 +60,8 @@ impl Ty { } /// Create a new coroutine type. - pub fn new_coroutine(def: CoroutineDef, args: GenericArgs, mov: Movability) -> Ty { - Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov)) + pub fn new_coroutine(def: CoroutineDef, args: GenericArgs) -> Ty { + Ty::from_rigid_kind(RigidTy::Coroutine(def, args)) } /// Create a new closure type. @@ -560,8 +560,7 @@ pub enum RigidTy { FnDef(FnDef, GenericArgs), FnPtr(PolyFnSig), Closure(ClosureDef, GenericArgs), - // FIXME(rustc_public): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), + Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind), Never, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index b2d38e497bc..66f767a98f5 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -177,7 +177,7 @@ impl RustcInternal for RigidTy { RigidTy::Closure(def, args) => { rustc_ty::TyKind::Closure(def.0.internal(tables, tcx), args.internal(tables, tcx)) } - RigidTy::Coroutine(def, args, _mov) => { + RigidTy::Coroutine(def, args) => { rustc_ty::TyKind::Coroutine(def.0.internal(tables, tcx), args.internal(tables, tcx)) } RigidTy::CoroutineClosure(def, args) => rustc_ty::TyKind::CoroutineClosure( diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 8dee579e598..be8ee80bed3 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -653,7 +653,6 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { crate::mir::AggregateKind::Coroutine( tables.coroutine_def(*def_id), generic_arg.stable(tables, cx), - cx.coroutine_movability(*def_id).stable(tables, cx), ) } mir::AggregateKind::CoroutineClosure(def_id, generic_args) => { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index d679615b3bd..5a661072bc7 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -457,7 +457,6 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Coroutine(def_id, generic_args) => TyKind::RigidTy(RigidTy::Coroutine( tables.coroutine_def(*def_id), generic_args.stable(tables, cx), - cx.coroutine_movability(*def_id).stable(tables, cx), )), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index 45e2a815470..87f1cc6ae69 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -166,7 +166,7 @@ impl Visitable for RigidTy { } RigidTy::Adt(_, args) | RigidTy::Closure(_, args) - | RigidTy::Coroutine(_, args, _) + | RigidTy::Coroutine(_, args) | RigidTy::CoroutineWitness(_, args) | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), diff --git a/compiler/rustc_public_bridge/src/alloc.rs b/compiler/rustc_public_bridge/src/alloc.rs index ecf9004562c..7e6af342546 100644 --- a/compiler/rustc_public_bridge/src/alloc.rs +++ b/compiler/rustc_public_bridge/src/alloc.rs @@ -38,11 +38,10 @@ pub fn try_new_scalar<'tcx, B: Bridge>( pub fn try_new_slice<'tcx, B: Bridge>( layout: TyAndLayout<'tcx, Ty<'tcx>>, - data: ConstAllocation<'tcx>, + alloc_id: AllocId, meta: u64, cx: &CompilerCtxt<'tcx, B>, ) -> Result<Allocation, B::Error> { - let alloc_id = cx.tcx.reserve_and_set_memory_alloc(data); let ptr = Pointer::new(alloc_id.into(), Size::ZERO); let scalar_ptr = Scalar::from_pointer(ptr, &cx.tcx); let scalar_meta: Scalar = Scalar::from_target_usize(meta, &cx.tcx); diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 612e44b56b1..9b3948d232d 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -63,7 +63,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { self.tcx.coroutine_movability(def_id) } - pub fn valtree_to_const_val(&self, key: ty::Value<'tcx>) -> ConstValue<'tcx> { + pub fn valtree_to_const_val(&self, key: ty::Value<'tcx>) -> ConstValue { self.tcx.valtree_to_const_val(key) } @@ -675,10 +675,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { } /// Try to evaluate an instance into a constant. - pub fn eval_instance( - &self, - instance: ty::Instance<'tcx>, - ) -> Result<ConstValue<'tcx>, ErrorHandled> { + pub fn eval_instance(&self, instance: ty::Instance<'tcx>) -> Result<ConstValue, ErrorHandled> { self.tcx.const_eval_instance( self.fully_monomorphized(), instance, diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 04fc32a9b50..7e258aaa54f 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -498,12 +498,12 @@ impl<D: Deps> DepGraph<D> { #[cfg(debug_assertions)] { - if let Some(target) = task_deps.node { - if let Some(ref forbidden_edge) = data.current.forbidden_edge { - let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; - if forbidden_edge.test(&src, &target) { - panic!("forbidden edge {:?} -> {:?} created", src, target) - } + if let Some(target) = task_deps.node + && let Some(ref forbidden_edge) = data.current.forbidden_edge + { + let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; + if forbidden_edge.test(&src, &target) { + panic!("forbidden edge {:?} -> {:?} created", src, target) } } } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 7e61f5026da..fd1ea997ebe 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -289,10 +289,10 @@ where F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>, { // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query.parent(query_map) { - if let Some(cycle) = visit(query.span(query_map), parent) { - return Some(cycle); - } + if let Some(parent) = query.parent(query_map) + && let Some(cycle) = visit(query.span(query_map), parent) + { + return Some(cycle); } // Visit the explicit waiters which use condvars and are resumable diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 06e59eb4ccc..e74de5edc42 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -720,7 +720,7 @@ fn incremental_verify_ich_failed<Tcx>( static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) }; }; - let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); + let old_in_panic = INSIDE_VERIFY_PANIC.replace(true); if old_in_panic { tcx.sess().dcx().emit_err(crate::error::Reentrant); @@ -739,7 +739,7 @@ fn incremental_verify_ich_failed<Tcx>( panic!("Found unstable fingerprints for {dep_node:?}: {}", result()); } - INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic)); + INSIDE_VERIFY_PANIC.set(old_in_panic); } /// Ensure that either this query has all green inputs or been executed. diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index aa818cc9c46..39e9a9cc58a 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -41,8 +41,6 @@ resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion = resolve_attributes_starting_with_rustc_are_reserved = attributes starting with `rustc` are reserved for use by the `rustc` compiler -resolve_bad_macro_import = bad macro import - resolve_binding_in_never_pattern = never patterns cannot contain variable bindings .suggestion = use a wildcard `_` instead diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 737577baa7a..7912345ec56 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -11,20 +11,24 @@ use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; use rustc_ast::{ self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem, - ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StaticItem, StmtKind, TyAlias, + ForeignItemKind, Impl, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias, }; +use rustc_attr_data_structures::{AttributeKind, MacroUseArgs}; use rustc_attr_parsing as attr; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; +use rustc_hir::Attribute; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::LoadedMacro; -use rustc_middle::bug; use rustc_middle::metadata::ModChild; use rustc_middle::ty::{Feed, Visibility}; +use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; use rustc_span::{Ident, Span, Symbol, kw, sym}; +use thin_vec::ThinVec; use tracing::debug; use crate::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -42,31 +46,59 @@ type Res = def::Res<NodeId>; impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. - pub(crate) fn define_binding( + pub(crate) fn define_binding_local( &mut self, parent: Module<'ra>, ident: Ident, ns: Namespace, binding: NameBinding<'ra>, ) { - let key = self.new_disambiguated_key(ident, ns); - if let Err(old_binding) = self.try_define(parent, key, binding, false) { + if let Err(old_binding) = self.try_define_local(parent, ident, ns, binding, false) { self.report_conflict(parent, ident, ns, old_binding, binding); } } - fn define( + fn define_local( &mut self, parent: Module<'ra>, ident: Ident, ns: Namespace, res: Res, - vis: Visibility<impl Into<DefId>>, + vis: Visibility, span: Span, expn_id: LocalExpnId, ) { let binding = self.arenas.new_res_binding(res, vis.to_def_id(), span, expn_id); - self.define_binding(parent, ident, ns, binding) + self.define_binding_local(parent, ident, ns, binding); + } + + fn define_extern( + &self, + parent: Module<'ra>, + ident: Ident, + ns: Namespace, + res: Res, + vis: Visibility<DefId>, + span: Span, + expn_id: LocalExpnId, + ) { + let binding = self.arenas.new_res_binding(res, vis, span, expn_id); + // Even if underscore names cannot be looked up, we still need to add them to modules, + // because they can be fetched by glob imports from those modules, and bring traits + // into scope both directly and through glob imports. + let key = BindingKey::new_disambiguated(ident, ns, || { + parent.underscore_disambiguator.update(|d| d + 1); + parent.underscore_disambiguator.get() + }); + if self + .resolution_or_default(parent, key) + .borrow_mut() + .non_glob_binding + .replace(binding) + .is_some() + { + span_bug!(span, "an external binding was already defined"); + } } /// Walks up the tree of definitions starting at `def_id`, @@ -189,7 +221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { visitor.parent_scope.macro_rules } - pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'ra>) { + pub(crate) fn build_reduced_graph_external(&self, module: Module<'ra>) { for child in self.tcx.module_children(module.def_id()) { let parent_scope = ParentScope::module(module, self); self.build_reduced_graph_for_external_crate_res(child, parent_scope) @@ -198,7 +230,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Builds the reduced graph for a single item in an external crate. fn build_reduced_graph_for_external_crate_res( - &mut self, + &self, child: &ModChild, parent_scope: ParentScope<'ra>, ) { @@ -229,7 +261,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _, ) | Res::PrimTy(..) - | Res::ToolMod => self.define(parent, ident, TypeNS, res, vis, span, expansion), + | Res::ToolMod => self.define_extern(parent, ident, TypeNS, res, vis, span, expansion), Res::Def( DefKind::Fn | DefKind::AssocFn @@ -238,9 +270,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | DefKind::AssocConst | DefKind::Ctor(..), _, - ) => self.define(parent, ident, ValueNS, res, vis, span, expansion), + ) => self.define_extern(parent, ident, ValueNS, res, vis, span, expansion), Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => { - self.define(parent, ident, MacroNS, res, vis, span, expansion) + self.define_extern(parent, ident, MacroNS, res, vis, span, expansion) } Res::Def( DefKind::TyParam @@ -442,16 +474,20 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.indeterminate_imports.push(import); match import.kind { - // Don't add unresolved underscore imports to modules - ImportKind::Single { target: Ident { name: kw::Underscore, .. }, .. } => {} ImportKind::Single { target, type_ns_only, .. } => { - self.r.per_ns(|this, ns| { - if !type_ns_only || ns == TypeNS { - let key = BindingKey::new(target, ns); - let mut resolution = this.resolution(current_module, key).borrow_mut(); - resolution.single_imports.insert(import); - } - }); + // Don't add underscore imports to `single_imports` + // because they cannot define any usable names. + if target.name != kw::Underscore { + self.r.per_ns(|this, ns| { + if !type_ns_only || ns == TypeNS { + let key = BindingKey::new(target, ns); + this.resolution_or_default(current_module, key) + .borrow_mut() + .single_imports + .insert(import); + } + }); + } } // We don't add prelude imports to the globs since they only affect lexical scopes, // which are not relevant to import resolution. @@ -704,7 +740,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let expansion = parent_scope.expansion; // Define a name in the type namespace if it is not anonymous. - self.r.define(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion); + self.r.define_local(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion); self.r.feed_visibility(feed, adt_vis); let def_id = feed.key(); @@ -756,7 +792,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ItemKind::Mod(_, ident, ref mod_kind) => { - self.r.define(parent, ident, TypeNS, res, vis, sp, expansion); + self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion); if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind { self.r.mods_with_parse_errors.insert(def_id); @@ -775,10 +811,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ItemKind::Const(box ConstItem { ident, .. }) | ItemKind::Delegation(box Delegation { ident, .. }) | ItemKind::Static(box StaticItem { ident, .. }) => { - self.r.define(parent, ident, ValueNS, res, vis, sp, expansion); + self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion); } ItemKind::Fn(box Fn { ident, .. }) => { - self.r.define(parent, ident, ValueNS, res, vis, sp, expansion); + self.r.define_local(parent, ident, ValueNS, res, vis, sp, expansion); // Functions introducing procedural macros reserve a slot // in the macro namespace as well (see #52225). @@ -787,11 +823,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // These items live in the type namespace. ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => { - self.r.define(parent, ident, TypeNS, res, vis, sp, expansion); + self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion); } ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => { - self.r.define(parent, ident, TypeNS, res, vis, sp, expansion); + self.r.define_local(parent, ident, TypeNS, res, vis, sp, expansion); self.parent_scope.module = self.r.new_local_module( Some(parent), @@ -843,7 +879,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let feed = self.r.feed(ctor_node_id); let ctor_def_id = feed.key(); let ctor_res = self.res(ctor_def_id); - self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion); + self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion); self.r.feed_visibility(feed, ctor_vis); // We need the field visibility spans also for the constructor for E0603. self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata.fields()); @@ -948,18 +984,17 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // more details: https://github.com/rust-lang/rust/pull/111761 return; } - let entry = self - .r - .extern_prelude - .entry(ident) - .or_insert(ExternPreludeEntry { binding: None, introduced_by_item: true }); + let entry = self.r.extern_prelude.entry(ident).or_insert(ExternPreludeEntry { + binding: Cell::new(None), + introduced_by_item: true, + }); if orig_name.is_some() { entry.introduced_by_item = true; } // Binding from `extern crate` item in source code can replace // a binding from `--extern` on command line here. if !entry.is_import() { - entry.binding = Some(imported_binding) + entry.binding.set(Some(imported_binding)); } else if ident.name != kw::Underscore { self.r.dcx().span_delayed_bug( item.span, @@ -967,7 +1002,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ); } } - self.r.define_binding(parent, ident, TypeNS, imported_binding); + self.r.define_binding_local(parent, ident, TypeNS, imported_binding); } /// Constructs the reduced graph for one foreign item. @@ -984,7 +1019,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; let vis = self.resolve_visibility(&item.vis); - self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion); + self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion); self.r.feed_visibility(feed, vis); } @@ -1019,42 +1054,31 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { /// Returns `true` if we should consider the underlying `extern crate` to be used. fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> bool { let mut import_all = None; - let mut single_imports = Vec::new(); - for attr in &item.attrs { - if attr.has_name(sym::macro_use) { - if self.parent_scope.module.parent.is_some() { - self.r.dcx().emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { - span: item.span, - }); - } - if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind - && orig_name == kw::SelfLower - { - self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); - } - let ill_formed = |span| { - self.r.dcx().emit_err(errors::BadMacroImport { span }); - }; - match attr.meta() { - Some(meta) => match meta.kind { - MetaItemKind::Word => { - import_all = Some(meta.span); - break; - } - MetaItemKind::List(meta_item_inners) => { - for meta_item_inner in meta_item_inners { - match meta_item_inner.ident() { - Some(ident) if meta_item_inner.is_word() => { - single_imports.push(ident) - } - _ => ill_formed(meta_item_inner.span()), - } - } - } - MetaItemKind::NameValue(..) => ill_formed(meta.span), - }, - None => ill_formed(attr.span), - } + let mut single_imports = ThinVec::new(); + if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) = + AttributeParser::parse_limited( + self.r.tcx.sess, + &item.attrs, + sym::macro_use, + item.span, + item.id, + None, + ) + { + if self.parent_scope.module.parent.is_some() { + self.r + .dcx() + .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span }); + } + if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind + && orig_name == kw::SelfLower + { + self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span }); + } + + match arguments { + MacroUseArgs::UseAll => import_all = Some(span), + MacroUseArgs::UseSpecific(imports) => single_imports = imports, } } @@ -1078,7 +1102,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if let Some(span) = import_all { let import = macro_use_import(self, span, false); self.r.potentially_unused_imports.push(import); - module.for_each_child(self, |this, ident, ns, binding| { + module.for_each_child_mut(self, |this, ident, ns, binding| { if ns == MacroNS { let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module) { @@ -1243,7 +1267,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }); self.r.import_use_map.insert(import, Used::Other); let import_binding = self.r.import(binding, import); - self.r.define_binding(self.r.graph_root, ident, MacroNS, import_binding); + self.r.define_binding_local(self.r.graph_root, ident, MacroNS, import_binding); } else { self.r.check_reserved_macro_name(ident, res); self.insert_unused_macro(ident, def_id, item.id); @@ -1271,7 +1295,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if !vis.is_public() { self.insert_unused_macro(ident, def_id, item.id); } - self.r.define(module, ident, MacroNS, res, vis, span, expansion); + self.r.define_local(module, ident, MacroNS, res, vis, span, expansion); self.r.feed_visibility(feed, vis); self.parent_scope.macro_rules } @@ -1407,10 +1431,13 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if ctxt == AssocCtxt::Trait { let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; - self.r.define(parent, ident, ns, self.res(def_id), vis, item.span, expansion); - } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) { + self.r.define_local(parent, ident, ns, self.res(def_id), vis, item.span, expansion); + } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) + && ident.name != kw::Underscore + { + // Don't add underscore names, they cannot be looked up anyway. let impl_def_id = self.r.tcx.local_parent(local_def_id); - let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns); + let key = BindingKey::new(ident, ns); self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key); } @@ -1492,7 +1519,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let feed = self.r.feed(variant.id); let def_id = feed.key(); let vis = self.resolve_visibility(&variant.vis); - self.r.define(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id); + self.r.define_local(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id); self.r.feed_visibility(feed, vis); // If the variant is marked as non_exhaustive then lower the visibility to within the crate. @@ -1508,7 +1535,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let feed = self.r.feed(ctor_node_id); let ctor_def_id = feed.key(); let ctor_res = self.res(ctor_def_id); - self.r.define(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id); + self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id); self.r.feed_visibility(feed, ctor_vis); } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 81ee02ac3c7..b85a814776a 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -509,9 +509,7 @@ impl Resolver<'_, '_> { let mut check_redundant_imports = FxIndexSet::default(); for module in self.arenas.local_modules().iter() { for (_key, resolution) in self.resolutions(*module).borrow().iter() { - let resolution = resolution.borrow(); - - if let Some(binding) = resolution.best_binding() + if let Some(binding) = resolution.borrow().best_binding() && let NameBindingKind::Import { import, .. } = binding.kind && let ImportKind::Single { id, .. } = import.kind { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d72fbc189e7..3af69b28780 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -523,7 +523,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } pub(crate) fn add_module_candidates( - &mut self, + &self, module: Module<'ra>, names: &mut Vec<TypoSuggestion>, filter_fn: &impl Fn(Res) -> bool, @@ -1076,11 +1076,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } - Scope::CrateRoot => { - let root_ident = Ident::new(kw::PathRoot, ident.span); - let root_module = this.resolve_crate_root(root_ident); - this.add_module_candidates(root_module, &mut suggestions, filter_fn, None); - } Scope::Module(module, _) => { this.add_module_candidates(module, &mut suggestions, filter_fn, None); } @@ -1155,7 +1150,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn lookup_import_candidates_from_module<FilterFn>( - &mut self, + &self, lookup_ident: Ident, namespace: Namespace, parent_scope: &ParentScope<'ra>, @@ -2664,10 +2659,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return None; } - let resolutions = self.resolutions(crate_module).borrow(); let binding_key = BindingKey::new(ident, MacroNS); - let resolution = resolutions.get(&binding_key)?; - let binding = resolution.borrow().binding()?; + let binding = self.resolution(crate_module, binding_key)?.binding()?; let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { return None; }; diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 34d1e9552fd..fe6e5b8e6eb 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -114,9 +114,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { /// including their whole reexport chains. fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { let module = self.r.expect_module(module_id.to_def_id()); - let resolutions = self.r.resolutions(module); - - for (_, name_resolution) in resolutions.borrow().iter() { + for (_, name_resolution) in self.r.resolutions(module).borrow().iter() { let Some(mut binding) = name_resolution.borrow().binding() else { continue; }; diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index b34bcb38f84..d6b1e4de6ea 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -816,13 +816,6 @@ pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { } #[derive(Diagnostic)] -#[diag(resolve_bad_macro_import, code = E0466)] -pub(crate) struct BadMacroImport { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] #[diag(resolve_extern_crate_self_requires_renaming)] pub(crate) struct ExternCrateSelfRequiresRenaming { #[primary_span] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 34941398a2b..f5bc46bf053 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -93,20 +93,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // 6. Language prelude: builtin attributes (closed, controlled). let rust_2015 = ctxt.edition().is_rust_2015(); - let (ns, macro_kind, is_absolute_path) = match scope_set { - ScopeSet::All(ns) => (ns, None, false), - ScopeSet::AbsolutePath(ns) => (ns, None, true), - ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), - ScopeSet::Late(ns, ..) => (ns, None, false), + let (ns, macro_kind) = match scope_set { + ScopeSet::All(ns) + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::Late(ns, ..) => (ns, None), + ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; let module = match scope_set { // Start with the specified module. - ScopeSet::Late(_, module, _) => module, + ScopeSet::Late(_, module, _) | ScopeSet::ModuleAndExternPrelude(_, module) => module, // Jump out of trait or enum modules, they do not act as scopes. _ => parent_scope.module.nearest_item_scope(), }; + let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)); let mut scope = match ns { - _ if is_absolute_path => Scope::CrateRoot, + _ if module_and_extern_prelude => Scope::Module(module, None), TypeNS | ValueNS => Scope::Module(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; @@ -134,11 +135,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } true } - Scope::CrateRoot => true, Scope::Module(..) => true, Scope::MacroUsePrelude => use_prelude || rust_2015, Scope::BuiltinAttrs => true, - Scope::ExternPrelude => use_prelude || is_absolute_path, + Scope::ExternPrelude => use_prelude || module_and_extern_prelude, Scope::ToolPrelude => use_prelude, Scope::StdLibPrelude => use_prelude || ns == MacroNS, Scope::BuiltinTypes => true, @@ -174,7 +174,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } MacroRulesScope::Empty => Scope::Module(module, None), }, - Scope::CrateRoot => match ns { + Scope::Module(..) if module_and_extern_prelude => match ns { TypeNS => { ctxt.adjust(ExpnId::root()); Scope::ExternPrelude @@ -203,7 +203,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => Scope::StdLibPrelude, Scope::BuiltinAttrs => break, // nowhere else to search - Scope::ExternPrelude if is_absolute_path => break, + Scope::ExternPrelude if module_and_extern_prelude => break, Scope::ExternPrelude => Scope::ToolPrelude, Scope::ToolPrelude => Scope::StdLibPrelude, Scope::StdLibPrelude => match ns { @@ -404,10 +404,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let (ns, macro_kind) = match scope_set { - ScopeSet::All(ns) => (ns, None), - ScopeSet::AbsolutePath(ns) => (ns, None), + ScopeSet::All(ns) + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::Late(ns, ..) => (ns, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), - ScopeSet::Late(ns, ..) => (ns, None), }; // This is *the* result, resolution from the scope closest to the resolved identifier. @@ -487,31 +487,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), _ => Err(Determinacy::Determined), }, - Scope::CrateRoot => { - let root_ident = Ident::new(kw::PathRoot, ident.span); - let root_module = this.resolve_crate_root(root_ident); - let binding = this.resolve_ident_in_module( - ModuleOrUniformRoot::Module(root_module), - ident, - ns, - parent_scope, - finalize, - ignore_binding, - ignore_import, - ); - match binding { - Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), - Err((Determinacy::Undetermined, Weak::No)) => { - return Some(Err(Determinacy::determined(force))); - } - Err((Determinacy::Undetermined, Weak::Yes)) => { - Err(Determinacy::Undetermined) - } - Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), - } - } Scope::Module(module, derive_fallback_lint_id) => { - let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; + let (adjusted_parent_scope, finalize) = + if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { + (parent_scope, finalize) + } else { + ( + &ParentScope { module, ..*parent_scope }, + finalize.map(|f| Finalize { used: Used::Scope, ..f }), + ) + }; let binding = this.resolve_ident_in_module_unadjusted( ModuleOrUniformRoot::Module(module), ident, @@ -522,7 +507,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { Shadowing::Restricted }, - finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), + finalize, ignore_binding, ignore_import, ); @@ -776,7 +761,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ModuleOrUniformRoot::ExternPrelude => { ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); } - ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => { + ModuleOrUniformRoot::ModuleAndExternPrelude(..) | ModuleOrUniformRoot::CurrentScope => { // No adjustments } } @@ -810,11 +795,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Result<NameBinding<'ra>, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, - ModuleOrUniformRoot::CrateRootAndExternPrelude => { + ModuleOrUniformRoot::ModuleAndExternPrelude(module) => { assert_eq!(shadowing, Shadowing::Unrestricted); let binding = self.early_resolve_ident_in_lexical_scope( ident, - ScopeSet::AbsolutePath(ns), + ScopeSet::ModuleAndExternPrelude(ns, module), parent_scope, finalize, finalize.is_some(), @@ -863,8 +848,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let key = BindingKey::new(ident, ns); - let resolution = - self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports. + // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding + // doesn't need to be mutable. It will fail when there is a cycle of imports, and without + // the exclusive access infinite recursion will crash the compiler with stack overflow. + let resolution = &*self + .resolution_or_default(module, key) + .try_borrow_mut() + .map_err(|_| (Determined, Weak::No))?; // If the primary binding is unusable, search further and return the shadowed glob // binding if it exists. What we really want here is having two separate scopes in @@ -1531,7 +1521,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && self.tcx.sess.at_least_rust_2018() { // `::a::b` from 2015 macro on 2018 global edition - module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); + let crate_root = self.resolve_crate_root(ident); + module = Some(ModuleOrUniformRoot::ModuleAndExternPrelude(crate_root)); continue; } if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 0a4c25b0eb0..156df45147f 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -25,7 +25,7 @@ use rustc_span::{Ident, Span, Symbol, kw, sym}; use smallvec::SmallVec; use tracing::debug; -use crate::Namespace::*; +use crate::Namespace::{self, *}; use crate::diagnostics::{DiagMode, Suggestion, import_candidates}; use crate::errors::{ CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate, @@ -181,10 +181,10 @@ pub(crate) struct ImportData<'ra> { /// /// | `module_path` | `imported_module` | remark | /// |-|-|-| - /// |`use prefix::foo`| `ModuleOrUniformRoot::Module(prefix)` | - | - /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | - /// |`use ::foo` | `ModuleOrUniformRoot::CrateRootAndExternPrelude` | a special case in 2015 edition | - /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | + /// |`use prefix::foo`| `ModuleOrUniformRoot::Module(prefix)` | - | + /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | + /// |`use ::foo` | `ModuleOrUniformRoot::ModuleAndExternPrelude` | a special case in 2015 edition | + /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>, pub vis: Visibility, } @@ -334,18 +334,25 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } /// Define the name or return the existing binding if there is a collision. - /// `update` indicates if the definition is a redefinition of an existing binding. - pub(crate) fn try_define( + pub(crate) fn try_define_local( &mut self, module: Module<'ra>, - key: BindingKey, + ident: Ident, + ns: Namespace, binding: NameBinding<'ra>, warn_ambiguity: bool, ) -> Result<(), NameBinding<'ra>> { let res = binding.res(); - self.check_reserved_macro_name(key.ident, res); + self.check_reserved_macro_name(ident, res); self.set_binding_parent_module(binding, module); - self.update_resolution(module, key, warn_ambiguity, |this, resolution| { + // Even if underscore names cannot be looked up, we still need to add them to modules, + // because they can be fetched by glob imports from those modules, and bring traits + // into scope both directly and through glob imports. + let key = BindingKey::new_disambiguated(ident, ns, || { + module.underscore_disambiguator.update(|d| d + 1); + module.underscore_disambiguator.get() + }); + self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { if let Some(old_binding) = resolution.best_binding() { if res == Res::Err && old_binding.res() != Res::Err { // Do not override real bindings with `Res::Err`s from error recovery. @@ -383,7 +390,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (old_glob @ true, false) | (old_glob @ false, true) => { let (glob_binding, non_glob_binding) = if old_glob { (old_binding, binding) } else { (binding, old_binding) }; - if key.ns == MacroNS + if ns == MacroNS && non_glob_binding.expansion != LocalExpnId::ROOT && glob_binding.res() != non_glob_binding.res() { @@ -448,7 +455,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. - fn update_resolution<T, F>( + fn update_local_resolution<T, F>( &mut self, module: Module<'ra>, key: BindingKey, @@ -461,7 +468,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Ensure that `resolution` isn't borrowed when defining in the module's glob importers, // during which the resolution might end up getting re-defined via a glob cycle. let (binding, t, warn_ambiguity) = { - let resolution = &mut *self.resolution(module, key).borrow_mut(); + let resolution = &mut *self.resolution_or_default(module, key).borrow_mut(); let old_binding = resolution.binding(); let t = f(self, resolution); @@ -489,10 +496,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; if self.is_accessible_from(binding.vis, scope) { let imported_binding = self.import(binding, *import); - let key = BindingKey { ident, ..key }; - let _ = self.try_define( + let _ = self.try_define_local( import.parent_scope.module, - key, + ident, + key.ns, imported_binding, warn_ambiguity, ); @@ -514,11 +521,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let dummy_binding = self.dummy_binding; let dummy_binding = self.import(dummy_binding, import); self.per_ns(|this, ns| { - let key = BindingKey::new(target, ns); - let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false); - this.update_resolution(import.parent_scope.module, key, false, |_, resolution| { - resolution.single_imports.swap_remove(&import); - }) + let module = import.parent_scope.module; + let _ = this.try_define_local(module, target, ns, dummy_binding, false); + // Don't remove underscores from `single_imports`, they were never added. + if target.name != kw::Underscore { + let key = BindingKey::new(target, ns); + this.update_local_resolution(module, key, false, |_, resolution| { + resolution.single_imports.swap_remove(&import); + }) + } }); self.record_use(target, dummy_binding, Used::Other); } else if import.imported_module.get().is_none() { @@ -639,7 +650,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for module in self.arenas.local_modules().iter() { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); - let Some(binding) = resolution.best_binding() else { continue }; if let NameBindingKind::Import { import, .. } = binding.kind @@ -891,14 +901,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // We need the `target`, `source` can be extracted. let imported_binding = this.import(binding, import); - this.define_binding(parent, target, ns, imported_binding); + this.define_binding_local(parent, target, ns, imported_binding); PendingBinding::Ready(Some(imported_binding)) } Err(Determinacy::Determined) => { - // Don't update the resolution for underscores, because it was never added. + // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { let key = BindingKey::new(target, ns); - this.update_resolution(parent, key, false, |_, resolution| { + this.update_local_resolution(parent, key, false, |_, resolution| { resolution.single_imports.swap_remove(&import); }); } @@ -1190,41 +1200,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); return if all_ns_failed { - let resolutions = match module { - ModuleOrUniformRoot::Module(module) => Some(self.resolutions(module).borrow()), - _ => None, - }; - let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); - let names = resolutions - .filter_map(|(BindingKey { ident: i, .. }, resolution)| { - if i.name == ident.name { - return None; - } // Never suggest the same name - match *resolution.borrow() { - ref resolution - if let Some(name_binding) = resolution.best_binding() => - { - match name_binding.kind { - NameBindingKind::Import { binding, .. } => { - match binding.kind { - // Never suggest the name that has binding error - // i.e., the name that cannot be previously resolved - NameBindingKind::Res(Res::Err) => None, - _ => Some(i.name), + let names = match module { + ModuleOrUniformRoot::Module(module) => { + self.resolutions(module) + .borrow() + .iter() + .filter_map(|(BindingKey { ident: i, .. }, resolution)| { + if i.name == ident.name { + return None; + } // Never suggest the same name + + let resolution = resolution.borrow(); + if let Some(name_binding) = resolution.best_binding() { + match name_binding.kind { + NameBindingKind::Import { binding, .. } => { + match binding.kind { + // Never suggest the name that has binding error + // i.e., the name that cannot be previously resolved + NameBindingKind::Res(Res::Err) => None, + _ => Some(i.name), + } } + _ => Some(i.name), } - _ => Some(i.name), + } else if resolution.single_imports.is_empty() { + None + } else { + Some(i.name) } - } - NameResolution { ref single_imports, .. } - if single_imports.is_empty() => - { - None - } - _ => Some(i.name), - } - }) - .collect::<Vec<Symbol>>(); + }) + .collect() + } + _ => Vec::new(), + }; let lev_suggestion = find_best_match_for_name(&names, ident.name, None).map(|suggestion| { @@ -1505,12 +1513,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let imported_binding = self.import(binding, import); let warn_ambiguity = self .resolution(import.parent_scope.module, key) - .borrow() - .binding() + .and_then(|r| r.binding()) .is_some_and(|binding| binding.warn_ambiguity_recursive()); - let _ = self.try_define( + let _ = self.try_define_local( import.parent_scope.module, - key, + key.ident, + key.ns, imported_binding, warn_ambiguity, ); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a3a770502de..261d099abdc 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -910,22 +910,15 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc span, |this| { this.visit_generic_params(&fn_ptr.generic_params, false); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: ty.id, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - ty.id, - false, - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - fn_ptr.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &fn_ptr.decl.output, - ) - }, - ); + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + fn_ptr.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &fn_ptr.decl.output, + false, + ) }, ) } @@ -1042,19 +1035,12 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_fn_header(&sig.header); self.visit_ident(ident); self.visit_generics(generics); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - fn_id, - sig.decl.has_self(), - sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &sig.decl.output, - ); - }, + self.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + false, ); return; } @@ -1080,22 +1066,15 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc .coroutine_kind .map(|coroutine_kind| coroutine_kind.return_id()); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: coro_node_id.is_some(), - }, - |this| { - this.resolve_fn_signature( - fn_id, - declaration.has_self(), - declaration - .inputs - .iter() - .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), - &declaration.output, - ); - }, + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + coro_node_id.is_some(), ); if let Some(contract) = contract { @@ -1307,19 +1286,12 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc kind: LifetimeBinderKind::PolyTrait, .. } => { - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - binder, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, - ) - }, + self.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + false, ); break; } @@ -2236,25 +2208,32 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { has_self: bool, inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone, output_ty: &'ast FnRetTy, + report_elided_lifetimes_in_path: bool, ) { - // Add each argument to the rib. - let elision_lifetime = self.resolve_fn_params(has_self, inputs); - debug!(?elision_lifetime); - - let outer_failures = take(&mut self.diag_metadata.current_elision_failures); - let output_rib = if let Ok(res) = elision_lifetime.as_ref() { - self.r.lifetime_elision_allowed.insert(fn_id); - LifetimeRibKind::Elided(*res) - } else { - LifetimeRibKind::ElisionFailure + let rib = LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: report_elided_lifetimes_in_path, }; - self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, output_ty)); - let elision_failures = - replace(&mut self.diag_metadata.current_elision_failures, outer_failures); - if !elision_failures.is_empty() { - let Err(failure_info) = elision_lifetime else { bug!() }; - self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); - } + self.with_lifetime_rib(rib, |this| { + // Add each argument to the rib. + let elision_lifetime = this.resolve_fn_params(has_self, inputs); + debug!(?elision_lifetime); + + let outer_failures = take(&mut this.diag_metadata.current_elision_failures); + let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + this.r.lifetime_elision_allowed.insert(fn_id); + LifetimeRibKind::Elided(*res) + } else { + LifetimeRibKind::ElisionFailure + }; + this.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, output_ty)); + let elision_failures = + replace(&mut this.diag_metadata.current_elision_failures, outer_failures); + if !elision_failures.is_empty() { + let Err(failure_info) = elision_lifetime else { bug!() }; + this.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + } + }); } /// Resolve inside function parameters and parameter types. @@ -3449,8 +3428,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; ident.span.normalize_to_macros_2_0_and_adjust(module.expansion); let key = BindingKey::new(ident, ns); - let mut binding = - self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding()); + let mut binding = self.r.resolution(module, key).and_then(|r| r.best_binding()); debug!(?binding); if binding.is_none() { // We could not find the trait item in the correct namespace. @@ -3461,8 +3439,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { _ => ns, }; let key = BindingKey::new(ident, ns); - binding = - self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.best_binding()); + binding = self.r.resolution(module, key).and_then(|r| r.best_binding()); debug!(?binding); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 69095942f52..98e48664e68 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -818,10 +818,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // If the first argument in call is `self` suggest calling a method. if let Some((call_span, args_span)) = self.call_has_self_arg(source) { let mut args_snippet = String::new(); - if let Some(args_span) = args_span { - if let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span) { - args_snippet = snippet; - } + if let Some(args_span) = args_span + && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span) + { + args_snippet = snippet; } err.span_suggestion( @@ -955,59 +955,57 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), false, ) = (source, res, is_macro) - { - if let Some(bounds @ [first_bound, .., last_bound]) = + && let Some(bounds @ [first_bound, .., last_bound]) = self.diag_metadata.current_trait_object - { - fallback = true; - let spans: Vec<Span> = bounds - .iter() - .map(|bound| bound.span()) - .filter(|&sp| sp != base_error.span) - .collect(); + { + fallback = true; + let spans: Vec<Span> = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_error.span) + .collect(); - let start_span = first_bound.span(); - // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = last_bound.span(); - // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) - let last_bound_span = spans.last().cloned().unwrap(); - let mut multi_span: MultiSpan = spans.clone().into(); - for sp in spans { - let msg = if sp == last_bound_span { - format!( - "...because of {these} bound{s}", - these = pluralize!("this", bounds.len() - 1), - s = pluralize!(bounds.len() - 1), - ) - } else { - String::new() - }; - multi_span.push_span_label(sp, msg); - } - multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); - err.span_help( - multi_span, - "`+` is used to constrain a \"trait object\" type with lifetimes or \ + let start_span = first_bound.span(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = last_bound.span(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {these} bound{s}", + these = pluralize!("this", bounds.len() - 1), + s = pluralize!(bounds.len() - 1), + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ auto-traits; structs and enums can't be bound in that way", - ); - if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, - ast::GenericBound::Trait(tr) => tr.span == base_error.span, - }) { - let mut sugg = vec![]; - if base_error.span != start_span { - sugg.push((start_span.until(base_error.span), String::new())); - } - if base_error.span != end_span { - sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); - } - - err.multipart_suggestion( - "if you meant to use a type and not a trait here, remove the bounds", - sugg, - Applicability::MaybeIncorrect, - ); + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, + ast::GenericBound::Trait(tr) => tr.span == base_error.span, + }) { + let mut sugg = vec![]; + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); + } + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); } } @@ -1151,13 +1149,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } err.code(E0411); err.span_label(span, "`Self` is only available in impls, traits, and type definitions"); - if let Some(item) = self.diag_metadata.current_item { - if let Some(ident) = item.kind.ident() { - err.span_label( - ident.span, - format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()), - ); - } + if let Some(item) = self.diag_metadata.current_item + && let Some(ident) = item.kind.ident() + { + err.span_label( + ident.span, + format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()), + ); } true } @@ -1461,15 +1459,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, None, None) { - let resolutions = self.r.resolutions(module).borrow(); - let targets: Vec<_> = resolutions + let targets: Vec<_> = self + .r + .resolutions(module) + .borrow() .iter() .filter_map(|(key, resolution)| { resolution .borrow() .best_binding() .map(|binding| binding.res()) - .and_then(|res| if filter_fn(res) { Some((key, res)) } else { None }) + .and_then(|res| if filter_fn(res) { Some((*key, res)) } else { None }) }) .collect(); if let [target] = targets.as_slice() { @@ -1932,11 +1932,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor { - if let PathSource::Expr(Some(parent)) = source { - if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind { - bad_struct_syntax_suggestion(self, err, def_id); - return true; - } + if let PathSource::Expr(Some(parent)) = source + && let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind + { + bad_struct_syntax_suggestion(self, err, def_id); + return true; } struct_ctor } else { @@ -2300,8 +2300,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return None; } - let resolutions = self.r.resolutions(*module); - let targets = resolutions + let targets = self + .r + .resolutions(*module) .borrow() .iter() .filter_map(|(key, res)| { @@ -2344,19 +2345,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) { if let Some(node_id) = self.diag_metadata.current_self_type.as_ref().and_then(extract_node_id) + && let Some(resolution) = self.r.partial_res_map.get(&node_id) + && let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = resolution.full_res() + && let Some(fields) = self.r.field_idents(did) + && let Some(field) = fields.iter().find(|id| ident.name == id.name) { // Look for a field with the same name in the current self_type. - if let Some(resolution) = self.r.partial_res_map.get(&node_id) { - if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = - resolution.full_res() - { - if let Some(fields) = self.r.field_idents(did) { - if let Some(field) = fields.iter().find(|id| ident.name == id.name) { - return Some(AssocSuggestion::Field(field.span)); - } - } - } - } + return Some(AssocSuggestion::Field(field.span)); } } @@ -2391,44 +2386,44 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } // Look for associated items in the current trait. - if let Some((module, _)) = self.current_trait_ref { - if let Ok(binding) = self.r.maybe_resolve_ident_in_module( + if let Some((module, _)) = self.current_trait_ref + && let Ok(binding) = self.r.maybe_resolve_ident_in_module( ModuleOrUniformRoot::Module(module), ident, ns, &self.parent_scope, None, - ) { - let res = binding.res(); - if filter_fn(res) { - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => { - let has_self = match def_id.as_local() { - Some(def_id) => self - .r - .delegation_fn_sigs - .get(&def_id) - .is_some_and(|sig| sig.has_self), - None => { - self.r.tcx.fn_arg_idents(def_id).first().is_some_and(|&ident| { - matches!(ident, Some(Ident { name: kw::SelfLower, .. })) - }) - } - }; - if has_self { - return Some(AssocSuggestion::MethodWithSelf { called }); - } else { - return Some(AssocSuggestion::AssocFn { called }); + ) + { + let res = binding.res(); + if filter_fn(res) { + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => { + let has_self = match def_id.as_local() { + Some(def_id) => self + .r + .delegation_fn_sigs + .get(&def_id) + .is_some_and(|sig| sig.has_self), + None => { + self.r.tcx.fn_arg_idents(def_id).first().is_some_and(|&ident| { + matches!(ident, Some(Ident { name: kw::SelfLower, .. })) + }) } + }; + if has_self { + return Some(AssocSuggestion::MethodWithSelf { called }); + } else { + return Some(AssocSuggestion::AssocFn { called }); } - Res::Def(DefKind::AssocConst, _) => { - return Some(AssocSuggestion::AssocConst); - } - Res::Def(DefKind::AssocTy, _) => { - return Some(AssocSuggestion::AssocType); - } - _ => {} } + Res::Def(DefKind::AssocConst, _) => { + return Some(AssocSuggestion::AssocConst); + } + Res::Def(DefKind::AssocTy, _) => { + return Some(AssocSuggestion::AssocType); + } + _ => {} } } } @@ -2630,7 +2625,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { false } - fn find_module(&mut self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> { + fn find_module(&self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> { let mut result = None; let mut seen_modules = FxHashSet::default(); let root_did = self.r.graph_root.def_id(); @@ -2687,7 +2682,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { result } - fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> { + fn collect_enum_ctors(&self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> { self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| { let mut variants = Vec::new(); enum_module.for_each_child(self.r, |_, ident, _, name_binding| { @@ -2704,7 +2699,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Adds a suggestion for using an enum's variant when an enum is used instead. fn suggest_using_enum_variant( - &mut self, + &self, err: &mut Diag<'_>, source: PathSource<'_, '_, '_>, def_id: DefId, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0d41a822e8a..88dfb50b47d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -21,7 +21,7 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, Ref, RefCell}; use std::collections::BTreeSet; use std::fmt; use std::sync::Arc; @@ -119,7 +119,6 @@ enum Scope<'ra> { DeriveHelpers(LocalExpnId), DeriveHelpersCompat, MacroRules(MacroRulesScopeRef<'ra>), - CrateRoot, // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` // lint if it should be reported. Module(Module<'ra>, Option<NodeId>), @@ -139,8 +138,8 @@ enum Scope<'ra> { enum ScopeSet<'ra> { /// All scopes with the given namespace. All(Namespace), - /// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros). - AbsolutePath(Namespace), + /// A module, then extern prelude (used for mixed 2015-2018 mode in macros). + ModuleAndExternPrelude(Namespace, Module<'ra>), /// All scopes with macro namespace and the given macro kind restriction. Macro(MacroKind), /// All scopes with the given namespace, used for partially performing late resolution. @@ -419,8 +418,10 @@ enum ModuleOrUniformRoot<'ra> { /// Regular module. Module(Module<'ra>), - /// Virtual module that denotes resolution in crate root with fallback to extern prelude. - CrateRootAndExternPrelude, + /// Virtual module that denotes resolution in a module with fallback to extern prelude. + /// Used for paths starting with `::` coming from 2015 edition macros + /// used in 2018+ edition crates. + ModuleAndExternPrelude(Module<'ra>), /// Virtual module that denotes resolution in extern prelude. /// Used for paths starting with `::` on 2018 edition. @@ -532,15 +533,26 @@ struct BindingKey { /// identifier. ident: Ident, ns: Namespace, - /// 0 if ident is not `_`, otherwise a value that's unique to the specific - /// `_` in the expanded AST that introduced this binding. + /// When we add an underscore binding (with ident `_`) to some module, this field has + /// a non-zero value that uniquely identifies this binding in that module. + /// For non-underscore bindings this field is zero. + /// When a key is constructed for name lookup (as opposed to name definition), this field is + /// also zero, even for underscore names, so for underscores the lookup will never succeed. disambiguator: u32, } impl BindingKey { fn new(ident: Ident, ns: Namespace) -> Self { - let ident = ident.normalize_to_macros_2_0(); - BindingKey { ident, ns, disambiguator: 0 } + BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator: 0 } + } + + fn new_disambiguated( + ident: Ident, + ns: Namespace, + disambiguator: impl FnOnce() -> u32, + ) -> BindingKey { + let disambiguator = if ident.name == kw::Underscore { disambiguator() } else { 0 }; + BindingKey { ident: ident.normalize_to_macros_2_0(), ns, disambiguator } } } @@ -568,6 +580,8 @@ struct ModuleData<'ra> { lazy_resolutions: Resolutions<'ra>, /// True if this is a module from other crate that needs to be populated on access. populate_on_access: Cell<bool>, + /// Used to disambiguate underscore items (`const _: T = ...`) in the module. + underscore_disambiguator: Cell<u32>, /// Macro invocations that can expand into items in this module. unexpanded_invocations: RefCell<FxHashSet<LocalExpnId>>, @@ -628,6 +642,7 @@ impl<'ra> ModuleData<'ra> { kind, lazy_resolutions: Default::default(), populate_on_access: Cell::new(is_foreign), + underscore_disambiguator: Cell::new(0), unexpanded_invocations: Default::default(), no_implicit_prelude, glob_importers: RefCell::new(Vec::new()), @@ -641,11 +656,23 @@ impl<'ra> ModuleData<'ra> { } impl<'ra> Module<'ra> { - fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F) - where - R: AsMut<Resolver<'ra, 'tcx>>, - F: FnMut(&mut R, Ident, Namespace, NameBinding<'ra>), - { + fn for_each_child<'tcx, R: AsRef<Resolver<'ra, 'tcx>>>( + self, + resolver: &R, + mut f: impl FnMut(&R, Ident, Namespace, NameBinding<'ra>), + ) { + for (key, name_resolution) in resolver.as_ref().resolutions(self).borrow().iter() { + if let Some(binding) = name_resolution.borrow().best_binding() { + f(resolver, key.ident, key.ns, binding); + } + } + } + + fn for_each_child_mut<'tcx, R: AsMut<Resolver<'ra, 'tcx>>>( + self, + resolver: &mut R, + mut f: impl FnMut(&mut R, Ident, Namespace, NameBinding<'ra>), + ) { for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() { if let Some(binding) = name_resolution.borrow().best_binding() { f(resolver, key.ident, key.ns, binding); @@ -654,10 +681,7 @@ impl<'ra> Module<'ra> { } /// This modifies `self` in place. The traits will be stored in `self.traits`. - fn ensure_traits<'tcx, R>(self, resolver: &mut R) - where - R: AsMut<Resolver<'ra, 'tcx>>, - { + fn ensure_traits<'tcx>(self, resolver: &impl AsRef<Resolver<'ra, 'tcx>>) { let mut traits = self.traits.borrow_mut(); if traits.is_none() { let mut collected_traits = Vec::new(); @@ -666,7 +690,7 @@ impl<'ra> Module<'ra> { return; } if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() { - collected_traits.push((name, binding, r.as_mut().get_module(def_id))) + collected_traits.push((name, binding, r.as_ref().get_module(def_id))) } }); *traits = Some(collected_traits.into_boxed_slice()); @@ -985,13 +1009,13 @@ impl<'ra> NameBindingData<'ra> { #[derive(Default, Clone)] struct ExternPreludeEntry<'ra> { - binding: Option<NameBinding<'ra>>, + binding: Cell<Option<NameBinding<'ra>>>, introduced_by_item: bool, } impl ExternPreludeEntry<'_> { fn is_import(&self) -> bool { - self.binding.is_some_and(|binding| binding.is_import()) + self.binding.get().is_some_and(|binding| binding.is_import()) } } @@ -1087,8 +1111,6 @@ pub struct Resolver<'ra, 'tcx> { extern_module_map: RefCell<FxIndexMap<DefId, Module<'ra>>>, binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>, - underscore_disambiguator: u32, - /// Maps glob imports to the names of items actually imported. glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>, glob_error: Option<ErrorGuaranteed>, @@ -1327,6 +1349,12 @@ impl<'ra, 'tcx> AsMut<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> { } } +impl<'ra, 'tcx> AsRef<Resolver<'ra, 'tcx>> for Resolver<'ra, 'tcx> { + fn as_ref(&self) -> &Resolver<'ra, 'tcx> { + self + } +} + impl<'tcx> Resolver<'_, 'tcx> { fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { self.opt_feed(node).map(|f| f.key()) @@ -1500,7 +1528,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { extern_crate_map: Default::default(), module_children: Default::default(), trait_map: NodeMap::default(), - underscore_disambiguator: 0, empty_module, local_module_map, extern_module_map: Default::default(), @@ -1849,7 +1876,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We don't reject trait aliases (`trait_module == None`) because we don't have access to their // associated items. fn trait_may_have_item( - &mut self, + &self, trait_module: Option<Module<'ra>>, assoc_item: Option<(Symbol, Namespace)>, ) -> bool { @@ -1881,18 +1908,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { import_ids } - fn new_disambiguated_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey { - let ident = ident.normalize_to_macros_2_0(); - let disambiguator = if ident.name == kw::Underscore { - self.underscore_disambiguator += 1; - self.underscore_disambiguator - } else { - 0 - }; - BindingKey { ident, ns, disambiguator } - } - - fn resolutions(&mut self, module: Module<'ra>) -> &'ra Resolutions<'ra> { + fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { if module.populate_on_access.get() { module.populate_on_access.set(false); self.build_reduced_graph_external(module); @@ -1901,12 +1917,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn resolution( - &mut self, + &self, + module: Module<'ra>, + key: BindingKey, + ) -> Option<Ref<'ra, NameResolution<'ra>>> { + self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow()) + } + + fn resolution_or_default( + &self, module: Module<'ra>, key: BindingKey, ) -> &'ra RefCell<NameResolution<'ra>> { - *self - .resolutions(module) + self.resolutions(module) .borrow_mut() .entry(key) .or_insert_with(|| self.arenas.alloc_name_resolution()) @@ -1983,7 +2006,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // but not introduce it, as used if they are accessed from lexical scope. if used == Used::Scope { if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) { - if !entry.introduced_by_item && entry.binding == Some(used_binding) { + if !entry.introduced_by_item && entry.binding.get() == Some(used_binding) { return; } } @@ -2147,7 +2170,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let norm_ident = ident.normalize_to_macros_2_0(); let binding = self.extern_prelude.get(&norm_ident).cloned().and_then(|entry| { - Some(if let Some(binding) = entry.binding { + Some(if let Some(binding) = entry.binding.get() { if finalize { if !entry.is_import() { self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); @@ -2172,8 +2195,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) }); - if let Some(entry) = self.extern_prelude.get_mut(&norm_ident) { - entry.binding = binding; + if let Some(entry) = self.extern_prelude.get(&norm_ident) { + entry.binding.set(binding); } binding diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 77ef7f56c09..20504ea609d 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1,6 +1,7 @@ //! A bunch of methods and structures more or less related to resolving macros and //! interface provided by `Resolver` to macro expander. +use std::any::Any; use std::cell::Cell; use std::mem; use std::sync::Arc; @@ -13,10 +14,10 @@ use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind, }; -use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{ AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion, }; +use rustc_expand::{MacroRulesMacroExpander, compile_declarative_macro}; use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_middle::middle::stability; @@ -357,8 +358,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else { continue; }; + let ext: &dyn Any = ext.as_ref(); + let Some(m) = ext.downcast_ref::<MacroRulesMacroExpander>() else { + continue; + }; for arm_i in unused_arms.iter() { - if let Some((ident, rule_span)) = ext.get_unused_rule(arm_i) { + if let Some((ident, rule_span)) = m.get_unused_rule(arm_i) { self.lint_buffer.buffer_lint( UNUSED_MACRO_RULES, node_id, @@ -506,7 +511,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } fn glob_delegation_suffixes( - &mut self, + &self, trait_def_id: DefId, impl_def_id: LocalDefId, ) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> { @@ -530,7 +535,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { target_trait.for_each_child(self, |this, ident, ns, _binding| { // FIXME: Adjust hygiene for idents from globs, like for glob imports. if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) - && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns)) + && overriding_keys.contains(&BindingKey::new(ident, ns)) { // The name is overridden, do not produce it from the glob delegation. } else { @@ -1018,40 +1023,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { node_id: NodeId, ) { let span = path.span; - if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } = + if let Some(stability) = &ext.stability + && let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } = stability.level - { - let feature = stability.feature; - - let is_allowed = - |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); - let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); - if !is_allowed(feature) && !allowed_by_implication { - let lint_buffer = &mut self.lint_buffer; - let soft_handler = |lint, span, msg: String| { - lint_buffer.buffer_lint( - lint, - node_id, - span, - BuiltinLintDiag::UnstableFeature( - // FIXME make this translatable - msg.into(), - ), - ) - }; - stability::report_unstable( - self.tcx.sess, - feature, - reason.to_opt_reason(), - issue, - None, - is_soft, + { + let feature = stability.feature; + + let is_allowed = + |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); + let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); + if !is_allowed(feature) && !allowed_by_implication { + let lint_buffer = &mut self.lint_buffer; + let soft_handler = |lint, span, msg: String| { + lint_buffer.buffer_lint( + lint, + node_id, span, - soft_handler, - stability::UnstableKind::Regular, - ); - } + BuiltinLintDiag::UnstableFeature( + // FIXME make this translatable + msg.into(), + ), + ) + }; + stability::report_unstable( + self.tcx.sess, + feature, + reason.to_opt_reason(), + issue, + None, + is_soft, + span, + soft_handler, + stability::UnstableKind::Regular, + ); } } if let Some(depr) = &ext.deprecation { diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 24e15ded94f..6450f63472c 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -509,9 +509,8 @@ fn collect_link_data<'input, F: BrokenLinkCallback<'input>>( display_text.map(String::into_boxed_str) } -/// Returns a tuple containing a span encompassing all the document fragments and a boolean that is -/// `true` if any of the fragments are from a macro expansion. -pub fn span_of_fragments_with_expansion(fragments: &[DocFragment]) -> Option<(Span, bool)> { +/// Returns a span encompassing all the document fragments. +pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> { let (first_fragment, last_fragment) = match fragments { [] => return None, [first, .., last] => (first, last), @@ -520,15 +519,7 @@ pub fn span_of_fragments_with_expansion(fragments: &[DocFragment]) -> Option<(Sp if first_fragment.span == DUMMY_SP { return None; } - Some(( - first_fragment.span.to(last_fragment.span), - fragments.iter().any(|frag| frag.from_expansion), - )) -} - -/// Returns a span encompassing all the document fragments. -pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> { - span_of_fragments_with_expansion(fragments).map(|(sp, _)| sp) + Some(first_fragment.span.to(last_fragment.span)) } /// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code. @@ -686,7 +677,7 @@ pub fn source_span_for_markdown_range_inner( } } - let (span, _) = span_of_fragments_with_expansion(fragments)?; + let span = span_of_fragments(fragments)?; let src_span = span.from_inner(InnerSpan::new( md_range.start + start_bytes, md_range.end + start_bytes + end_bytes, diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index c69991f3fb2..52717d73b8a 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -339,7 +339,7 @@ pub(crate) fn transform_instance<'tcx>( } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual // functions to match the type erasure done below. - let upcast_ty = match tcx.trait_of_item(def_id) { + let upcast_ty = match tcx.trait_of_assoc(def_id) { Some(trait_id) => trait_object_ty( tcx, ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), @@ -364,7 +364,7 @@ pub(crate) fn transform_instance<'tcx>( }; instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); } else if let ty::InstanceKind::VTableShim(def_id) = instance.def - && let Some(trait_id) = tcx.trait_of_item(def_id) + && let Some(trait_id) = tcx.trait_of_assoc(def_id) { // Adjust the type ids of VTableShims to the type id expected in the call sites for the // entry in the vtable (i.e., by using the signature of the closure passed as an argument @@ -466,7 +466,7 @@ fn implemented_method<'tcx>( let method_id; let trait_id; let trait_method; - let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) { + let ancestor = if let Some(impl_id) = tcx.impl_of_assoc(instance.def_id()) { // Implementation in an `impl` block trait_ref = tcx.impl_trait_ref(impl_id)?; let impl_method = tcx.associated_item(instance.def_id()); @@ -480,7 +480,7 @@ fn implemented_method<'tcx>( // Provided method in a `trait` block trait_method = trait_method_bound; method_id = instance.def_id(); - trait_id = tcx.trait_of_item(method_id)?; + trait_id = tcx.trait_of_assoc(method_id)?; trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args)); trait_id } else { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7bea8685724..8f624e0fb2f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -343,12 +343,12 @@ impl LinkSelfContained { if let Some(component_to_enable) = component.strip_prefix('+') { self.explicitly_set = None; self.enabled_components - .insert(LinkSelfContainedComponents::from_str(component_to_enable)?); + .insert(LinkSelfContainedComponents::from_str(component_to_enable).ok()?); Some(()) } else if let Some(component_to_disable) = component.strip_prefix('-') { self.explicitly_set = None; self.disabled_components - .insert(LinkSelfContainedComponents::from_str(component_to_disable)?); + .insert(LinkSelfContainedComponents::from_str(component_to_disable).ok()?); Some(()) } else { None diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b33e3815ea4..44b35e8921e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1296,7 +1296,7 @@ pub mod parse { } pub(crate) fn parse_linker_flavor(slot: &mut Option<LinkerFlavorCli>, v: Option<&str>) -> bool { - match v.and_then(LinkerFlavorCli::from_str) { + match v.and_then(|v| LinkerFlavorCli::from_str(v).ok()) { Some(lf) => *slot = Some(lf), _ => return false, } @@ -2168,8 +2168,6 @@ options! { "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], "the backend to use"), - combine_cgu: bool = (false, parse_bool, [TRACKED], - "combine CGUs into a single one"), contract_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], "emit runtime checks for contract pre- and post-conditions (default: no)"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 9097b27b86c..426480f0dba 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -107,10 +107,10 @@ pub fn feature_err_issue( let span = span.into(); // Cancel an earlier warning for this same error, if it exists. - if let Some(span) = span.primary_span() { - if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { - err.cancel() - } + if let Some(span) = span.primary_span() + && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) + { + err.cancel() } let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 85bd8340c3c..e7097ec8327 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -313,6 +313,7 @@ impl Session { || self.opts.unstable_opts.query_dep_graph || self.opts.unstable_opts.dump_mir.is_some() || self.opts.unstable_opts.unpretty.is_some() + || self.prof.is_args_recording_enabled() || self.opts.output_types.contains_key(&OutputType::Mir) || std::env::var_os("RUSTC_LOG").is_some() { @@ -1362,11 +1363,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); } - if let Some(flavor) = sess.opts.cg.linker_flavor { - if let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) { - let flavor = flavor.desc(); - sess.dcx().emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list }); - } + if let Some(flavor) = sess.opts.cg.linker_flavor + && let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) + { + let flavor = flavor.desc(); + sess.dcx().emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list }); } if sess.opts.unstable_opts.function_return != FunctionReturn::default() { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index c3080875da8..97d1d9c2d2a 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -322,6 +322,7 @@ impl ExpnId { /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than /// `expn_id.is_descendant_of(ctxt.outer_expn())`. + #[inline] pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) } @@ -394,6 +395,7 @@ impl HygieneData { } } + #[inline] fn with<R>(f: impl FnOnce(&mut HygieneData) -> R) -> R { with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut())) } @@ -406,6 +408,7 @@ impl HygieneData { } } + #[inline] fn local_expn_data(&self, expn_id: LocalExpnId) -> &ExpnData { self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID") } @@ -437,23 +440,28 @@ impl HygieneData { } } + #[inline] fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { self.syntax_context_data[ctxt.0 as usize].opaque } + #[inline] fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } + #[inline] fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { self.syntax_context_data[ctxt.0 as usize].outer_expn } + #[inline] fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { let data = &self.syntax_context_data[ctxt.0 as usize]; (data.outer_expn, data.outer_transparency) } + #[inline] fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { self.syntax_context_data[ctxt.0 as usize].parent } @@ -718,11 +726,13 @@ impl SyntaxContext { SyntaxContext(raw as u32) } + #[inline] fn from_usize(raw: usize) -> SyntaxContext { SyntaxContext(u32::try_from(raw).unwrap()) } /// Extend a syntax context with a given expansion and transparency. + #[inline] pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) } @@ -743,10 +753,12 @@ impl SyntaxContext { /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the /// invocation of f that created g1. /// Returns the mark that was removed. + #[inline] pub fn remove_mark(&mut self) -> ExpnId { HygieneData::with(|data| data.remove_mark(self).0) } + #[inline] pub fn marks(self) -> Vec<(ExpnId, Transparency)> { HygieneData::with(|data| data.marks(self)) } @@ -776,11 +788,13 @@ impl SyntaxContext { /// ``` /// This returns the expansion whose definition scope we use to privacy check the resolution, /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). + #[inline] pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { HygieneData::with(|data| data.adjust(self, expn_id)) } /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. + #[inline] pub(crate) fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { HygieneData::with(|data| { *self = data.normalize_to_macros_2_0(*self); @@ -901,10 +915,12 @@ impl SyntaxContext { HygieneData::with(|data| data.outer_mark(self)) } + #[inline] pub(crate) fn dollar_crate_name(self) -> Symbol { HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } + #[inline] pub fn edition(self) -> Edition { HygieneData::with(|data| data.expn_data(data.outer_expn(self)).edition) } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 9b0e009b2cd..3f72ccd9f89 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -167,6 +167,7 @@ where } } +#[inline] pub fn with_session_globals<R, F>(f: F) -> R where F: FnOnce(&SessionGlobals) -> R, @@ -715,12 +716,17 @@ impl Span { (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site) } - /// Walk down the expansion ancestors to find a span that's contained within `outer`. + /// Find the first ancestor span that's contained within `outer`. + /// + /// This method traverses the macro expansion ancestors until it finds the first span + /// that's contained within `outer`. /// - /// The span returned by this method may have a different [`SyntaxContext`] as `outer`. + /// The span returned by this method may have a different [`SyntaxContext`] than `outer`. /// If you need to extend the span, use [`find_ancestor_inside_same_ctxt`] instead, /// because joining spans with different syntax contexts can create unexpected results. /// + /// This is used to find the span of the macro call when a parent expr span, i.e. `outer`, is known. + /// /// [`find_ancestor_inside_same_ctxt`]: Self::find_ancestor_inside_same_ctxt pub fn find_ancestor_inside(mut self, outer: Span) -> Option<Span> { while !outer.contains(self) { @@ -729,8 +735,10 @@ impl Span { Some(self) } - /// Walk down the expansion ancestors to find a span with the same [`SyntaxContext`] as - /// `other`. + /// Find the first ancestor span with the same [`SyntaxContext`] as `other`. + /// + /// This method traverses the macro expansion ancestors until it finds a span + /// that has the same [`SyntaxContext`] as `other`. /// /// Like [`find_ancestor_inside_same_ctxt`], but specifically for when spans might not /// overlap. Take care when using this, and prefer [`find_ancestor_inside`] or @@ -746,9 +754,12 @@ impl Span { Some(self) } - /// Walk down the expansion ancestors to find a span that's contained within `outer` and + /// Find the first ancestor span that's contained within `outer` and /// has the same [`SyntaxContext`] as `outer`. /// + /// This method traverses the macro expansion ancestors until it finds a span + /// that is both contained within `outer` and has the same [`SyntaxContext`] as `outer`. + /// /// This method is the combination of [`find_ancestor_inside`] and /// [`find_ancestor_in_same_ctxt`] and should be preferred when extending the returned span. /// If you do not need to modify the span, use [`find_ancestor_inside`] instead. @@ -762,43 +773,43 @@ impl Span { Some(self) } - /// Recursively walk down the expansion ancestors to find the oldest ancestor span with the same - /// [`SyntaxContext`] the initial span. + /// Find the first ancestor span that does not come from an external macro. /// - /// This method is suitable for peeling through *local* macro expansions to find the "innermost" - /// span that is still local and shares the same [`SyntaxContext`]. For example, given + /// This method traverses the macro expansion ancestors until it finds a span + /// that is either from user-written code or from a local macro (defined in the current crate). /// - /// ```ignore (illustrative example, contains type error) - /// macro_rules! outer { - /// ($x: expr) => { - /// inner!($x) - /// } - /// } + /// External macros are those defined in dependencies or the standard library. + /// This method is useful for reporting errors in user-controllable code and avoiding + /// diagnostics inside external macros. /// - /// macro_rules! inner { - /// ($x: expr) => { - /// format!("error: {}", $x) - /// //~^ ERROR mismatched types - /// } - /// } + /// # See also /// - /// fn bar(x: &str) -> Result<(), Box<dyn std::error::Error>> { - /// Err(outer!(x)) - /// } - /// ``` + /// - [`Self::find_ancestor_not_from_macro`] + /// - [`Self::in_external_macro`] + pub fn find_ancestor_not_from_extern_macro(mut self, sm: &SourceMap) -> Option<Span> { + while self.in_external_macro(sm) { + self = self.parent_callsite()?; + } + Some(self) + } + + /// Find the first ancestor span that does not come from any macro expansion. /// - /// if provided the initial span of `outer!(x)` inside `bar`, this method will recurse - /// the parent callsites until we reach `format!("error: {}", $x)`, at which point it is the - /// oldest ancestor span that is both still local and shares the same [`SyntaxContext`] as the - /// initial span. - pub fn find_oldest_ancestor_in_same_ctxt(self) -> Span { - let mut cur = self; - while cur.eq_ctxt(self) - && let Some(parent_callsite) = cur.parent_callsite() - { - cur = parent_callsite; + /// This method traverses the macro expansion ancestors until it finds a span + /// that originates from user-written code rather than any macro-generated code. + /// + /// This method is useful for reporting errors at the exact location users wrote code + /// and providing suggestions at directly editable locations. + /// + /// # See also + /// + /// - [`Self::find_ancestor_not_from_extern_macro`] + /// - [`Span::from_expansion`] + pub fn find_ancestor_not_from_macro(mut self) -> Option<Span> { + while self.from_expansion() { + self = self.parent_callsite()?; } - cur + Some(self) } /// Edition of the crate from which this span came. diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 8a3644163ca..d9315149798 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -826,10 +826,10 @@ impl SourceMap { /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` /// `c`. pub fn span_through_char(&self, sp: Span, c: char) -> Span { - if let Ok(snippet) = self.span_to_snippet(sp) { - if let Some(offset) = snippet.find(c) { - return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); - } + if let Ok(snippet) = self.span_to_snippet(sp) + && let Some(offset) = snippet.find(c) + { + return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); } sp } diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index 0121c752dbd..56932c24922 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -12,7 +12,10 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } +serde = "1.0.219" +serde_derive = "1.0.219" serde_json = "1.0.59" +serde_path_to_error = "0.1.17" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 27b41cc09ed..d567ad401bb 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -314,16 +314,15 @@ fn classify_arg<'a, Ty, C>( } fn extend_integer_width<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, _) = scalar.primitive() { - // 32-bit integers are always sign-extended - if i.size().bits() == 32 && xlen > 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, _) = scalar.primitive() + && i.size().bits() == 32 + && xlen > 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + // 32-bit integers are always sign-extended + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(xlen); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 77c0cf06fc1..0209838bec1 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -6,15 +6,14 @@ use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, signed) = scalar.primitive() { - if !signed && i.size().bits() == 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, signed) = scalar.primitive() + && !signed + && i.size().bits() == 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(bits); @@ -58,13 +57,12 @@ where ret.cast_to(reg); return; } - } else if ret.layout.fields.count() == 2 { - if let Some(reg0) = float_reg(cx, ret, 0) { - if let Some(reg1) = float_reg(cx, ret, 1) { - ret.cast_to(CastTarget::pair(reg0, reg1)); - return; - } - } + } else if ret.layout.fields.count() == 2 + && let Some(reg0) = float_reg(cx, ret, 0) + && let Some(reg1) = float_reg(cx, ret, 1) + { + ret.cast_to(CastTarget::pair(reg0, reg1)); + return; } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index ab3271220eb..63e56744aec 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -495,18 +495,16 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn extend_integer_width_to(&mut self, bits: u64) { // Only integers have signedness - if let BackendRepr::Scalar(scalar) = self.layout.backend_repr { - if let Primitive::Int(i, signed) = scalar.primitive() { - if i.size().bits() < bits { - if let PassMode::Direct(ref mut attrs) = self.mode { - if signed { - attrs.ext(ArgExtension::Sext) - } else { - attrs.ext(ArgExtension::Zext) - }; - } - } - } + if let BackendRepr::Scalar(scalar) = self.layout.backend_repr + && let Primitive::Int(i, signed) = scalar.primitive() + && i.size().bits() < bits + && let PassMode::Direct(ref mut attrs) = self.mode + { + if signed { + attrs.ext(ArgExtension::Sext) + } else { + attrs.ext(ArgExtension::Zext) + }; } } diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index a06f54d60e7..161e2c1645f 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -393,16 +393,14 @@ fn classify_arg<'a, Ty, C>( } fn extend_integer_width<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, _) = scalar.primitive() { - // 32-bit integers are always sign-extended - if i.size().bits() == 32 && xlen > 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, _) = scalar.primitive() + && i.size().bits() == 32 + && xlen > 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(xlen); diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs index 4fcc477921b..896609bdbe3 100644 --- a/compiler/rustc_target/src/json.rs +++ b/compiler/rustc_target/src/json.rs @@ -114,3 +114,18 @@ impl ToJson for rustc_abi::CanonAbi { self.to_string().to_json() } } + +macro_rules! serde_deserialize_from_str { + ($ty:ty) => { + impl<'de> serde::Deserialize<'de> for $ty { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(serde::de::Error::custom) + } + } + }; +} +pub(crate) use serde_deserialize_from_str; diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 6c716f87125..d27c1929aef 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -1,60 +1,47 @@ -use std::borrow::Cow; use std::collections::BTreeMap; use std::str::FromStr; -use rustc_abi::{Align, AlignFromBytesError, ExternAbi}; -use serde_json::Value; +use rustc_abi::{Align, AlignFromBytesError}; -use super::{Target, TargetKind, TargetOptions, TargetWarnings}; +use super::crt_objects::CrtObjects; +use super::{ + BinaryFormat, CodeModel, DebuginfoKind, FloatAbi, FramePointer, LinkArgsCli, + LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFlavorCli, LldFlavor, + MergeFunctions, PanicStrategy, RelocModel, RelroLevel, RustcAbi, SanitizerSet, + SmallDataThresholdSupport, SplitDebuginfo, StackProbeType, StaticCow, SymbolVisibility, Target, + TargetKind, TargetOptions, TargetWarnings, TlsModel, +}; use crate::json::{Json, ToJson}; use crate::spec::AbiMap; impl Target { /// Loads a target descriptor from a JSON object. - pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> { - // While ugly, this code must remain this way to retain - // compatibility with existing JSON fields and the internal - // expected naming of the Target and TargetOptions structs. - // To ensure compatibility is retained, the built-in targets - // are round-tripped through this code to catch cases where - // the JSON parser is not updated to match the structs. - - let mut obj = match obj { - Value::Object(obj) => obj, - _ => return Err("Expected JSON object for target")?, - }; + pub fn from_json(json: &str) -> Result<(Target, TargetWarnings), String> { + let json_deserializer = &mut serde_json::Deserializer::from_str(json); - let mut get_req_field = |name: &str| { - obj.remove(name) - .and_then(|j| j.as_str().map(str::to_string)) - .ok_or_else(|| format!("Field {name} in target specification is required")) - }; + let json: TargetSpecJson = + serde_path_to_error::deserialize(json_deserializer).map_err(|err| err.to_string())?; let mut base = Target { - llvm_target: get_req_field("llvm-target")?.into(), + llvm_target: json.llvm_target, metadata: Default::default(), - pointer_width: get_req_field("target-pointer-width")? - .parse::<u32>() - .map_err(|_| "target-pointer-width must be an integer".to_string())?, - data_layout: get_req_field("data-layout")?.into(), - arch: get_req_field("arch")?.into(), + pointer_width: json + .target_pointer_width + .parse() + .map_err(|err| format!("invalid target-pointer-width: {err}"))?, + data_layout: json.data_layout, + arch: json.arch, options: Default::default(), }; // FIXME: This doesn't properly validate anything and just ignores the data if it's invalid. // That's okay for now, the only use of this is when generating docs, which we don't do for // custom targets. - if let Some(Json::Object(mut metadata)) = obj.remove("metadata") { - base.metadata.description = metadata - .remove("description") - .and_then(|desc| desc.as_str().map(|desc| desc.to_owned().into())); - base.metadata.tier = metadata - .remove("tier") - .and_then(|tier| tier.as_u64()) - .filter(|tier| (1..=3).contains(tier)); - base.metadata.host_tools = - metadata.remove("host_tools").and_then(|host| host.as_bool()); - base.metadata.std = metadata.remove("std").and_then(|host| host.as_bool()); + if let Some(metadata) = json.metadata { + base.metadata.description = metadata.description; + base.metadata.tier = metadata.tier.filter(|tier| (1..=3).contains(tier)); + base.metadata.host_tools = metadata.host_tools; + base.metadata.std = metadata.std; } let alignment_error = |field_name: &str, error: AlignFromBytesError| -> String { @@ -65,640 +52,188 @@ impl Target { format!("`{}` bits is not a valid value for {field_name}: {msg}", error.align() * 8) }; - let mut incorrect_type = vec![]; - - macro_rules! key { - ($key_name:ident) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { - base.$key_name = s; - } - } ); - ($key_name:ident = $json_name:expr) => ( { - let name = $json_name; - if let Some(s) = obj.remove(name).and_then(|s| s.as_str().map(str::to_string).map(Cow::from)) { - base.$key_name = s; - } - } ); - ($key_name:ident = $json_name:expr, u64 as $int_ty:ty) => ( { - let name = $json_name; - if let Some(s) = obj.remove(name).and_then(|b| b.as_u64()) { - base.$key_name = s as $int_ty; - } - } ); - ($key_name:ident, bool) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { - base.$key_name = s; - } - } ); - ($key_name:ident = $json_name:expr, bool) => ( { - let name = $json_name; - if let Some(s) = obj.remove(name).and_then(|b| b.as_bool()) { - base.$key_name = s; - } - } ); - ($key_name:ident, u32) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { - if s < 1 || s > 5 { - return Err("Not a valid DWARF version number".into()); - } - base.$key_name = s as u32; - } - } ); - ($key_name:ident, Option<bool>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_bool()) { - base.$key_name = Some(s); - } - } ); - ($key_name:ident, Option<u64>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { - base.$key_name = Some(s); - } - } ); - ($key_name:ident, Option<StaticCow<str>>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(s) = obj.remove(&name).and_then(|b| Some(b.as_str()?.to_string())) { - base.$key_name = Some(s.into()); - } - } ); - ($key_name:ident, Option<Align>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(b) = obj.remove(&name).and_then(|b| b.as_u64()) { - match Align::from_bits(b) { - Ok(align) => base.$key_name = Some(align), - Err(e) => return Err(alignment_error(&name, e)), - } - } - } ); - ($key_name:ident, BinaryFormat) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|f| f.as_str().and_then(|s| { - match s.parse::<super::BinaryFormat>() { - Ok(binary_format) => base.$key_name = binary_format, - _ => return Some(Err(format!( - "'{s}' is not a valid value for binary_format. \ - Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' " - ))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, MergeFunctions) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::MergeFunctions>() { - Ok(mergefunc) => base.$key_name = mergefunc, - _ => return Some(Err(format!("'{}' is not a valid value for \ - merge-functions. Use 'disabled', \ - 'trampolines', or 'aliases'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, FloatAbi) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::FloatAbi>() { - Ok(float_abi) => base.$key_name = Some(float_abi), - _ => return Some(Err(format!("'{}' is not a valid value for \ - llvm-floatabi. Use 'soft' or 'hard'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RustcAbi) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::RustcAbi>() { - Ok(rustc_abi) => base.$key_name = Some(rustc_abi), - _ => return Some(Err(format!( - "'{s}' is not a valid value for rustc-abi. \ - Use 'x86-softfloat' or leave the field unset." - ))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelocModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::RelocModel>() { - Ok(relocation_model) => base.$key_name = relocation_model, - _ => return Some(Err(format!("'{}' is not a valid relocation model. \ - Run `rustc --print relocation-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, CodeModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::CodeModel>() { - Ok(code_model) => base.$key_name = Some(code_model), - _ => return Some(Err(format!("'{}' is not a valid code model. \ - Run `rustc --print code-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, TlsModel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::TlsModel>() { - Ok(tls_model) => base.$key_name = tls_model, - _ => return Some(Err(format!("'{}' is not a valid TLS model. \ - Run `rustc --print tls-models` to \ - see the list of supported values.", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, SmallDataThresholdSupport) => ( { - obj.remove("small-data-threshold-support").and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::SmallDataThresholdSupport>() { - Ok(support) => base.small_data_threshold_support = support, - _ => return Some(Err(format!("'{s}' is not a valid value for small-data-threshold-support."))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, PanicStrategy) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s { - "unwind" => base.$key_name = super::PanicStrategy::Unwind, - "abort" => base.$key_name = super::PanicStrategy::Abort, - _ => return Some(Err(format!("'{}' is not a valid value for \ - panic-strategy. Use 'unwind' or 'abort'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, RelroLevel) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::RelroLevel>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err(format!("'{}' is not a valid value for \ - relro-level. Use 'full', 'partial, or 'off'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, Option<SymbolVisibility>) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::SymbolVisibility>() { - Ok(level) => base.$key_name = Some(level), - _ => return Some(Err(format!("'{}' is not a valid value for \ - symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, DebuginfoKind) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::DebuginfoKind>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err( - format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ - 'dwarf-dsym' or 'pdb'.") - )), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, SplitDebuginfo) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::SplitDebuginfo>() { - Ok(level) => base.$key_name = level, - _ => return Some(Err(format!("'{}' is not a valid value for \ - split-debuginfo. Use 'off' or 'dsymutil'.", - s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(j) = obj.remove(&name) { - if let Some(v) = j.as_array() { - base.$key_name = v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect(); - } else { - incorrect_type.push(name) - } - } - } ); - ($key_name:ident, opt_list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(j) = obj.remove(&name) { - if let Some(v) = j.as_array() { - base.$key_name = Some(v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect()); - } else { - incorrect_type.push(name) - } + macro_rules! forward { + ($name:ident) => { + if let Some($name) = json.$name { + base.$name = $name; } - } ); - ($key_name:ident, fallible_list) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|j| { - if let Some(v) = j.as_array() { - match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() { - Ok(l) => { base.$key_name = l }, - // FIXME: `fallible_list` can't re-use the `key!` macro for list - // elements and the error messages from that macro, so it has a bad - // generic message instead - Err(_) => return Some(Err( - format!("`{:?}` is not a valid value for `{}`", j, name) - )), - } - } else { - incorrect_type.push(name) - } - Some(Ok(())) - }).unwrap_or(Ok(())) - } ); - ($key_name:ident, optional) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - base.$key_name = o - .as_str() - .map(|s| s.to_string().into()); - } - } ); - ($key_name:ident = $json_name:expr, LldFlavor) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - if let Some(flavor) = super::LldFlavor::from_str(&s) { - base.$key_name = flavor; - } else { - return Some(Err(format!( - "'{}' is not a valid value for lld-flavor. \ - Use 'darwin', 'gnu', 'link' or 'wasm'.", - s))) - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident = $json_name:expr, LinkerFlavorCli) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - match super::LinkerFlavorCli::from_str(s) { - Some(linker_flavor) => base.$key_name = linker_flavor, - _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \ - Use {}", s, super::LinkerFlavorCli::one_of()))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident, StackProbeType) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| match super::StackProbeType::from_json(&o) { - Ok(v) => { - base.$key_name = v; - Some(Ok(())) - }, - Err(s) => Some(Err( - format!("`{:?}` is not a valid value for `{}`: {}", o, name, s) - )), - }).unwrap_or(Ok(())) - } ); - ($key_name:ident, SanitizerSet) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(a) = o.as_array() { - for s in a { - use super::SanitizerSet; - base.$key_name |= match s.as_str() { - Some("address") => SanitizerSet::ADDRESS, - Some("cfi") => SanitizerSet::CFI, - Some("dataflow") => SanitizerSet::DATAFLOW, - Some("kcfi") => SanitizerSet::KCFI, - Some("kernel-address") => SanitizerSet::KERNELADDRESS, - Some("leak") => SanitizerSet::LEAK, - Some("memory") => SanitizerSet::MEMORY, - Some("memtag") => SanitizerSet::MEMTAG, - Some("safestack") => SanitizerSet::SAFESTACK, - Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK, - Some("thread") => SanitizerSet::THREAD, - Some("hwaddress") => SanitizerSet::HWADDRESS, - Some(s) => return Err(format!("unknown sanitizer {}", s)), - _ => return Err(format!("not a string: {:?}", s)), - }; - } - } else { - incorrect_type.push(name) - } - } - Ok::<(), String>(()) - } ); - ($key_name:ident, link_self_contained_components) => ( { - // Skeleton of what needs to be parsed: - // - // ``` - // $name: { - // "components": [ - // <array of strings> - // ] - // } - // ``` - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(o) = o.as_object() { - let component_array = o.get("components") - .ok_or_else(|| format!("{name}: expected a \ - JSON object with a `components` field."))?; - let component_array = component_array.as_array() - .ok_or_else(|| format!("{name}.components: expected a JSON array"))?; - let mut components = super::LinkSelfContainedComponents::empty(); - for s in component_array { - components |= match s.as_str() { - Some(s) => { - super::LinkSelfContainedComponents::from_str(s) - .ok_or_else(|| format!("unknown \ - `-Clink-self-contained` component: {s}"))? - }, - _ => return Err(format!("not a string: {:?}", s)), - }; - } - base.$key_name = super::LinkSelfContainedDefault::WithComponents(components); - } else { - incorrect_type.push(name) - } - } - Ok::<(), String>(()) - } ); - ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { - let name = $json_name; - obj.remove(name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<super::LinkSelfContainedDefault>() { - Ok(lsc_default) => base.$key_name = lsc_default, - _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ - Use 'false', 'true', 'musl' or 'mingw'", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); - ($key_name:ident = $json_name:expr, link_objects) => ( { - let name = $json_name; - if let Some(val) = obj.remove(name) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per CRT object kind.", name))?; - let mut args = super::CrtObjects::new(); - for (k, v) in obj { - let kind = super::LinkOutputKind::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for CRT object kind. \ - Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_str().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_string().into()) - }) - .collect::<Result<Vec<_>, String>>()?; - - args.insert(kind, v); - } - base.$key_name = args; - } - } ); - ($key_name:ident = $json_name:expr, link_args) => ( { - let name = $json_name; - if let Some(val) = obj.remove(name) { - let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ - JSON object with fields per linker-flavor.", name))?; - let mut args = super::LinkArgsCli::new(); - for (k, v) in obj { - let flavor = super::LinkerFlavorCli::from_str(&k).ok_or_else(|| { - format!("{}: '{}' is not a valid value for linker-flavor. \ - Use 'em', 'gcc', 'ld' or 'msvc'", name, k) - })?; - - let v = v.as_array().ok_or_else(|| - format!("{}.{}: expected a JSON array", name, k) - )?.iter().enumerate() - .map(|(i,s)| { - let s = s.as_str().ok_or_else(|| - format!("{}.{}[{}]: expected a JSON string", name, k, i))?; - Ok(s.to_string().into()) - }) - .collect::<Result<Vec<_>, String>>()?; - - args.insert(flavor, v); - } - base.$key_name = args; - } - } ); - ($key_name:ident, env) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(o) = obj.remove(&name) { - if let Some(a) = o.as_array() { - for o in a { - if let Some(s) = o.as_str() { - if let [k, v] = *s.split('=').collect::<Vec<_>>() { - base.$key_name - .to_mut() - .push((k.to_string().into(), v.to_string().into())) - } - } - } - } else { - incorrect_type.push(name) - } + }; + } + macro_rules! forward_opt { + ($name:ident) => { + if let Some($name) = json.$name { + base.$name = Some($name); } - } ); - ($key_name:ident, target_families) => ( { - if let Some(value) = obj.remove("target-family") { - if let Some(v) = value.as_array() { - base.$key_name = v.iter() - .map(|a| a.as_str().unwrap().to_string().into()) - .collect(); - } else if let Some(v) = value.as_str() { - base.$key_name = vec![v.to_string().into()].into(); - } + }; + } + + if let Some(target_endian) = json.target_endian { + base.endian = target_endian.0; + } + + forward!(frame_pointer); + forward!(c_int_width); + forward_opt!(c_enum_min_bits); // if None, matches c_int_width + forward!(os); + forward!(env); + forward!(abi); + forward!(vendor); + forward_opt!(linker); + forward!(linker_flavor_json); + forward!(lld_flavor_json); + forward!(linker_is_gnu_json); + forward!(pre_link_objects); + forward!(post_link_objects); + forward!(pre_link_objects_self_contained); + forward!(post_link_objects_self_contained); + + // Deserializes the backwards-compatible variants of `-Clink-self-contained` + if let Some(link_self_contained) = json.link_self_contained_backwards_compatible { + base.link_self_contained = link_self_contained; + } + // Deserializes the components variant of `-Clink-self-contained` + if let Some(link_self_contained) = json.link_self_contained { + let components = link_self_contained + .components + .into_iter() + .fold(LinkSelfContainedComponents::empty(), |a, b| a | b); + base.link_self_contained = LinkSelfContainedDefault::WithComponents(components); + } + + forward!(pre_link_args_json); + forward!(late_link_args_json); + forward!(late_link_args_dynamic_json); + forward!(late_link_args_static_json); + forward!(post_link_args_json); + forward_opt!(link_script); + + if let Some(link_env) = json.link_env { + for s in link_env { + if let [k, v] = *s.split('=').collect::<Vec<_>>() { + base.link_env.to_mut().push((k.to_string().into(), v.to_string().into())) + } else { + return Err(format!("link-env value '{s}' must be of the pattern 'KEY=VALUE'")); } - } ); + } } - if let Some(j) = obj.remove("target-endian") { - if let Some(s) = j.as_str() { - base.endian = s.parse()?; - } else { - incorrect_type.push("target-endian".into()) + forward!(link_env_remove); + forward!(asm_args); + forward!(cpu); + forward!(need_explicit_cpu); + forward!(features); + forward!(dynamic_linking); + forward_opt!(direct_access_external_data); + forward!(dll_tls_export); + forward!(only_cdylib); + forward!(executables); + forward!(relocation_model); + forward_opt!(code_model); + forward!(tls_model); + forward!(disable_redzone); + forward!(function_sections); + forward!(dll_prefix); + forward!(dll_suffix); + forward!(exe_suffix); + forward!(staticlib_prefix); + forward!(staticlib_suffix); + + if let Some(target_family) = json.target_family { + match target_family { + TargetFamiliesJson::Array(families) => base.families = families, + TargetFamiliesJson::String(family) => base.families = vec![family].into(), } } - if let Some(fp) = obj.remove("frame-pointer") { - if let Some(s) = fp.as_str() { - base.frame_pointer = s - .parse() - .map_err(|()| format!("'{s}' is not a valid value for frame-pointer"))?; - } else { - incorrect_type.push("frame-pointer".into()) + forward!(abi_return_struct_as_int); + forward!(is_like_aix); + forward!(is_like_darwin); + forward!(is_like_solaris); + forward!(is_like_windows); + forward!(is_like_msvc); + forward!(is_like_wasm); + forward!(is_like_android); + forward!(binary_format); + forward!(default_dwarf_version); + forward!(allows_weak_linkage); + forward!(has_rpath); + forward!(no_default_libraries); + forward!(position_independent_executables); + forward!(static_position_independent_executables); + forward!(plt_by_default); + forward!(relro_level); + forward!(archive_format); + forward!(allow_asm); + forward!(main_needs_argc_argv); + forward!(has_thread_local); + forward!(obj_is_bitcode); + forward!(bitcode_llvm_cmdline); + forward_opt!(max_atomic_width); + forward_opt!(min_atomic_width); + forward!(atomic_cas); + forward!(panic_strategy); + forward!(crt_static_allows_dylibs); + forward!(crt_static_default); + forward!(crt_static_respected); + forward!(stack_probes); + + if let Some(min_global_align) = json.min_global_align { + match Align::from_bits(min_global_align) { + Ok(align) => base.min_global_align = Some(align), + Err(e) => return Err(alignment_error("min-global-align", e)), } } - key!(c_int_width = "target-c-int-width", u64 as u16); - key!(c_enum_min_bits, Option<u64>); // if None, matches c_int_width - key!(os); - key!(env); - key!(abi); - key!(vendor); - key!(linker, optional); - key!(linker_flavor_json = "linker-flavor", LinkerFlavorCli)?; - key!(lld_flavor_json = "lld-flavor", LldFlavor)?; - key!(linker_is_gnu_json = "linker-is-gnu", bool); - key!(pre_link_objects = "pre-link-objects", link_objects); - key!(post_link_objects = "post-link-objects", link_objects); - key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); - key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); - // Deserializes the backwards-compatible variants of `-Clink-self-contained` - key!( - link_self_contained = "crt-objects-fallback", - link_self_contained_backwards_compatible - )?; - // Deserializes the components variant of `-Clink-self-contained` - key!(link_self_contained, link_self_contained_components)?; - key!(pre_link_args_json = "pre-link-args", link_args); - key!(late_link_args_json = "late-link-args", link_args); - key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); - key!(late_link_args_static_json = "late-link-args-static", link_args); - key!(post_link_args_json = "post-link-args", link_args); - key!(link_script, optional); - key!(link_env, env); - key!(link_env_remove, list); - key!(asm_args, list); - key!(cpu); - key!(need_explicit_cpu, bool); - key!(features); - key!(dynamic_linking, bool); - key!(direct_access_external_data, Option<bool>); - key!(dll_tls_export, bool); - key!(only_cdylib, bool); - key!(executables, bool); - key!(relocation_model, RelocModel)?; - key!(code_model, CodeModel)?; - key!(tls_model, TlsModel)?; - key!(disable_redzone, bool); - key!(function_sections, bool); - key!(dll_prefix); - key!(dll_suffix); - key!(exe_suffix); - key!(staticlib_prefix); - key!(staticlib_suffix); - key!(families, target_families); - key!(abi_return_struct_as_int, bool); - key!(is_like_aix, bool); - key!(is_like_darwin, bool); - key!(is_like_solaris, bool); - key!(is_like_windows, bool); - key!(is_like_msvc, bool); - key!(is_like_wasm, bool); - key!(is_like_android, bool); - key!(binary_format, BinaryFormat)?; - key!(default_dwarf_version, u32); - key!(allows_weak_linkage, bool); - key!(has_rpath, bool); - key!(no_default_libraries, bool); - key!(position_independent_executables, bool); - key!(static_position_independent_executables, bool); - key!(plt_by_default, bool); - key!(relro_level, RelroLevel)?; - key!(archive_format); - key!(allow_asm, bool); - key!(main_needs_argc_argv, bool); - key!(has_thread_local, bool); - key!(obj_is_bitcode, bool); - key!(bitcode_llvm_cmdline); - key!(max_atomic_width, Option<u64>); - key!(min_atomic_width, Option<u64>); - key!(atomic_cas, bool); - key!(panic_strategy, PanicStrategy)?; - key!(crt_static_allows_dylibs, bool); - key!(crt_static_default, bool); - key!(crt_static_respected, bool); - key!(stack_probes, StackProbeType)?; - key!(min_global_align, Option<Align>); - key!(default_codegen_units, Option<u64>); - key!(default_codegen_backend, Option<StaticCow<str>>); - key!(trap_unreachable, bool); - key!(requires_lto, bool); - key!(singlethread, bool); - key!(no_builtins, bool); - key!(default_visibility, Option<SymbolVisibility>)?; - key!(emit_debug_gdb_scripts, bool); - key!(requires_uwtable, bool); - key!(default_uwtable, bool); - key!(simd_types_indirect, bool); - key!(limit_rdylib_exports, bool); - key!(override_export_symbols, opt_list); - key!(merge_functions, MergeFunctions)?; - key!(mcount = "target-mcount"); - key!(llvm_mcount_intrinsic, optional); - key!(llvm_abiname); - key!(llvm_floatabi, FloatAbi)?; - key!(rustc_abi, RustcAbi)?; - key!(relax_elf_relocations, bool); - key!(llvm_args, list); - key!(use_ctors_section, bool); - key!(eh_frame_header, bool); - key!(has_thumb_interworking, bool); - key!(debuginfo_kind, DebuginfoKind)?; - key!(split_debuginfo, SplitDebuginfo)?; - key!(supported_split_debuginfo, fallible_list)?; - key!(supported_sanitizers, SanitizerSet)?; - key!(generate_arange_section, bool); - key!(supports_stack_protector, bool); - key!(small_data_threshold_support, SmallDataThresholdSupport)?; - key!(entry_name); - key!(supports_xray, bool); + forward_opt!(default_codegen_units); + forward_opt!(default_codegen_backend); + forward!(trap_unreachable); + forward!(requires_lto); + forward!(singlethread); + forward!(no_builtins); + forward_opt!(default_visibility); + forward!(emit_debug_gdb_scripts); + forward!(requires_uwtable); + forward!(default_uwtable); + forward!(simd_types_indirect); + forward!(limit_rdylib_exports); + forward_opt!(override_export_symbols); + forward!(merge_functions); + forward!(mcount); + forward_opt!(llvm_mcount_intrinsic); + forward!(llvm_abiname); + forward_opt!(llvm_floatabi); + forward_opt!(rustc_abi); + forward!(relax_elf_relocations); + forward!(llvm_args); + forward!(use_ctors_section); + forward!(eh_frame_header); + forward!(has_thumb_interworking); + forward!(debuginfo_kind); + forward!(split_debuginfo); + forward!(supported_split_debuginfo); + + if let Some(supported_sanitizers) = json.supported_sanitizers { + base.supported_sanitizers = + supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b); + } + + forward!(generate_arange_section); + forward!(supports_stack_protector); + forward!(small_data_threshold_support); + forward!(entry_name); + forward!(supports_xray); // we're going to run `update_from_cli`, but that won't change the target's AbiMap // FIXME: better factor the Target definition so we enforce this on a type level let abi_map = AbiMap::from_target(&base); - - if let Some(abi_str) = obj.remove("entry-abi") { - if let Json::String(abi_str) = abi_str { - match abi_str.parse::<ExternAbi>() { - Ok(abi) => base.options.entry_abi = abi_map.canonize_abi(abi, false).unwrap(), - Err(_) => return Err(format!("{abi_str} is not a valid ExternAbi")), - } - } else { - incorrect_type.push("entry-abi".to_owned()) - } + if let Some(entry_abi) = json.entry_abi { + base.options.entry_abi = abi_map.canonize_abi(entry_abi.0, false).unwrap(); } base.update_from_cli(); base.check_consistency(TargetKind::Json)?; - // Each field should have been read using `Json::remove` so any keys remaining are unused. - let remaining_keys = obj.keys(); - Ok(( - base, - TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type }, - )) + Ok((base, TargetWarnings { unused_fields: vec![] })) } } @@ -877,3 +412,189 @@ impl ToJson for Target { Json::Object(d) } } + +#[derive(serde_derive::Deserialize)] +struct LinkSelfContainedComponentsWrapper { + components: Vec<LinkSelfContainedComponents>, +} + +#[derive(serde_derive::Deserialize)] +#[serde(untagged)] +enum TargetFamiliesJson { + Array(StaticCow<[StaticCow<str>]>), + String(StaticCow<str>), +} + +/// `Endian` is in `rustc_abi`, which doesn't have access to the macro and serde. +struct EndianWrapper(rustc_abi::Endian); +impl FromStr for EndianWrapper { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + rustc_abi::Endian::from_str(s).map(Self) + } +} +crate::json::serde_deserialize_from_str!(EndianWrapper); + +/// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde. +struct ExternAbiWrapper(rustc_abi::ExternAbi); +impl FromStr for ExternAbiWrapper { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + rustc_abi::ExternAbi::from_str(s) + .map(Self) + .map_err(|_| format!("{s} is not a valid extern ABI")) + } +} +crate::json::serde_deserialize_from_str!(ExternAbiWrapper); + +#[derive(serde_derive::Deserialize)] +struct TargetSpecJsonMetadata { + description: Option<StaticCow<str>>, + tier: Option<u64>, + host_tools: Option<bool>, + std: Option<bool>, +} + +#[derive(serde_derive::Deserialize)] +#[serde(rename_all = "kebab-case")] +// Ensure that all unexpected fields get turned into errors. +// This helps users stay up to date when the schema changes instead of silently +// ignoring their old values. +#[serde(deny_unknown_fields)] +struct TargetSpecJson { + llvm_target: StaticCow<str>, + target_pointer_width: String, + data_layout: StaticCow<str>, + arch: StaticCow<str>, + + metadata: Option<TargetSpecJsonMetadata>, + + // options: + target_endian: Option<EndianWrapper>, + frame_pointer: Option<FramePointer>, + #[serde(rename = "target-c-int-width")] + c_int_width: Option<u16>, + c_enum_min_bits: Option<u64>, + os: Option<StaticCow<str>>, + env: Option<StaticCow<str>>, + abi: Option<StaticCow<str>>, + vendor: Option<StaticCow<str>>, + linker: Option<StaticCow<str>>, + #[serde(rename = "linker-flavor")] + linker_flavor_json: Option<LinkerFlavorCli>, + #[serde(rename = "lld-flavor")] + lld_flavor_json: Option<LldFlavor>, + #[serde(rename = "linker-is-gnu")] + linker_is_gnu_json: Option<bool>, + #[serde(rename = "pre-link-objects")] + pre_link_objects: Option<CrtObjects>, + #[serde(rename = "post-link-objects")] + post_link_objects: Option<CrtObjects>, + #[serde(rename = "pre-link-objects-fallback")] + pre_link_objects_self_contained: Option<CrtObjects>, + #[serde(rename = "post-link-objects-fallback")] + post_link_objects_self_contained: Option<CrtObjects>, + #[serde(rename = "crt-objects-fallback")] + link_self_contained_backwards_compatible: Option<LinkSelfContainedDefault>, + link_self_contained: Option<LinkSelfContainedComponentsWrapper>, + #[serde(rename = "pre-link-args")] + pre_link_args_json: Option<LinkArgsCli>, + #[serde(rename = "late-link-args")] + late_link_args_json: Option<LinkArgsCli>, + #[serde(rename = "late-link-args-dynamic")] + late_link_args_dynamic_json: Option<LinkArgsCli>, + #[serde(rename = "late-link-args-static")] + late_link_args_static_json: Option<LinkArgsCli>, + #[serde(rename = "post-link-args")] + post_link_args_json: Option<LinkArgsCli>, + link_script: Option<StaticCow<str>>, + link_env: Option<Vec<StaticCow<str>>>, + link_env_remove: Option<StaticCow<[StaticCow<str>]>>, + asm_args: Option<StaticCow<[StaticCow<str>]>>, + cpu: Option<StaticCow<str>>, + need_explicit_cpu: Option<bool>, + features: Option<StaticCow<str>>, + dynamic_linking: Option<bool>, + direct_access_external_data: Option<bool>, + dll_tls_export: Option<bool>, + only_cdylib: Option<bool>, + executables: Option<bool>, + relocation_model: Option<RelocModel>, + code_model: Option<CodeModel>, + tls_model: Option<TlsModel>, + disable_redzone: Option<bool>, + function_sections: Option<bool>, + dll_prefix: Option<StaticCow<str>>, + dll_suffix: Option<StaticCow<str>>, + exe_suffix: Option<StaticCow<str>>, + staticlib_prefix: Option<StaticCow<str>>, + staticlib_suffix: Option<StaticCow<str>>, + target_family: Option<TargetFamiliesJson>, + abi_return_struct_as_int: Option<bool>, + is_like_aix: Option<bool>, + is_like_darwin: Option<bool>, + is_like_solaris: Option<bool>, + is_like_windows: Option<bool>, + is_like_msvc: Option<bool>, + is_like_wasm: Option<bool>, + is_like_android: Option<bool>, + binary_format: Option<BinaryFormat>, + default_dwarf_version: Option<u32>, + allows_weak_linkage: Option<bool>, + has_rpath: Option<bool>, + no_default_libraries: Option<bool>, + position_independent_executables: Option<bool>, + static_position_independent_executables: Option<bool>, + plt_by_default: Option<bool>, + relro_level: Option<RelroLevel>, + archive_format: Option<StaticCow<str>>, + allow_asm: Option<bool>, + main_needs_argc_argv: Option<bool>, + has_thread_local: Option<bool>, + obj_is_bitcode: Option<bool>, + bitcode_llvm_cmdline: Option<StaticCow<str>>, + max_atomic_width: Option<u64>, + min_atomic_width: Option<u64>, + atomic_cas: Option<bool>, + panic_strategy: Option<PanicStrategy>, + crt_static_allows_dylibs: Option<bool>, + crt_static_default: Option<bool>, + crt_static_respected: Option<bool>, + stack_probes: Option<StackProbeType>, + min_global_align: Option<u64>, + default_codegen_units: Option<u64>, + default_codegen_backend: Option<StaticCow<str>>, + trap_unreachable: Option<bool>, + requires_lto: Option<bool>, + singlethread: Option<bool>, + no_builtins: Option<bool>, + default_visibility: Option<SymbolVisibility>, + emit_debug_gdb_scripts: Option<bool>, + requires_uwtable: Option<bool>, + default_uwtable: Option<bool>, + simd_types_indirect: Option<bool>, + limit_rdylib_exports: Option<bool>, + override_export_symbols: Option<StaticCow<[StaticCow<str>]>>, + merge_functions: Option<MergeFunctions>, + #[serde(rename = "target-mcount")] + mcount: Option<StaticCow<str>>, + llvm_mcount_intrinsic: Option<StaticCow<str>>, + llvm_abiname: Option<StaticCow<str>>, + llvm_floatabi: Option<FloatAbi>, + rustc_abi: Option<RustcAbi>, + relax_elf_relocations: Option<bool>, + llvm_args: Option<StaticCow<[StaticCow<str>]>>, + use_ctors_section: Option<bool>, + eh_frame_header: Option<bool>, + has_thumb_interworking: Option<bool>, + debuginfo_kind: Option<DebuginfoKind>, + split_debuginfo: Option<SplitDebuginfo>, + supported_split_debuginfo: Option<StaticCow<[SplitDebuginfo]>>, + supported_sanitizers: Option<Vec<SanitizerSet>>, + generate_arange_section: Option<bool>, + supports_stack_protector: Option<bool>, + small_data_threshold_support: Option<SmallDataThresholdSupport>, + entry_name: Option<StaticCow<str>>, + supports_xray: Option<bool>, + entry_abi: Option<ExternAbiWrapper>, +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4bc0d88a910..033590e01a6 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,6 +37,7 @@ //! //! [JSON]: https://json.org +use core::result::Result; use std::borrow::Cow; use std::collections::BTreeMap; use std::hash::{Hash, Hasher}; @@ -198,18 +199,29 @@ impl LldFlavor { LldFlavor::Link => "link", } } +} - fn from_str(s: &str) -> Option<Self> { - Some(match s { +impl FromStr for LldFlavor { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { "darwin" => LldFlavor::Ld64, "gnu" => LldFlavor::Ld, "link" => LldFlavor::Link, "wasm" => LldFlavor::Wasm, - _ => return None, + _ => { + return Err( + "invalid value for lld flavor: '{s}', expected one of 'darwin', 'gnu', 'link', 'wasm'" + .into(), + ); + } }) } } +crate::json::serde_deserialize_from_str!(LldFlavor); + impl ToJson for LldFlavor { fn to_json(&self) -> Json { self.as_str().to_json() @@ -494,19 +506,23 @@ macro_rules! linker_flavor_cli_impls { concat!("one of: ", $($string, " ",)*) } - pub fn from_str(s: &str) -> Option<LinkerFlavorCli> { - Some(match s { - $($string => $($flavor)*,)* - _ => return None, - }) - } - pub fn desc(self) -> &'static str { match self { $($($flavor)* => $string,)* } } } + + impl FromStr for LinkerFlavorCli { + type Err = String; + + fn from_str(s: &str) -> Result<LinkerFlavorCli, Self::Err> { + Ok(match s { + $($string => $($flavor)*,)* + _ => return Err(format!("invalid linker flavor, allowed values: {}", Self::one_of())), + }) + } + } ) } @@ -540,6 +556,8 @@ linker_flavor_cli_impls! { (LinkerFlavorCli::Em) "em" } +crate::json::serde_deserialize_from_str!(LinkerFlavorCli); + impl ToJson for LinkerFlavorCli { fn to_json(&self) -> Json { self.desc().to_json() @@ -573,19 +591,26 @@ pub enum LinkSelfContainedDefault { /// Parses a backwards-compatible `-Clink-self-contained` option string, without components. impl FromStr for LinkSelfContainedDefault { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<LinkSelfContainedDefault, ()> { + fn from_str(s: &str) -> Result<LinkSelfContainedDefault, Self::Err> { Ok(match s { "false" => LinkSelfContainedDefault::False, "true" | "wasm" => LinkSelfContainedDefault::True, "musl" => LinkSelfContainedDefault::InferredForMusl, "mingw" => LinkSelfContainedDefault::InferredForMingw, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid `-Clink-self-contained` default. \ + Use 'false', 'true', 'wasm', 'musl' or 'mingw'", + )); + } }) } } +crate::json::serde_deserialize_from_str!(LinkSelfContainedDefault); + impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { match *self { @@ -652,19 +677,6 @@ bitflags::bitflags! { rustc_data_structures::external_bitflags_debug! { LinkSelfContainedComponents } impl LinkSelfContainedComponents { - /// Parses a single `-Clink-self-contained` well-known component, not a set of flags. - pub fn from_str(s: &str) -> Option<LinkSelfContainedComponents> { - Some(match s { - "crto" => LinkSelfContainedComponents::CRT_OBJECTS, - "libc" => LinkSelfContainedComponents::LIBC, - "unwind" => LinkSelfContainedComponents::UNWIND, - "linker" => LinkSelfContainedComponents::LINKER, - "sanitizers" => LinkSelfContainedComponents::SANITIZERS, - "mingw" => LinkSelfContainedComponents::MINGW, - _ => return None, - }) - } - /// Return the component's name. /// /// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags). @@ -708,6 +720,29 @@ impl LinkSelfContainedComponents { } } +impl FromStr for LinkSelfContainedComponents { + type Err = String; + + /// Parses a single `-Clink-self-contained` well-known component, not a set of flags. + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { + "crto" => LinkSelfContainedComponents::CRT_OBJECTS, + "libc" => LinkSelfContainedComponents::LIBC, + "unwind" => LinkSelfContainedComponents::UNWIND, + "linker" => LinkSelfContainedComponents::LINKER, + "sanitizers" => LinkSelfContainedComponents::SANITIZERS, + "mingw" => LinkSelfContainedComponents::MINGW, + _ => { + return Err(format!( + "'{s}' is not a valid link-self-contained component, expected 'crto', 'libc', 'unwind', 'linker', 'sanitizers', 'mingw'" + )); + } + }) + } +} + +crate::json::serde_deserialize_from_str!(LinkSelfContainedComponents); + impl ToJson for LinkSelfContainedComponents { fn to_json(&self) -> Json { let components: Vec<_> = Self::all_components() @@ -821,6 +856,25 @@ impl PanicStrategy { } } +impl FromStr for PanicStrategy { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { + "unwind" => PanicStrategy::Unwind, + "abort" => PanicStrategy::Abort, + _ => { + return Err(format!( + "'{}' is not a valid value for \ + panic-strategy. Use 'unwind' or 'abort'.", + s + )); + } + }) + } +} + +crate::json::serde_deserialize_from_str!(PanicStrategy); + impl ToJson for PanicStrategy { fn to_json(&self) -> Json { match *self { @@ -867,18 +921,24 @@ impl SymbolVisibility { } impl FromStr for SymbolVisibility { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<SymbolVisibility, ()> { + fn from_str(s: &str) -> Result<SymbolVisibility, Self::Err> { match s { "hidden" => Ok(SymbolVisibility::Hidden), "protected" => Ok(SymbolVisibility::Protected), "interposable" => Ok(SymbolVisibility::Interposable), - _ => Err(()), + _ => Err(format!( + "'{}' is not a valid value for \ + symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", + s + )), } } } +crate::json::serde_deserialize_from_str!(SymbolVisibility); + impl ToJson for SymbolVisibility { fn to_json(&self) -> Json { match *self { @@ -890,19 +950,25 @@ impl ToJson for SymbolVisibility { } impl FromStr for RelroLevel { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<RelroLevel, ()> { + fn from_str(s: &str) -> Result<RelroLevel, Self::Err> { match s { "full" => Ok(RelroLevel::Full), "partial" => Ok(RelroLevel::Partial), "off" => Ok(RelroLevel::Off), "none" => Ok(RelroLevel::None), - _ => Err(()), + _ => Err(format!( + "'{}' is not a valid value for \ + relro-level. Use 'full', 'partial, 'off', or 'none'.", + s + )), } } } +crate::json::serde_deserialize_from_str!(RelroLevel); + impl ToJson for RelroLevel { fn to_json(&self) -> Json { match *self { @@ -923,7 +989,7 @@ pub enum SmallDataThresholdSupport { } impl FromStr for SmallDataThresholdSupport { - type Err = (); + type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { if s == "none" { @@ -935,11 +1001,13 @@ impl FromStr for SmallDataThresholdSupport { } else if let Some(arg) = s.strip_prefix("llvm-arg=") { Ok(Self::LlvmArg(arg.to_string().into())) } else { - Err(()) + Err(format!("'{s}' is not a valid value for small-data-threshold-support.")) } } } +crate::json::serde_deserialize_from_str!(SmallDataThresholdSupport); + impl ToJson for SmallDataThresholdSupport { fn to_json(&self) -> Value { match self { @@ -969,18 +1037,25 @@ impl MergeFunctions { } impl FromStr for MergeFunctions { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<MergeFunctions, ()> { + fn from_str(s: &str) -> Result<MergeFunctions, Self::Err> { match s { "disabled" => Ok(MergeFunctions::Disabled), "trampolines" => Ok(MergeFunctions::Trampolines), "aliases" => Ok(MergeFunctions::Aliases), - _ => Err(()), + _ => Err(format!( + "'{}' is not a valid value for \ + merge-functions. Use 'disabled', \ + 'trampolines', or 'aliases'.", + s + )), } } } +crate::json::serde_deserialize_from_str!(MergeFunctions); + impl ToJson for MergeFunctions { fn to_json(&self) -> Json { match *self { @@ -1040,9 +1115,9 @@ impl RelocModel { } impl FromStr for RelocModel { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<RelocModel, ()> { + fn from_str(s: &str) -> Result<RelocModel, Self::Err> { Ok(match s { "static" => RelocModel::Static, "pic" => RelocModel::Pic, @@ -1051,11 +1126,19 @@ impl FromStr for RelocModel { "ropi" => RelocModel::Ropi, "rwpi" => RelocModel::Rwpi, "ropi-rwpi" => RelocModel::RopiRwpi, - _ => return Err(()), + _ => { + return Err(format!( + "invalid relocation model '{s}'. + Run `rustc --print relocation-models` to \ + see the list of supported values.'" + )); + } }) } } +crate::json::serde_deserialize_from_str!(RelocModel); + impl ToJson for RelocModel { fn to_json(&self) -> Json { self.desc().to_json() @@ -1072,20 +1155,28 @@ pub enum CodeModel { } impl FromStr for CodeModel { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<CodeModel, ()> { + fn from_str(s: &str) -> Result<CodeModel, Self::Err> { Ok(match s { "tiny" => CodeModel::Tiny, "small" => CodeModel::Small, "kernel" => CodeModel::Kernel, "medium" => CodeModel::Medium, "large" => CodeModel::Large, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid code model. \ + Run `rustc --print code-models` to \ + see the list of supported values." + )); + } }) } } +crate::json::serde_deserialize_from_str!(CodeModel); + impl ToJson for CodeModel { fn to_json(&self) -> Json { match *self { @@ -1107,17 +1198,25 @@ pub enum FloatAbi { } impl FromStr for FloatAbi { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<FloatAbi, ()> { + fn from_str(s: &str) -> Result<FloatAbi, Self::Err> { Ok(match s { "soft" => FloatAbi::Soft, "hard" => FloatAbi::Hard, - _ => return Err(()), + _ => { + return Err(format!( + "'{}' is not a valid value for \ + llvm-floatabi. Use 'soft' or 'hard'.", + s + )); + } }) } } +crate::json::serde_deserialize_from_str!(FloatAbi); + impl ToJson for FloatAbi { fn to_json(&self) -> Json { match *self { @@ -1138,17 +1237,24 @@ pub enum RustcAbi { } impl FromStr for RustcAbi { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<RustcAbi, ()> { + fn from_str(s: &str) -> Result<RustcAbi, Self::Err> { Ok(match s { "x86-sse2" => RustcAbi::X86Sse2, "x86-softfloat" => RustcAbi::X86Softfloat, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid value for rustc-abi. \ + Use 'x86-softfloat' or leave the field unset." + )); + } }) } } +crate::json::serde_deserialize_from_str!(RustcAbi); + impl ToJson for RustcAbi { fn to_json(&self) -> Json { match *self { @@ -1169,9 +1275,9 @@ pub enum TlsModel { } impl FromStr for TlsModel { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<TlsModel, ()> { + fn from_str(s: &str) -> Result<TlsModel, Self::Err> { Ok(match s { // Note the difference "general" vs "global" difference. The model name is "general", // but the user-facing option name is "global" for consistency with other compilers. @@ -1180,11 +1286,19 @@ impl FromStr for TlsModel { "initial-exec" => TlsModel::InitialExec, "local-exec" => TlsModel::LocalExec, "emulated" => TlsModel::Emulated, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid TLS model. \ + Run `rustc --print tls-models` to \ + see the list of supported values." + )); + } }) } } +crate::json::serde_deserialize_from_str!(TlsModel); + impl ToJson for TlsModel { fn to_json(&self) -> Json { match *self { @@ -1230,19 +1344,6 @@ impl LinkOutputKind { } } - pub(super) fn from_str(s: &str) -> Option<LinkOutputKind> { - Some(match s { - "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, - "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, - "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, - "static-pic-exe" => LinkOutputKind::StaticPicExe, - "dynamic-dylib" => LinkOutputKind::DynamicDylib, - "static-dylib" => LinkOutputKind::StaticDylib, - "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, - _ => return None, - }) - } - pub fn can_link_dylib(self) -> bool { match self { LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false, @@ -1255,6 +1356,31 @@ impl LinkOutputKind { } } +impl FromStr for LinkOutputKind { + type Err = String; + + fn from_str(s: &str) -> Result<LinkOutputKind, Self::Err> { + Ok(match s { + "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, + "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, + "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, + "static-pic-exe" => LinkOutputKind::StaticPicExe, + "dynamic-dylib" => LinkOutputKind::DynamicDylib, + "static-dylib" => LinkOutputKind::StaticDylib, + "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, + _ => { + return Err(format!( + "invalid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib' or 'wasi-reactor-exe'" + )); + } + }) + } +} + +crate::json::serde_deserialize_from_str!(LinkOutputKind); + impl fmt::Display for LinkOutputKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) @@ -1290,18 +1416,25 @@ impl DebuginfoKind { } impl FromStr for DebuginfoKind { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<Self, ()> { + fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match s { "dwarf" => DebuginfoKind::Dwarf, "dwarf-dsym" => DebuginfoKind::DwarfDsym, "pdb" => DebuginfoKind::Pdb, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ + 'dwarf-dsym' or 'pdb'." + )); + } }) } } +crate::json::serde_deserialize_from_str!(DebuginfoKind); + impl ToJson for DebuginfoKind { fn to_json(&self) -> Json { self.as_str().to_json() @@ -1354,18 +1487,25 @@ impl SplitDebuginfo { } impl FromStr for SplitDebuginfo { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result<Self, ()> { + fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match s { "off" => SplitDebuginfo::Off, "unpacked" => SplitDebuginfo::Unpacked, "packed" => SplitDebuginfo::Packed, - _ => return Err(()), + _ => { + return Err(format!( + "'{s}' is not a valid value for \ + split-debuginfo. Use 'off', 'unpacked', or 'packed'.", + )); + } }) } } +crate::json::serde_deserialize_from_str!(SplitDebuginfo); + impl ToJson for SplitDebuginfo { fn to_json(&self) -> Json { self.as_str().to_json() @@ -1378,7 +1518,9 @@ impl fmt::Display for SplitDebuginfo { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "kebab-case")] pub enum StackProbeType { /// Don't emit any stack probes. None, @@ -1390,44 +1532,10 @@ pub enum StackProbeType { Call, /// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline` /// and call `__rust_probestack` otherwise. - InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) }, -} - -impl StackProbeType { - fn from_json(json: &Json) -> Result<Self, String> { - let object = json.as_object().ok_or_else(|| "expected a JSON object")?; - let kind = object - .get("kind") - .and_then(|o| o.as_str()) - .ok_or_else(|| "expected `kind` to be a string")?; - match kind { - "none" => Ok(StackProbeType::None), - "inline" => Ok(StackProbeType::Inline), - "call" => Ok(StackProbeType::Call), - "inline-or-call" => { - let min_version = object - .get("min-llvm-version-for-inline") - .and_then(|o| o.as_array()) - .ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?; - let mut iter = min_version.into_iter().map(|v| { - let int = v.as_u64().ok_or_else( - || "expected `min-llvm-version-for-inline` values to be integers", - )?; - u32::try_from(int) - .map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32") - }); - let min_llvm_version_for_inline = ( - iter.next().unwrap_or(Ok(11))?, - iter.next().unwrap_or(Ok(0))?, - iter.next().unwrap_or(Ok(0))?, - ); - Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline }) - } - _ => Err(String::from( - "`kind` expected to be one of `none`, `inline`, `call` or `inline-or-call`", - )), - } - } + InlineOrCall { + #[serde(rename = "min-llvm-version-for-inline")] + min_llvm_version_for_inline: (u32, u32, u32), + }, } impl ToJson for StackProbeType { @@ -1549,6 +1657,29 @@ impl fmt::Display for SanitizerSet { } } +impl FromStr for SanitizerSet { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { + "address" => SanitizerSet::ADDRESS, + "cfi" => SanitizerSet::CFI, + "dataflow" => SanitizerSet::DATAFLOW, + "kcfi" => SanitizerSet::KCFI, + "kernel-address" => SanitizerSet::KERNELADDRESS, + "leak" => SanitizerSet::LEAK, + "memory" => SanitizerSet::MEMORY, + "memtag" => SanitizerSet::MEMTAG, + "safestack" => SanitizerSet::SAFESTACK, + "shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK, + "thread" => SanitizerSet::THREAD, + "hwaddress" => SanitizerSet::HWADDRESS, + s => return Err(format!("unknown sanitizer {s}")), + }) + } +} + +crate::json::serde_deserialize_from_str!(SanitizerSet); + impl ToJson for SanitizerSet { fn to_json(&self) -> Json { self.into_iter() @@ -1587,17 +1718,19 @@ impl FramePointer { } impl FromStr for FramePointer { - type Err = (); - fn from_str(s: &str) -> Result<Self, ()> { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match s { "always" => Self::Always, "non-leaf" => Self::NonLeaf, "may-omit" => Self::MayOmit, - _ => return Err(()), + _ => return Err(format!("'{s}' is not a valid value for frame-pointer")), }) } } +crate::json::serde_deserialize_from_str!(FramePointer); + impl ToJson for FramePointer { fn to_json(&self) -> Json { match *self { @@ -1685,7 +1818,7 @@ impl BinaryFormat { } impl FromStr for BinaryFormat { - type Err = (); + type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "coff" => Ok(Self::Coff), @@ -1693,11 +1826,16 @@ impl FromStr for BinaryFormat { "mach-o" => Ok(Self::MachO), "wasm" => Ok(Self::Wasm), "xcoff" => Ok(Self::Xcoff), - _ => Err(()), + _ => Err(format!( + "'{s}' is not a valid value for binary_format. \ + Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' " + )), } } } +crate::json::serde_deserialize_from_str!(BinaryFormat); + impl ToJson for BinaryFormat { fn to_json(&self) -> Json { match self { @@ -2130,12 +2268,11 @@ pub(crate) use cvs; #[derive(Debug, PartialEq)] pub struct TargetWarnings { unused_fields: Vec<String>, - incorrect_type: Vec<String>, } impl TargetWarnings { pub fn empty() -> Self { - Self { unused_fields: Vec::new(), incorrect_type: Vec::new() } + Self { unused_fields: Vec::new() } } pub fn warning_messages(&self) -> Vec<String> { @@ -2146,12 +2283,6 @@ impl TargetWarnings { self.unused_fields.join(", ") )); } - if !self.incorrect_type.is_empty() { - warnings.push(format!( - "target json file contains fields whose value doesn't have the correct json type: {}", - self.incorrect_type.join(", ") - )); - } warnings } } @@ -3325,7 +3456,8 @@ impl Target { /// Test target self-consistency and JSON encoding/decoding roundtrip. #[cfg(test)] fn test_target(mut self) { - let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j); + let recycled_target = + Target::from_json(&serde_json::to_string(&self.to_json()).unwrap()).map(|(j, _)| j); self.update_to_cli(); self.check_consistency(TargetKind::Builtin).unwrap(); assert_eq!(recycled_target, Ok(self)); @@ -3373,8 +3505,7 @@ impl Target { fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> { let contents = fs::read_to_string(path).map_err(|e| e.to_string())?; - let obj = serde_json::from_str(&contents).map_err(|e| e.to_string())?; - Target::from_json(obj) + Target::from_json(&contents) } match *target_tuple { @@ -3422,10 +3553,7 @@ impl Target { Err(format!("could not find specification for target {target_tuple:?}")) } } - TargetTuple::TargetJson { ref contents, .. } => { - let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?; - Target::from_json(obj) - } + TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents), } } @@ -3470,6 +3598,7 @@ impl Target { ), "x86" => (Architecture::I386, None), "s390x" => (Architecture::S390x, None), + "m68k" => (Architecture::M68k, None), "mips" | "mips32r6" => (Architecture::Mips, None), "mips64" | "mips64r6" => ( // While there are currently no builtin targets diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs index 58daaa03675..478726fbef6 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_musl.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); base.max_atomic_width = Some(128); base.supports_xray = true; - base.features = "+v8a".into(); + base.features = "+v8a,+outline-atomics".into(); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI diff --git a/compiler/rustc_target/src/tests.rs b/compiler/rustc_target/src/tests.rs index 76375170db6..ee847a84007 100644 --- a/compiler/rustc_target/src/tests.rs +++ b/compiler/rustc_target/src/tests.rs @@ -2,8 +2,7 @@ use crate::spec::Target; #[test] fn report_unused_fields() { - let json = serde_json::from_str( - r#" + let json = r#" { "arch": "powerpc64", "data-layout": "e-m:e-i64:64-n32:64", @@ -11,47 +10,8 @@ fn report_unused_fields() { "target-pointer-width": "64", "code-mode": "foo" } - "#, - ) - .unwrap(); - let warnings = Target::from_json(json).unwrap().1; - assert_eq!(warnings.warning_messages().len(), 1); - assert!(warnings.warning_messages().join("\n").contains("code-mode")); -} - -#[test] -fn report_incorrect_json_type() { - let json = serde_json::from_str( - r#" - { - "arch": "powerpc64", - "data-layout": "e-m:e-i64:64-n32:64", - "llvm-target": "powerpc64le-elf", - "target-pointer-width": "64", - "link-env-remove": "foo" - } - "#, - ) - .unwrap(); - let warnings = Target::from_json(json).unwrap().1; - assert_eq!(warnings.warning_messages().len(), 1); - assert!(warnings.warning_messages().join("\n").contains("link-env-remove")); -} - -#[test] -fn no_warnings_for_valid_target() { - let json = serde_json::from_str( - r#" - { - "arch": "powerpc64", - "data-layout": "e-m:e-i64:64-n32:64", - "llvm-target": "powerpc64le-elf", - "target-pointer-width": "64", - "link-env-remove": ["foo"] - } - "#, - ) - .unwrap(); - let warnings = Target::from_json(json).unwrap().1; - assert_eq!(warnings.warning_messages().len(), 0); + "#; + let result = Target::from_json(json); + eprintln!("{result:#?}"); + assert!(result.is_err()); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 1db05ced8d2..022d549a9df 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1303,7 +1303,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && let Some(args) = self.node_args_opt(expr.hir_id) && args.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) - && self.tecx.tcx.trait_of_item(def_id).is_some() + && self.tecx.tcx.trait_of_assoc(def_id).is_some() && !has_impl_trait(def_id) // FIXME(fn_delegation): In delegation item argument spans are equal to last path // segment. This leads to ICE's when emitting `multipart_suggestion`. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 772a7f01332..2a3268d3339 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -104,10 +104,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty::AssocKind::Fn { .. } => { if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id)) + && let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id) { - if let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id) { - visitor.visit_fn_decl(decl); - } + visitor.visit_fn_decl(decl); } } _ => {} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index c0daf08ce07..44baa213b28 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -91,51 +91,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with // some modifications due to that being in typeck and this being in infer. - if let ObligationCauseCode::Pattern { .. } = cause.code() { - if let ty::Adt(expected_adt, args) = exp_found.expected.kind() { - let compatible_variants: Vec<_> = expected_adt - .variants() - .iter() - .filter(|variant| { - variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn) - }) - .filter_map(|variant| { - let sole_field = &variant.single_field(); - let sole_field_ty = sole_field.ty(self.tcx, args); - if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { - let variant_path = - with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); - // FIXME #56861: DRYer prelude filtering - if let Some(path) = variant_path.strip_prefix("std::prelude::") { - if let Some((_, path)) = path.split_once("::") { - return Some(path.to_string()); - } - } - Some(variant_path) - } else { - None + if let ObligationCauseCode::Pattern { .. } = cause.code() + && let ty::Adt(expected_adt, args) = exp_found.expected.kind() + { + let compatible_variants: Vec<_> = expected_adt + .variants() + .iter() + .filter(|variant| { + variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn) + }) + .filter_map(|variant| { + let sole_field = &variant.single_field(); + let sole_field_ty = sole_field.ty(self.tcx, args); + if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { + let variant_path = + with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); + // FIXME #56861: DRYer prelude filtering + if let Some(path) = variant_path.strip_prefix("std::prelude::") + && let Some((_, path)) = path.split_once("::") + { + return Some(path.to_string()); } - }) - .collect(); - match &compatible_variants[..] { - [] => {} - [variant] => { - let sugg = SuggestTuplePatternOne { - variant: variant.to_owned(), - span_low: cause.span.shrink_to_lo(), - span_high: cause.span.shrink_to_hi(), - }; - diag.subdiagnostic(sugg); - } - _ => { - // More than one matching variant. - let sugg = SuggestTuplePatternMany { - path: self.tcx.def_path_str(expected_adt.did()), - cause_span: cause.span, - compatible_variants, - }; - diag.subdiagnostic(sugg); + Some(variant_path) + } else { + None } + }) + .collect(); + match &compatible_variants[..] { + [] => {} + [variant] => { + let sugg = SuggestTuplePatternOne { + variant: variant.to_owned(), + span_low: cause.span.shrink_to_lo(), + span_high: cause.span.shrink_to_hi(), + }; + diag.subdiagnostic(sugg); + } + _ => { + // More than one matching variant. + let sugg = SuggestTuplePatternMany { + path: self.tcx.def_path_str(expected_adt.did()), + cause_span: cause.span, + compatible_variants, + }; + diag.subdiagnostic(sugg); } } } @@ -288,19 +288,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.name, field.ty(self.tcx, expected_args))) .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) + && let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let suggestion = if expected_def.is_struct() { - SuggestAccessingField::Safe { span, snippet, name, ty } - } else if expected_def.is_union() { - SuggestAccessingField::Unsafe { span, snippet, name, ty } - } else { - return; - }; - diag.subdiagnostic(suggestion); - } - } + let suggestion = if expected_def.is_struct() { + SuggestAccessingField::Safe { span, snippet, name, ty } + } else if expected_def.is_union() { + SuggestAccessingField::Unsafe { span, snippet, name, ty } + } else { + return; + }; + diag.subdiagnostic(suggestion); } } } @@ -540,38 +538,35 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) -> Option<SuggestAsRefKind> { if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) = (expected.kind(), found.kind()) + && let ty::Adt(found_def, found_args) = *found_ty.kind() { - if let ty::Adt(found_def, found_args) = *found_ty.kind() { - if exp_def == &found_def { - let have_as_ref = &[ - (sym::Option, SuggestAsRefKind::Option), - (sym::Result, SuggestAsRefKind::Result), - ]; - if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { - self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) - }) { - let mut show_suggestion = true; - for (exp_ty, found_ty) in - std::iter::zip(exp_args.types(), found_args.types()) - { - match *exp_ty.kind() { - ty::Ref(_, exp_ty, _) => { - match (exp_ty.kind(), found_ty.kind()) { - (_, ty::Param(_)) - | (_, ty::Infer(_)) - | (ty::Param(_), _) - | (ty::Infer(_), _) => {} - _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} - _ => show_suggestion = false, - }; - } - ty::Param(_) | ty::Infer(_) => {} - _ => show_suggestion = false, + if exp_def == &found_def { + let have_as_ref = &[ + (sym::Option, SuggestAsRefKind::Option), + (sym::Result, SuggestAsRefKind::Result), + ]; + if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { + self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) + }) { + let mut show_suggestion = true; + for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) { + match *exp_ty.kind() { + ty::Ref(_, exp_ty, _) => { + match (exp_ty.kind(), found_ty.kind()) { + (_, ty::Param(_)) + | (_, ty::Infer(_)) + | (ty::Param(_), _) + | (ty::Infer(_), _) => {} + _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} + _ => show_suggestion = false, + }; } + ty::Param(_) | ty::Infer(_) => {} + _ => show_suggestion = false, } - if show_suggestion { - return Some(*msg); - } + } + if show_suggestion { + return Some(*msg); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 98f67257fd1..cdf1402252a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -360,7 +360,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }, ] = path.segments && data.trait_ref.def_id == *trait_id - && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && self.tcx.trait_of_assoc(*item_id) == Some(*trait_id) && let None = self.tainted_by_errors() { let assoc_item = self.tcx.associated_item(item_id); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 2344bc79f21..5765dfd891d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -381,15 +381,15 @@ impl IgnoredDiagnosticOption { old: Option<Span>, option_name: &'static str, ) { - if let (Some(new_item), Some(old_item)) = (new, old) { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - new_item, - IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, - ); - } + if let (Some(new_item), Some(old_item)) = (new, old) + && let Some(item_def_id) = item_def_id.as_local() + { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index bf7d4257b62..c182fd99b17 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -987,7 +987,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { else { return false; }; - if self.tcx.trait_of_item(did) != Some(clone_trait) { + if self.tcx.trait_of_assoc(did) != Some(clone_trait) { return false; } Some(ident.span) @@ -2213,26 +2213,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, trait_ref: DefId, ) { - if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) { - if let ty::AssocKind::Const { .. } | ty::AssocKind::Type { .. } = assoc_item.kind { - err.note(format!( - "{}s cannot be accessed directly on a `trait`, they can only be \ + if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) + && let ty::AssocKind::Const { .. } | ty::AssocKind::Type { .. } = assoc_item.kind + { + err.note(format!( + "{}s cannot be accessed directly on a `trait`, they can only be \ accessed through a specific `impl`", - self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) - )); + self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) + )); - if !assoc_item.is_impl_trait_in_trait() { - err.span_suggestion_verbose( - span, - "use the fully qualified path to an implementation", - format!( - "<Type as {}>::{}", - self.tcx.def_path_str(trait_ref), - assoc_item.name() - ), - Applicability::HasPlaceholders, - ); - } + if !assoc_item.is_impl_trait_in_trait() { + err.span_suggestion_verbose( + span, + "use the fully qualified path to an implementation", + format!( + "<Type as {}>::{}", + self.tcx.def_path_str(trait_ref), + assoc_item.name() + ), + Applicability::HasPlaceholders, + ); } } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3ce0f025512..01bdae7435d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -197,6 +197,12 @@ where delegate.compute_goal_fast_path(goal, obligation.cause.span) { match certainty { + // This fast path doesn't depend on region identity so it doesn't + // matter if the goal contains inference variables or not, so we + // don't need to call `push_hir_typeck_potentially_region_dependent_goal` + // here. + // + // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} Certainty::Maybe(_) => { self.obligations.register(obligation, None); @@ -207,7 +213,7 @@ where let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on); self.inspect_evaluated_obligation(infcx, &obligation, &result); - let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(E::from_solver_error( @@ -218,6 +224,10 @@ where } }; + // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work + // in the next iteration. This does not resolve the inference variables + // constrained by evaluating the goal. + obligation.predicate = goal.predicate; if has_changed == HasChanged::Yes { // We increment the recursion depth here to track the number of times // this goal has resulted in inference progress. This doesn't precisely @@ -230,7 +240,22 @@ where } match certainty { - Certainty::Yes => {} + Certainty::Yes => { + // Goals may depend on structural identity. Region uniquification at the + // start of MIR borrowck may cause things to no longer be so, potentially + // causing an ICE. + // + // While we uniquify root goals in HIR this does not handle cases where + // regions are hidden inside of a type or const inference variable. + // + // FIXME(-Znext-solver): This does not handle inference variables hidden + // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the + // storage, we can also rely on structural identity of `?x` even if we + // later uniquify it in MIR borrowck. + if infcx.in_hir_typeck && obligation.has_non_region_infer() { + infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); + } + } Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f50f01a285b..07e78da37b3 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -695,15 +695,14 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { source: CandidateSource::Impl(def_id), result: Ok(_), } = cand.kind() + && let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - let message = infcx - .tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); - if let Some(message) = message { - self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); - } + let message = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(message) = message { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); } } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 91c41544f78..9b5e59ce0fd 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -759,7 +759,7 @@ fn instantiate_and_check_impossible_predicates<'tcx>( // Specifically check trait fulfillment to avoid an error when trying to resolve // associated items. - if let Some(trait_def_id) = tcx.trait_of_item(key.0) { + if let Some(trait_def_id) = tcx.trait_of_assoc(key.0) { let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); predicates.push(trait_ref.upcast(tcx)); } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 28b5b7cf391..581191b2036 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1507,7 +1507,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); let item_def_id = obligation.predicate.def_id; - let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let trait_def_id = tcx.trait_of_assoc(item_def_id).unwrap(); let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) { let discriminant_def_id = diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f90316f520b..d7c3543cb3f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -781,16 +781,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self, &project_obligation, ) - { - if let Some(cached_res) = self + && let Some(cached_res) = self .infcx .inner .borrow_mut() .projection_cache() .is_complete(key) - { - break 'compute_res Ok(cached_res); - } + { + break 'compute_res Ok(cached_res); } // Need to explicitly set the depth of nested goals here as @@ -1436,24 +1434,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { let tcx = self.tcx(); // Treat reservation impls as ambiguity. - if let ImplCandidate(def_id) = candidate { - if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { - if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let message = tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); - if let Some(message) = message { - debug!( - "filter_reservation_impls: \ + if let ImplCandidate(def_id) = candidate + && let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) + { + if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { + let message = + tcx.get_attr(def_id, sym::rustc_reservation_impl).and_then(|a| a.value_str()); + if let Some(message) = message { + debug!( + "filter_reservation_impls: \ reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses - .insert(IntercrateAmbiguityCause::ReservationImpl { message }); - } + def_id + ); + intercrate_ambiguity_clauses + .insert(IntercrateAmbiguityCause::ReservationImpl { message }); } - return Ok(None); } + return Ok(None); } Ok(Some(candidate)) } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f0ff50318ab..af2e000e340 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -642,11 +642,11 @@ fn fn_abi_adjust_for_abi<'tcx>( // The `deduced_param_attrs` list could be empty if this is a type of function // we can't deduce any parameters for, so make sure the argument index is in // bounds. - if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) { - if deduced_param_attrs.read_only { - attrs.regular.insert(ArgAttribute::ReadOnly); - debug!("added deduced read-only attribute"); - } + if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) + && deduced_param_attrs.read_only + { + attrs.regular.insert(ArgAttribute::ReadOnly); + debug!("added deduced read-only attribute"); } } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 166e8f19342..e6c3568620b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -21,7 +21,7 @@ fn resolve_instance_raw<'tcx>( ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> { let PseudoCanonicalInput { typing_env, value: (def_id, args) } = key; - let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + let result = if let Some(trait_def_id) = tcx.trait_of_assoc(def_id) { debug!(" => associated item, attempting to find impl in typing_env {:#?}", typing_env); resolve_associated_item( tcx, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 163e2b30883..79f7e228e2a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -603,12 +603,6 @@ fn layout_of_uncached<'tcx>( .flatten() }; - let dont_niche_optimize_enum = def.repr().inhibit_enum_layout_opt() - || def - .variants() - .iter_enumerated() - .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())); - let maybe_unsized = def.is_struct() && def.non_enum_variant().tail_opt().is_some_and(|last_field| { let typing_env = ty::TypingEnv::post_analysis(tcx, def.did()); @@ -625,7 +619,6 @@ fn layout_of_uncached<'tcx>( tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), - dont_niche_optimize_enum, !maybe_unsized, ) .map_err(|err| map_error(cx, ty, err))?; @@ -651,7 +644,6 @@ fn layout_of_uncached<'tcx>( tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), - dont_niche_optimize_enum, !maybe_unsized, ) else { bug!("failed to compute unsized layout of {ty:?}"); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index f8d793464a9..2e0b16d9227 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -80,8 +80,11 @@ fn sizedness_constraint_for_ty<'tcx>( fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { match tcx.hir_node_by_def_id(def_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness, - hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { defaultness, of_trait: Some(_), .. }), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, node => { bug!("`defaultness` called on {:?}", node); @@ -106,10 +109,10 @@ fn adt_sizedness_constraint<'tcx>( tcx: TyCtxt<'tcx>, (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { - if let Some(def_id) = def_id.as_local() { - if let ty::Representability::Infinite(_) = tcx.representability(def_id) { - return None; - } + if let Some(def_id) = def_id.as_local() + && let ty::Representability::Infinite(_) = tcx.representability(def_id) + { + return None; } let def = tcx.adt_def(def_id); diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index e86a2305e23..b4873c8c71c 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -148,6 +148,10 @@ pub trait InferCtxtLike: Sized { true } + fn in_hir_typeck(&self) -> bool { + false + } + fn typing_mode(&self) -> TypingMode<Self::Interner>; fn universe(&self) -> ty::UniverseIndex; diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index e3c4a793b37..3a00fe89360 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -400,8 +400,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>( (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => { - let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?; - Ok(Ty::new_adt(cx, a_def, args)) + Ok(if a_args.is_empty() { + a + } else { + let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?; + if args == a_args { a } else { Ty::new_adt(cx, a_def, args) } + }) } (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), @@ -515,8 +519,12 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>( } (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { - let args = relation.relate_item_args(a_def_id, a_args, b_args)?; - Ok(Ty::new_fn_def(cx, a_def_id, args)) + Ok(if a_args.is_empty() { + a + } else { + let args = relation.relate_item_args(a_def_id, a_args, b_args)?; + if args == a_args { a } else { Ty::new_fn_def(cx, a_def_id, args) } + }) } (ty::FnPtr(a_sig_tys, a_hdr), ty::FnPtr(b_sig_tys, b_hdr)) => { |
