diff options
Diffstat (limited to 'compiler')
227 files changed, 3222 insertions, 1855 deletions
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 5de2e69072f..155e14a3796 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" memchr = "2.7.4" -rustc-literal-escaper = "0.0.4" +rustc-literal-escaper = "0.0.5" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3c576316f62..8c2b521c560 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -18,7 +18,7 @@ //! - [`Attribute`]: Metadata associated with item. //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::{cmp, fmt}; pub use GenericArgs::*; @@ -155,6 +155,59 @@ impl Path { } } +/// Joins multiple symbols with "::" into a path, e.g. "a::b::c". If the first +/// segment is `kw::PathRoot` it will be printed as empty, e.g. "::b::c". +/// +/// The generics on the `path` argument mean it can accept many forms, such as: +/// - `&[Symbol]` +/// - `Vec<Symbol>` +/// - `Vec<&Symbol>` +/// - `impl Iterator<Item = Symbol>` +/// - `impl Iterator<Item = &Symbol>` +/// +/// Panics if `path` is empty or a segment after the first is `kw::PathRoot`. +pub fn join_path_syms(path: impl IntoIterator<Item = impl Borrow<Symbol>>) -> String { + // This is a guess at the needed capacity that works well in practice. It is slightly faster + // than (a) starting with an empty string, or (b) computing the exact capacity required. + // `8` works well because it's about the right size and jemalloc's size classes are all + // multiples of 8. + let mut iter = path.into_iter(); + let len_hint = iter.size_hint().1.unwrap_or(1); + let mut s = String::with_capacity(len_hint * 8); + + let first_sym = *iter.next().unwrap().borrow(); + if first_sym != kw::PathRoot { + s.push_str(first_sym.as_str()); + } + for sym in iter { + let sym = *sym.borrow(); + debug_assert_ne!(sym, kw::PathRoot); + s.push_str("::"); + s.push_str(sym.as_str()); + } + s +} + +/// Like `join_path_syms`, but for `Ident`s. This function is necessary because +/// `Ident::to_string` does more than just print the symbol in the `name` field. +pub fn join_path_idents(path: impl IntoIterator<Item = impl Borrow<Ident>>) -> String { + let mut iter = path.into_iter(); + let len_hint = iter.size_hint().1.unwrap_or(1); + let mut s = String::with_capacity(len_hint * 8); + + let first_ident = *iter.next().unwrap().borrow(); + if first_ident.name != kw::PathRoot { + s.push_str(&first_ident.to_string()); + } + for ident in iter { + let ident = *ident.borrow(); + debug_assert_ne!(ident.name, kw::PathRoot); + s.push_str("::"); + s.push_str(&ident.to_string()); + } + s +} + /// A segment of a path: an identifier, an optional lifetime, and a set of types. /// /// E.g., `std`, `String` or `Box<T>`. @@ -3637,6 +3690,7 @@ impl Default for FnHeader { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Trait { + pub constness: Const, pub safety: Safety, pub is_auto: IsAuto, pub ident: Ident, diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index fa7878873e5..2dfd695d880 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -126,11 +126,11 @@ impl LitKind { token::CStr => { let s = symbol.as_str(); let mut buf = Vec::with_capacity(s.len()); - unescape_c_str(s, |_span, c| match c { + unescape_c_str(s, |_span, res| match res { Ok(MixedUnit::Char(c)) => { - buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()) + buf.extend_from_slice(c.get().encode_utf8(&mut [0; 4]).as_bytes()) } - Ok(MixedUnit::HighByte(b)) => buf.push(b), + Ok(MixedUnit::HighByte(b)) => buf.push(b.get()), Err(err) => { assert!(!err.is_fatal(), "failed to unescape C string literal") } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 37fcc0d2167..a344f23c345 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -738,7 +738,8 @@ macro_rules! common_visitor_and_walkers { try_visit!(vis.visit_ty(self_ty)); visit_assoc_items(vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() }) } - ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => { + ItemKind::Trait(box Trait { constness, safety, is_auto: _, ident, generics, bounds, items }) => { + try_visit!(visit_constness(vis, constness)); try_visit!(visit_safety(vis, safety)); try_visit!(vis.visit_ident(ident)); try_visit!(vis.visit_generics(generics)); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 9d40a7386f6..abd70c7517c 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -417,7 +417,16 @@ impl<'hir> LoweringContext<'_, 'hir> { items: new_impl_items, })) } - ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + constness, + is_auto, + safety, + ident, + generics, + bounds, + items, + }) => { + let constness = self.lower_constness(*constness); let ident = self.lower_ident(*ident); let (generics, (safety, items, bounds)) = self.lower_generics( generics, @@ -435,7 +444,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (safety, items, bounds) }, ); - hir::ItemKind::Trait(*is_auto, safety, ident, generics, bounds, items) + hir::ItemKind::Trait(constness, *is_auto, safety, ident, generics, bounds, items) } ItemKind::TraitAlias(ident, generics, bounds) => { let ident = self.lower_ident(*ident); diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index c5780c957c9..e419154d65d 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -240,10 +240,10 @@ ast_passes_static_without_body = ast_passes_tilde_const_disallowed = `[const]` is not allowed here .closure = closures cannot have `[const]` trait bounds .function = this function is not `const`, so it cannot have `[const]` trait bounds - .trait = this trait is not a `#[const_trait]`, so it cannot have `[const]` trait bounds + .trait = this trait is not `const`, so it cannot have `[const]` trait bounds .trait_impl = this impl is not `const`, so it cannot have `[const]` trait bounds .impl = inherent impls cannot have `[const]` trait bounds - .trait_assoc_ty = associated types in non-`#[const_trait]` traits cannot have `[const]` trait bounds + .trait_assoc_ty = associated types in non-`const` traits cannot have `[const]` trait bounds .trait_impl_assoc_ty = associated types in non-const impls cannot have `[const]` trait bounds .inherent_assoc_ty = inherent associated types cannot have `[const]` trait bounds .object = trait objects cannot have `[const]` trait bounds diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 38889d28151..c69250c0305 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -49,14 +49,14 @@ enum SelfSemantic { } enum TraitOrTraitImpl { - Trait { span: Span, constness_span: Option<Span> }, + Trait { span: Span, constness: Const }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, } impl TraitOrTraitImpl { fn constness(&self) -> Option<Span> { match self { - Self::Trait { constness_span: Some(span), .. } + Self::Trait { constness: Const::Yes(span), .. } | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span), _ => None, } @@ -110,15 +110,10 @@ impl<'a> AstValidator<'a> { self.outer_trait_or_trait_impl = old; } - fn with_in_trait( - &mut self, - span: Span, - constness_span: Option<Span>, - f: impl FnOnce(&mut Self), - ) { + fn with_in_trait(&mut self, span: Span, constness: Const, f: impl FnOnce(&mut Self)) { let old = mem::replace( &mut self.outer_trait_or_trait_impl, - Some(TraitOrTraitImpl::Trait { span, constness_span }), + Some(TraitOrTraitImpl::Trait { span, constness }), ); f(self); self.outer_trait_or_trait_impl = old; @@ -273,7 +268,7 @@ impl<'a> AstValidator<'a> { }; let make_trait_const_sugg = if const_trait_impl - && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent + && let TraitOrTraitImpl::Trait { span, constness: ast::Const::No } = parent { Some(span.shrink_to_lo()) } else { @@ -1131,10 +1126,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } visit::walk_item(self, item) } - ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => { + ItemKind::Trait(box Trait { + constness, + is_auto, + generics, + ident, + bounds, + items, + .. + }) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); - let is_const_trait = + // FIXME(const_trait_impl) remove this + let alt_const_trait_span = attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span); + let constness = match (*constness, alt_const_trait_span) { + (Const::Yes(span), _) | (Const::No, Some(span)) => Const::Yes(span), + (Const::No, None) => Const::No, + }; if *is_auto == IsAuto::Yes { // Auto traits cannot have generics, super traits nor contain items. self.deny_generic_params(generics, ident.span); @@ -1145,13 +1153,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound // context for the supertraits. - let disallowed = - is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span }); + let disallowed = matches!(constness, ast::Const::No) + .then(|| TildeConstReason::Trait { span: item.span }); self.with_tilde_const(disallowed, |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits) }); - self.with_in_trait(item.span, is_const_trait, |this| { + self.with_in_trait(item.span, constness, |this| { walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait); }); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 3b2730d4ff9..c1ebd025c7a 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -590,7 +590,7 @@ pub(crate) struct ConstBoundTraitObject { } // FIXME(const_trait_impl): Consider making the note/reason the message of the diagnostic. -// FIXME(const_trait_impl): Provide structured suggestions (e.g., add `const` / `#[const_trait]` here). +// FIXME(const_trait_impl): Provide structured suggestions (e.g., add `const` here). #[derive(Diagnostic)] #[diag(ast_passes_tilde_const_disallowed)] pub(crate) struct TildeConstDisallowed { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 6c442553976..11c97a552c6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -357,6 +357,7 @@ impl<'a> State<'a> { self.bclose(item.span, empty, cb); } ast::ItemKind::Trait(box ast::Trait { + constness, safety, is_auto, ident, @@ -366,6 +367,7 @@ impl<'a> State<'a> { }) => { let (cb, ib) = self.head(""); self.print_visibility(&item.vis); + self.print_constness(*constness); self.print_safety(*safety); self.print_is_auto(*is_auto); self.word_nbsp("trait"); diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 3dedeb1b372..3157b18b635 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -110,6 +110,22 @@ pub enum DeprecatedSince { Err, } +#[derive( + Copy, + Debug, + Eq, + PartialEq, + Encodable, + Decodable, + Clone, + HashStable_Generic, + PrintAttribute +)] +pub enum CoverageStatus { + On, + Off, +} + impl Deprecation { /// Whether an item marked with #[deprecated(since = "X")] is currently /// deprecated (i.e., whether X is not greater than the current rustc @@ -274,6 +290,9 @@ pub enum AttributeKind { /// Represents `#[const_trait]`. ConstTrait(Span), + /// Represents `#[coverage]`. + Coverage(Span, CoverageStatus), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), @@ -420,6 +439,9 @@ pub enum AttributeKind { /// Represents `#[rustc_unsafe_specialization_marker]`. UnsafeSpecializationMarker(Span), + /// Represents `#[unstable_feature_bound]`. + UnstableFeatureBound(ThinVec<(Symbol, Span)>), + /// Represents `#[used]` Used { used_by: UsedBy, span: Span }, // tidy-alphabetical-end 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 3e2dc0a15b2..8a3eb2ac845 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -28,6 +28,7 @@ impl AttributeKind { ConstStability { .. } => Yes, ConstStabilityIndirect => No, ConstTrait(..) => No, + Coverage(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, @@ -71,6 +72,7 @@ impl AttributeKind { TrackCaller(..) => Yes, TypeConst(..) => Yes, UnsafeSpecializationMarker(..) => No, + UnstableFeatureBound(..) => No, Used { .. } => No, // tidy-alphabetical-end } diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 8d0ead63a8d..35ff48cb5f2 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -136,6 +136,9 @@ attr_parsing_unrecognized_repr_hint = attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change +attr_parsing_unstable_feature_bound_incompatible_stability = Item annotated with `#[unstable_feature_bound]` should not be stable + .help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` + attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean attr_parsing_unsupported_literal_cfg_string = diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 1c51c3eee4e..a6bd2306ec5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser { } } +pub(crate) struct UnstableFeatureBoundParser; +impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; + type Item = (Symbol, Span); + const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items); + const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ..."); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> { + if !cx.features().staged_api() { + cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span }); + } + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) + } +} + pub(crate) struct AllowConstFnUnstableParser; impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser { const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index a56855b3bd3..6373cf6e08a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -9,7 +9,7 @@ use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, Stage}; +use crate::context::{AcceptContext, ShouldEmit, Stage}; use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; use crate::{ CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg, @@ -90,7 +90,7 @@ fn parse_cfg_entry_version<S: Stage>( list: &MetaItemListParser<'_>, meta_span: Span, ) -> Option<CfgEntry> { - try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features())); + try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option()); let Some(version) = list.single() else { cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span }); return None; @@ -119,7 +119,9 @@ fn parse_cfg_entry_target<S: Stage>( list: &MetaItemListParser<'_>, meta_span: Span, ) -> Option<CfgEntry> { - if !cx.features().cfg_target_compact() { + if let Some(features) = cx.features_option() + && !features.cfg_target_compact() + { feature_err( cx.sess(), sym::cfg_target_compact, @@ -186,12 +188,13 @@ pub fn eval_config_entry( cfg_entry: &CfgEntry, id: NodeId, features: Option<&Features>, + emit_lints: ShouldEmit, ) -> EvalConfigResult { match cfg_entry { CfgEntry::All(subs, ..) => { let mut all = None; for sub in subs { - let res = eval_config_entry(sess, sub, id, features); + let res = eval_config_entry(sess, sub, id, features, emit_lints); // We cannot short-circuit because `eval_config_entry` emits some lints if !res.as_bool() { all.get_or_insert(res); @@ -202,7 +205,7 @@ pub fn eval_config_entry( CfgEntry::Any(subs, span) => { let mut any = None; for sub in subs { - let res = eval_config_entry(sess, sub, id, features); + let res = eval_config_entry(sess, sub, id, features, emit_lints); // We cannot short-circuit because `eval_config_entry` emits some lints if res.as_bool() { any.get_or_insert(res); @@ -214,7 +217,7 @@ pub fn eval_config_entry( }) } CfgEntry::Not(sub, span) => { - if eval_config_entry(sess, sub, id, features).as_bool() { + if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() { EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } else { EvalConfigResult::True @@ -228,24 +231,28 @@ pub fn eval_config_entry( } } CfgEntry::NameValue { name, name_span, value, span } => { - match sess.psess.check_config.expecteds.get(name) { - Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => { - id.emit_span_lint( - sess, - UNEXPECTED_CFGS, - *span, - BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), - ); + if let ShouldEmit::ErrorsAndLints = emit_lints { + match sess.psess.check_config.expecteds.get(name) { + Some(ExpectedValues::Some(values)) + if !values.contains(&value.map(|(v, _)| v)) => + { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), + ); + } + None if sess.psess.check_config.exhaustive_names => { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), + ); + } + _ => { /* not unexpected */ } } - None if sess.psess.check_config.exhaustive_names => { - id.emit_span_lint( - sess, - UNEXPECTED_CFGS, - *span, - BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), - ); - } - _ => { /* not unexpected */ } } if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 34d9b048348..3e542771d58 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, OptimizeAttr, UsedBy}; +use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold; } +pub(crate) struct CoverageParser; + +impl<S: Stage> SingleAttributeParser<S> for CoverageParser { + const PATH: &[Symbol] = &[sym::coverage]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(args) = args.list() else { + cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]); + return None; + }; + + let Some(arg) = args.single() else { + cx.expected_single_argument(args.span); + return None; + }; + + let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]); + + let Some(arg) = arg.meta_item() else { + fail_incorrect_argument(args.span); + return None; + }; + + let status = match arg.path().word_sym() { + Some(sym::off) => CoverageStatus::Off, + Some(sym::on) => CoverageStatus::On, + None | Some(_) => { + fail_incorrect_argument(arg.span()); + return None; + } + }; + + Some(AttributeKind::Coverage(cx.attr_span, status)) + } +} + pub(crate) struct ExportNameParser; impl<S: Stage> SingleAttributeParser<S> for ExportNameParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6bccd0042a8..8f405e5aad9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -98,6 +98,16 @@ impl<S: Stage> AttributeParser<S> for StabilityParser { } } + if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability { + for other_attr in cx.all_attrs { + if other_attr.word_is(sym::unstable_feature_bound) { + cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability { + span: cx.target_span, + }); + } + } + } + let (stability, span) = self.stability?; Some(AttributeKind::Stability { stability, span }) diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index d5e088effd5..e69a533699b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -91,6 +91,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject; } +// FIXME(const_trait_impl): remove this // Const traits pub(crate) struct ConstTraitParser; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 567341d1517..4d692d9562c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -13,10 +13,13 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; -use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; +use crate::attributes::allow_unstable::{ + AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, +}; use crate::attributes::codegen_attrs::{ - ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser, - OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, + ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, + OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, + UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -133,9 +136,11 @@ attribute_parsers!( Combine<AllowInternalUnstableParser>, Combine<ReprParser>, Combine<TargetFeatureParser>, + Combine<UnstableFeatureBoundParser>, // tidy-alphabetical-end // tidy-alphabetical-start + Single<CoverageParser>, Single<DeprecationParser>, Single<DummyParser>, Single<ExportNameParser>, @@ -223,7 +228,7 @@ impl Stage for Early { sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - if self.emit_errors { + if self.emit_errors.should_emit() { sess.dcx().emit_err(diag) } else { sess.dcx().create_err(diag).delay_as_bug() @@ -254,7 +259,7 @@ pub struct Early { /// Whether to emit errors or delay them as a bug /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted - pub emit_errors: bool, + pub emit_errors: ShouldEmit, } /// used when parsing attributes during ast lowering pub struct Late; @@ -449,6 +454,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, + list: false, + }, + }) + } + + pub(crate) fn expected_specific_argument_and_list( + &self, + span: Span, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: false, + list: true, }, }) } @@ -466,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, + list: false, }, }) } @@ -555,6 +580,25 @@ pub enum OmitDoc { Skip, } +#[derive(Copy, Clone)] +pub enum ShouldEmit { + /// The operation will emit errors and lints. + /// This is usually what you need. + ErrorsAndLints, + /// The operation will emit *not* errors and lints. + /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`. + Nothing, +} + +impl ShouldEmit { + pub fn should_emit(&self) -> bool { + match self { + ShouldEmit::ErrorsAndLints => true, + ShouldEmit::Nothing => false, + } + } +} + /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess, S: Stage = Late> { @@ -597,7 +641,7 @@ impl<'sess> AttributeParser<'sess, Early> { tools: Vec::new(), parse_only: Some(sym), sess, - stage: Early { emit_errors: false }, + stage: Early { emit_errors: ShouldEmit::Nothing }, }; let mut parsed = p.parse_attribute_list( attrs, @@ -620,7 +664,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, - emit_errors: bool, + emit_errors: ShouldEmit, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, template: &AttributeTemplate, ) -> T { diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 2102a26108b..dc54cb6b840 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -95,7 +95,7 @@ pub use attributes::cfg_old::*; pub use attributes::util::{ find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, }; -pub use context::{AttributeParser, Early, Late, OmitDoc}; +pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit}; pub use lints::emit_attribute_lint; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 97bf3d1c549..9a400e0fe10 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -504,6 +504,14 @@ pub(crate) struct UnrecognizedReprHint { } #[derive(Diagnostic)] +#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] +#[help] +pub(crate) struct UnstableFeatureBoundIncompatibleStability { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { #[primary_span] @@ -525,7 +533,9 @@ pub(crate) struct LinkOrdinalOutOfRange { pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, - ExpectedStringLiteral { byte_string: Option<Span> }, + ExpectedStringLiteral { + byte_string: Option<Span>, + }, ExpectedIntegerLiteral, ExpectedAtLeastOneArgument, ExpectedSingleArgument, @@ -533,7 +543,12 @@ pub(crate) enum AttributeParseErrorReason { UnexpectedLiteral, ExpectedNameValue(Option<Symbol>), DuplicateKey(Symbol), - ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, + ExpectedSpecificArgument { + possibilities: Vec<&'static str>, + strings: bool, + /// Should we tell the user to write a list when they didn't? + list: bool, + }, } pub(crate) struct AttributeParseError { @@ -607,7 +622,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { format!("expected this to be of the form `{name} = \"...\"`"), ); } - AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => { + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: false, + } => { let quote = if strings { '"' } else { '`' }; match possibilities.as_slice() { &[] => {} @@ -633,6 +652,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { } } } + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: true, + } => { + let quote = if strings { '"' } else { '`' }; + match possibilities.as_slice() { + &[] => {} + &[x] => { + diag.span_label( + self.span, + format!( + "this attribute is only valid with {quote}{x}{quote} as an argument" + ), + ); + } + [first, second] => { + diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument")); + } + [first @ .., second_to_last, last] => { + let mut res = String::new(); + for i in first { + res.push_str(&format!("{quote}{i}{quote}, ")); + } + res.push_str(&format!( + "{quote}{second_to_last}{quote} or {quote}{last}{quote}" + )); + + diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}")); + } + } + } } let suggestions = self.template.suggestions(false, &name); diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index ca636a8c999..9bb96b94506 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; @@ -7,7 +8,7 @@ use rustc_infer::infer::{InferCtxt, SubregionOrigin}; use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, + self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions, }; use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -70,10 +71,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { - let QueryRegionConstraints { outlives } = query_constraints; + let QueryRegionConstraints { outlives, assumptions } = query_constraints; + let assumptions = + elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); for &(predicate, constraint_category) in outlives { - self.convert(predicate, constraint_category); + self.convert(predicate, constraint_category, &assumptions); } } @@ -112,15 +115,20 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.category = outlives_requirement.category; self.span = outlives_requirement.blame_span; - self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category); + self.convert( + ty::OutlivesPredicate(subject, outlived_region), + self.category, + &Default::default(), + ); } (self.category, self.span, self.from_closure) = backup; } fn convert( &mut self, - predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, + predicate: ty::ArgOutlivesPredicate<'tcx>, constraint_category: ConstraintCategory<'tcx>, + higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'tcx>>, ) { let tcx = self.infcx.tcx; debug!("generate: constraints at: {:#?}", self.locations); @@ -150,7 +158,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } let mut next_outlives_predicates = vec![]; - for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates { + for (pred, constraint_category) in outlives_predicates { + // Constraint is implied by a coroutine's well-formedness. + if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions + && higher_ranked_assumptions.contains(&pred) + { + continue; + } + + let ty::OutlivesPredicate(k1, r2) = pred; match k1.kind() { GenericArgKind::Lifetime(r1) => { let r1_vid = self.to_region_vid(r1); @@ -266,14 +282,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { &self, ty: Ty<'tcx>, next_outlives_predicates: &mut Vec<( - ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, + ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { - if let Some(QueryRegionConstraints { outlives }) = constraints { + // FIXME(higher_ranked_auto): What should we do with the assumptions here? + if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints { next_outlives_predicates.extend(outlives.iter().copied()); } ty diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f877e5eaadb..d500088c259 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>( pre_obligations.is_empty(), "there should be no incoming region obligations = {pre_obligations:#?}", ); + let pre_assumptions = infcx.take_registered_region_assumptions(); + assert!( + pre_assumptions.is_empty(), + "there should be no incoming region assumptions = {pre_assumptions:#?}", + ); debug!(?normalized_inputs_and_output); diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index a98e9c6d1c7..dd8f0e46a0e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -23,6 +23,7 @@ pub(crate) fn expand_deriving_copy( methods: Vec::new(), associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push); @@ -46,6 +47,7 @@ pub(crate) fn expand_deriving_const_param_ty( methods: Vec::new(), associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push); @@ -60,6 +62,7 @@ pub(crate) fn expand_deriving_const_param_ty( methods: Vec::new(), associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push); @@ -83,6 +86,7 @@ pub(crate) fn expand_deriving_unsized_const_param_ty( methods: Vec::new(), associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 69f8c273797..3c78f53c5cb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -87,6 +87,7 @@ pub(crate) fn expand_deriving_clone( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index eca79e4dc48..29d531219a6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -43,6 +43,7 @@ pub(crate) fn expand_deriving_eq( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand_ext(cx, mitem, item, push, true) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 1ed44c20bc6..0e1ecf3118a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -34,6 +34,7 @@ pub(crate) fn expand_deriving_ord( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index b1d950b8d89..990835fa277 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -30,6 +30,7 @@ pub(crate) fn expand_deriving_partial_eq( methods: Vec::new(), associated_types: Vec::new(), is_const: false, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; structural_trait_def.expand(cx, mitem, item, push); @@ -58,6 +59,7 @@ pub(crate) fn expand_deriving_partial_eq( methods, associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 0a076dd670b..f5d262ece36 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -64,6 +64,7 @@ pub(crate) fn expand_deriving_partial_ord( methods: vec![partial_cmp_def], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 8ab21986e68..1d63ce7d5fd 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_debug( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 1fe567e23f4..b4e2d27fed3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -51,6 +51,7 @@ pub(crate) fn expand_deriving_default( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index c55a9e73e38..b24e5563761 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -181,9 +181,11 @@ use std::{iter, vec}; pub(crate) use StaticFields::*; pub(crate) use SubstructureFields::*; use rustc_ast::ptr::P; +use rustc_ast::token::{IdentIsRaw, LitKind, Token, TokenKind}; +use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree}; use rustc_ast::{ - self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, - Generics, Mutability, PatKind, VariantData, + self as ast, AnonConst, AttrArgs, BindingMode, ByRef, DelimArgs, EnumDef, Expr, GenericArg, + GenericParamKind, Generics, Mutability, PatKind, Safety, VariantData, }; use rustc_attr_data_structures::{AttributeKind, ReprPacked}; use rustc_attr_parsing::AttributeParser; @@ -222,6 +224,8 @@ pub(crate) struct TraitDef<'a> { pub associated_types: Vec<(Ident, Ty)>, pub is_const: bool, + + pub is_staged_api_crate: bool, } pub(crate) struct MethodDef<'a> { @@ -784,8 +788,45 @@ impl<'a> TraitDef<'a> { // Create the type of `self`. let path = cx.path_all(self.span, false, vec![type_ident], self_params); let self_type = cx.ty_path(path); + let rustc_const_unstable = + cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span)); + + let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),]; + + // Only add `rustc_const_unstable` attributes if `derive_const` is used within libcore/libstd, + // Other crates don't need stability attributes, so adding them is not useful, but libcore needs them + // on all const trait impls. + if self.is_const && self.is_staged_api_crate { + attrs.push( + cx.attr_nested( + rustc_ast::AttrItem { + unsafety: Safety::Default, + path: rustc_const_unstable, + args: AttrArgs::Delimited(DelimArgs { + dspan: DelimSpan::from_single(self.span), + delim: rustc_ast::token::Delimiter::Parenthesis, + tokens: [ + TokenKind::Ident(sym::feature, IdentIsRaw::No), + TokenKind::Eq, + TokenKind::lit(LitKind::Str, sym::derive_const, None), + TokenKind::Comma, + TokenKind::Ident(sym::issue, IdentIsRaw::No), + TokenKind::Eq, + TokenKind::lit(LitKind::Str, sym::derive_const_issue, None), + ] + .into_iter() + .map(|kind| { + TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone) + }) + .collect(), + }), + tokens: None, + }, + self.span, + ), + ) + } - let attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),]; let opt_trait_ref = Some(trait_ref); cx.item( diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 6e6dbe19e4d..78534449895 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -41,6 +41,7 @@ pub(crate) fn expand_deriving_hash( }], associated_types: Vec::new(), is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), }; hash_trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index cebfffa1e16..ecfd46a84ec 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -4,8 +4,8 @@ use std::sync::Arc; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{join_path_idents, token}; use rustc_ast_pretty::pprust; use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, @@ -100,7 +100,7 @@ pub(crate) fn expand_mod( let sp = cx.with_def_site_ctxt(sp); check_zero_tts(cx, sp, tts, "module_path!"); let mod_path = &cx.current_expansion.module.mod_path; - let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::"); + let string = join_path_idents(mod_path); ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))) } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index b067578794b..ba3d8368b2a 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -5,7 +5,7 @@ use std::assert_matches::assert_matches; use std::iter; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, GenericParamKind, attr}; +use rustc_ast::{self as ast, GenericParamKind, attr, join_path_idents}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, Level}; use rustc_expand::base::*; @@ -446,12 +446,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, } fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String { - mod_path - .iter() - .chain(iter::once(item_ident)) - .map(|x| x.to_string()) - .collect::<Vec<String>>() - .join("::") + join_path_idents(mod_path.iter().chain(iter::once(item_ident))) } enum ShouldPanic { diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 442151fe32d..8ec3599b63d 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -530,8 +530,8 @@ fn codegen_cgu_content( for (mono_item, item_data) in mono_items { match mono_item { MonoItem::Fn(instance) => { - if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) - { + let flags = tcx.codegen_instance_attrs(instance.def).flags; + if flags.contains(CodegenFnAttrFlags::NAKED) { rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm }, instance, diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index b1f185b551c..b3497503bf0 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -127,7 +127,7 @@ fn codegen_and_compile_fn<'tcx>( module: &mut dyn Module, instance: Instance<'tcx>, ) { - if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + if tcx.codegen_instance_attrs(instance.def).flags.contains(CodegenFnAttrFlags::NAKED) { tcx.dcx() .span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode"); } diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index ffd47cace38..8f83c30b598 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -35,7 +35,7 @@ fn predefine_mono_items<'tcx>( is_compiler_builtins, ); let is_naked = tcx - .codegen_fn_attrs(instance.def_id()) + .codegen_instance_attrs(instance.def) .flags .contains(CodegenFnAttrFlags::NAKED); module diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index bf0927dc590..7a1ae6ca9c8 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -87,7 +87,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( #[cfg_attr(not(feature = "master"), allow(unused_variables))] func: Function<'gcc>, instance: ty::Instance<'tcx>, ) { - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); #[cfg(feature = "master")] { diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 10fce860b77..e554dd2500b 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; +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::*; @@ -176,7 +176,7 @@ pub(crate) fn run_fat( cgcx: &CodegenContext<GccCodegenBackend>, modules: Vec<FatLtoInput<GccCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, -) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> { +) -> Result<ModuleCodegen<GccContext>, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, dcx)?; @@ -201,7 +201,7 @@ fn fat_lto( mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, tmp_path: TempDir, //symbols_below_threshold: &[String], -) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> { +) -> Result<ModuleCodegen<GccContext>, FatalError> { let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -334,7 +334,7 @@ fn fat_lto( // of now. module.module_llvm.temp_dir = Some(tmp_path); - Ok(LtoModuleCodegen::Fat(module)) + Ok(module) } pub struct ModuleBuffer(PathBuf); @@ -358,7 +358,7 @@ pub(crate) fn run_thin( cgcx: &CodegenContext<GccCodegenBackend>, modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, -) -> Result<(Vec<LtoModuleCodegen<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> { +) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, dcx)?; @@ -427,7 +427,7 @@ fn thin_lto( tmp_path: TempDir, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, //_symbols_below_threshold: &[String], -) -> Result<(Vec<LtoModuleCodegen<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> { +) -> Result<(Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>), FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); info!("going for that thin, thin LTO"); @@ -573,8 +573,7 @@ fn thin_lto( }*/ info!(" - {}: re-compiled", module_name); - opt_jobs - .push(LtoModuleCodegen::Thin(ThinModule { shared: shared.clone(), idx: module_index })); + opt_jobs.push(ThinModule { shared: shared.clone(), idx: module_index }); } // Save the current ThinLTO import information for the next compilation diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index d03d063bdac..113abe70805 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -16,10 +16,12 @@ use crate::{GccCodegenBackend, GccContext}; pub(crate) fn codegen( cgcx: &CodegenContext<GccCodegenBackend>, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen<GccContext>, config: &ModuleConfig, ) -> Result<CompiledModule, FatalError> { + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name); { let context = &module.module_llvm.context; diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 189ac7cd779..e7ca95af594 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -105,7 +105,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) let is_hidden = if is_generic { // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() - || tcx.codegen_fn_attrs(instance_def_id).inline + || tcx.codegen_instance_attrs(instance.def).inline == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index d8fae1ca47d..d81bcc59775 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -93,7 +93,7 @@ use gccjit::{CType, Context, OptimizationLevel}; use gccjit::{TargetInfo, Version}; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn, }; @@ -353,11 +353,16 @@ impl WriteBackendMethods for GccCodegenBackend { type ThinData = ThinData; type ThinBuffer = ThinBuffer; - fn run_fat_lto( + fn run_and_optimize_fat_lto( cgcx: &CodegenContext<Self>, modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<LtoModuleCodegen<Self>, FatalError> { + diff_fncs: Vec<AutoDiffItem>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError> { + if !diff_fncs.is_empty() { + unimplemented!(); + } + back::lto::run_fat(cgcx, modules, cached_modules) } @@ -365,7 +370,7 @@ impl WriteBackendMethods for GccCodegenBackend { cgcx: &CodegenContext<Self>, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> { + ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> { back::lto::run_thin(cgcx, modules, cached_modules) } @@ -387,14 +392,6 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - fn optimize_fat( - _cgcx: &CodegenContext<Self>, - _module: &mut ModuleCodegen<Self::Module>, - ) -> Result<(), FatalError> { - // TODO(antoyo) - Ok(()) - } - fn optimize_thin( cgcx: &CodegenContext<Self>, thin: ThinModule<Self>, @@ -404,11 +401,10 @@ impl WriteBackendMethods for GccCodegenBackend { fn codegen( cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<CompiledModule, FatalError> { - back::write::codegen(cgcx, dcx, module, config) + back::write::codegen(cgcx, module, config) } fn prepare_thin( @@ -429,15 +425,6 @@ impl WriteBackendMethods for GccCodegenBackend { ) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::write::link(cgcx, dcx, modules) } - - fn autodiff( - _cgcx: &CodegenContext<Self>, - _module: &ModuleCodegen<Self::Module>, - _diff_functions: Vec<AutoDiffItem>, - _config: &ModuleConfig, - ) -> Result<(), FatalError> { - unimplemented!() - } } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index 539e3ac8507..51f35cbdee4 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -53,7 +53,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); self.linkage.set(base::linkage_to_gcc(linkage)); let decl = self.declare_fn(symbol_name, fn_abi); - //let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + //let attrs = self.tcx.codegen_instance_attrs(instance.def); attributes::from_fn_attrs(self, decl, instance); diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3885f18271f..f197ea74473 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,5 +1,4 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable -codegen_llvm_autodiff_without_lto = using the autodiff feature requires using fat-lto codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1ea5a062254..c32f11b27f3 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -344,7 +344,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( llfn: &'ll Value, instance: ty::Instance<'tcx>, ) { - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); let mut to_add = SmallVec::<[_; 16]>::new(); diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 74418adc43c..655e1c95373 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; +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::*; @@ -201,7 +201,7 @@ pub(crate) fn run_fat( cgcx: &CodegenContext<LlvmCodegenBackend>, modules: Vec<FatLtoInput<LlvmCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, -) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> { +) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; @@ -217,7 +217,7 @@ pub(crate) fn run_thin( cgcx: &CodegenContext<LlvmCodegenBackend>, modules: Vec<(String, ThinBuffer)>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, -) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> { +) -> 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)?; @@ -248,7 +248,7 @@ fn fat_lto( cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, symbols_below_threshold: &[*const libc::c_char], -) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> { +) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -366,7 +366,7 @@ fn fat_lto( save_temp_bitcode(cgcx, &module, "lto.after-restriction"); } - Ok(LtoModuleCodegen::Fat(module)) + Ok(module) } pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>); @@ -436,7 +436,7 @@ fn thin_lto( serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, symbols_below_threshold: &[*const libc::c_char], -) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> { +) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); unsafe { info!("going for that thin, thin LTO"); @@ -568,10 +568,7 @@ fn thin_lto( } info!(" - {}: re-compiled", module_name); - opt_jobs.push(LtoModuleCodegen::Thin(ThinModule { - shared: Arc::clone(&shared), - idx: module_index, - })); + opt_jobs.push(ThinModule { shared: Arc::clone(&shared), idx: module_index }); } // Save the current ThinLTO import information for the next compilation diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 506286fc255..68279008c03 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -817,10 +817,12 @@ pub(crate) fn link( pub(crate) fn codegen( cgcx: &CodegenContext<LlvmCodegenBackend>, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen<ModuleLlvm>, config: &ModuleConfig, ) -> Result<CompiledModule, FatalError> { + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name); { let llmod = module.module_llvm.llmod(); @@ -879,9 +881,7 @@ pub(crate) fn codegen( .generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name); let thin_bc = module.thin_lto_buffer.as_deref().expect("cannot find embedded bitcode"); - unsafe { - embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, &thin_bc); - } + embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, &thin_bc); } } @@ -945,7 +945,7 @@ pub(crate) fn codegen( // binaries. So we must clone the module to produce the asm output // if we are also producing object code. let llmod = if let EmitObj::ObjectCode(_) = config.emit_obj { - unsafe { llvm::LLVMCloneModule(llmod) } + llvm::LLVMCloneModule(llmod) } else { llmod }; @@ -1073,7 +1073,7 @@ pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> } /// Embed the bitcode of an LLVM module for LTO in the LLVM module itself. -unsafe fn embed_bitcode( +fn embed_bitcode( cgcx: &CodegenContext<LlvmCodegenBackend>, llcx: &llvm::Context, llmod: &llvm::Module, @@ -1115,43 +1115,40 @@ unsafe fn embed_bitcode( // Unfortunately, LLVM provides no way to set custom section flags. For ELF // and COFF we emit the sections using module level inline assembly for that // reason (see issue #90326 for historical background). - unsafe { - if cgcx.target_is_like_darwin - || cgcx.target_is_like_aix - || cgcx.target_arch == "wasm32" - || cgcx.target_arch == "wasm64" - { - // We don't need custom section flags, create LLVM globals. - let llconst = common::bytes_in_context(llcx, bitcode); - let llglobal = - llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.module"); - llvm::set_initializer(llglobal, llconst); - - llvm::set_section(llglobal, bitcode_section_name(cgcx)); - llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - - let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); - let llglobal = - llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline"); - llvm::set_initializer(llglobal, llconst); - let section = if cgcx.target_is_like_darwin { - c"__LLVM,__cmdline" - } else if cgcx.target_is_like_aix { - c".info" - } else { - c".llvmcmd" - }; - llvm::set_section(llglobal, section); - llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); + + if cgcx.target_is_like_darwin + || cgcx.target_is_like_aix + || cgcx.target_arch == "wasm32" + || cgcx.target_arch == "wasm64" + { + // We don't need custom section flags, create LLVM globals. + let llconst = common::bytes_in_context(llcx, bitcode); + let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.module"); + llvm::set_initializer(llglobal, llconst); + + llvm::set_section(llglobal, bitcode_section_name(cgcx)); + llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); + llvm::LLVMSetGlobalConstant(llglobal, llvm::True); + + let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); + let llglobal = llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline"); + llvm::set_initializer(llglobal, llconst); + let section = if cgcx.target_is_like_darwin { + c"__LLVM,__cmdline" + } else if cgcx.target_is_like_aix { + c".info" } else { - // We need custom section flags, so emit module-level inline assembly. - let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; - let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); - llvm::append_module_inline_asm(llmod, &asm); - let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); - llvm::append_module_inline_asm(llmod, &asm); - } + c".llvmcmd" + }; + llvm::set_section(llglobal, section); + llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); + } else { + // We need custom section flags, so emit module-level inline assembly. + let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; + let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); + llvm::append_module_inline_asm(llmod, &asm); + let asm = create_section_with_flags_asm(".llvmcmd", section_flags, cmdline.as_bytes()); + llvm::append_module_inline_asm(llmod, &asm); } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 9c3b866aa3c..514923ad6f3 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -302,10 +302,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { return; } - let id_str = "branch_weights"; - let id = unsafe { - llvm::LLVMMDStringInContext2(self.cx.llcx, id_str.as_ptr().cast(), id_str.len()) - }; + let id = self.cx.create_metadata(b"branch_weights"); // For switch instructions with 2 targets, the `llvm.expect` intrinsic is used. // This function handles switch instructions with more than 2 targets and it needs to @@ -637,17 +634,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } else if place.layout.is_llvm_immediate() { let mut const_llval = None; let llty = place.layout.llvm_type(self); - unsafe { - if let Some(global) = llvm::LLVMIsAGlobalVariable(place.val.llval) { - if llvm::LLVMIsGlobalConstant(global) == llvm::True { - if let Some(init) = llvm::LLVMGetInitializer(global) { - if self.val_ty(init) == llty { - const_llval = Some(init); - } + if let Some(global) = llvm::LLVMIsAGlobalVariable(place.val.llval) { + if llvm::LLVMIsGlobalConstant(global) == llvm::True { + if let Some(init) = llvm::LLVMGetInitializer(global) { + if self.val_ty(init) == llty { + const_llval = Some(init); } } } } + let llval = const_llval.unwrap_or_else(|| { let load = self.load(llty, place.val.llval, place.val.align); if let abi::BackendRepr::Scalar(scalar) = place.layout.backend_repr { @@ -1721,7 +1717,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } else { cfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; - let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); + let typeid_metadata = self.cx.create_metadata(typeid.as_bytes()); let dbg_loc = self.get_dbg_loc(); // Test whether the function pointer is associated with the type identifier using the diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 5afb9a60d42..829b3c513c2 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -2,7 +2,6 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; -use rustc_codegen_ssa::back::write::ModuleConfig; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_errors::FatalError; @@ -76,12 +75,12 @@ fn match_args_from_caller_to_enzyme<'ll>( outer_pos = 1; } - let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap(); - let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap(); - let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap(); - let enzyme_dupv = cx.create_metadata("enzyme_dupv".to_string()).unwrap(); - let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap(); - let enzyme_dupnoneedv = cx.create_metadata("enzyme_dupnoneedv".to_string()).unwrap(); + let enzyme_const = cx.create_metadata(b"enzyme_const"); + let enzyme_out = cx.create_metadata(b"enzyme_out"); + let enzyme_dup = cx.create_metadata(b"enzyme_dup"); + let enzyme_dupv = cx.create_metadata(b"enzyme_dupv"); + let enzyme_dupnoneed = cx.create_metadata(b"enzyme_dupnoneed"); + let enzyme_dupnoneedv = cx.create_metadata(b"enzyme_dupnoneedv"); while activity_pos < inputs.len() { let diff_activity = inputs[activity_pos as usize]; @@ -378,12 +377,12 @@ fn generate_enzyme_call<'ll>( let mut args = Vec::with_capacity(num_args as usize + 1); args.push(fn_to_diff); - let enzyme_primal_ret = cx.create_metadata("enzyme_primal_return".to_string()).unwrap(); + let enzyme_primal_ret = cx.create_metadata(b"enzyme_primal_return"); if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { args.push(cx.get_metadata_value(enzyme_primal_ret)); } if attrs.width > 1 { - let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap(); + let enzyme_width = cx.create_metadata(b"enzyme_width"); args.push(cx.get_metadata_value(enzyme_width)); args.push(cx.get_const_int(cx.type_i64(), attrs.width as u64)); } @@ -461,7 +460,6 @@ pub(crate) fn differentiate<'ll>( module: &'ll ModuleCodegen<ModuleLlvm>, cgcx: &CodegenContext<LlvmCodegenBackend>, diff_items: Vec<AutoDiffItem>, - _config: &ModuleConfig, ) -> Result<(), FatalError> { for item in &diff_items { trace!("{}", item); diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 6d68eca60af..5a3dd90ab24 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -102,7 +102,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let is_hidden = if is_generic { // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() - || tcx.codegen_fn_attrs(instance_def_id).inline + || tcx.codegen_instance_attrs(instance.def).inline == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index b9b5c776d86..f9ab96b5789 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -215,10 +215,10 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { bug!("symbol `{}` is already defined", sym); }); llvm::set_initializer(g, sc); - unsafe { - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); - } + + llvm::set_global_constant(g, true); + llvm::set_unnamed_address(g, llvm::UnnamedAddr::Global); + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); // Cast to default address space if globals are in a different addrspace let g = self.const_pointercast(g, self.type_ptr()); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 5deddb3ed98..0b96b63bc85 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -17,13 +17,12 @@ use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::llvm::{self, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use crate::{base, debuginfo}; +use crate::{base, debuginfo, llvm}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, @@ -247,7 +246,7 @@ impl<'ll> CodegenCx<'ll, '_> { }; llvm::set_initializer(gv, cv); set_global_alignment(self, gv, align); - llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global); + llvm::set_unnamed_address(gv, llvm::UnnamedAddr::Global); gv } @@ -272,9 +271,8 @@ impl<'ll> CodegenCx<'ll, '_> { return gv; } let gv = self.static_addr_of_mut(cv, align, kind); - unsafe { - llvm::LLVMSetGlobalConstant(gv, True); - } + llvm::set_global_constant(gv, true); + self.const_globals.borrow_mut().insert(cv, gv); gv } @@ -398,149 +396,140 @@ impl<'ll> CodegenCx<'ll, '_> { } fn codegen_static_item(&mut self, def_id: DefId) { - unsafe { - assert!( - llvm::LLVMGetInitializer( - self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap() - ) - .is_none() - ); - let attrs = self.tcx.codegen_fn_attrs(def_id); + assert!( + llvm::LLVMGetInitializer( + self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap() + ) + .is_none() + ); + let attrs = self.tcx.codegen_fn_attrs(def_id); - let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else { - // Error has already been reported - return; - }; - let alloc = alloc.inner(); + let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else { + // Error has already been reported + return; + }; + let alloc = alloc.inner(); - let val_llty = self.val_ty(v); + let val_llty = self.val_ty(v); - let g = self.get_static_inner(def_id, val_llty); - let llty = self.get_type_of_global(g); + let g = self.get_static_inner(def_id, val_llty); + let llty = self.get_type_of_global(g); - let g = if val_llty == llty { - g - } else { - // codegen_static_initializer creates the global value just from the - // `Allocation` data by generating one big struct value that is just - // all the bytes and pointers after each other. This will almost never - // match the type that the static was declared with. Unfortunately - // we can't just LLVMConstBitCast our way out of it because that has very - // specific rules on what can be cast. So instead of adding a new way to - // generate static initializers that match the static's type, we picked - // the easier option and retroactively change the type of the static item itself. - let name = llvm::get_value_name(g); - llvm::set_value_name(g, b""); - - let linkage = llvm::get_linkage(g); - let visibility = llvm::get_visibility(g); - - let new_g = llvm::LLVMRustGetOrInsertGlobal( - self.llmod, - name.as_c_char_ptr(), - name.len(), - val_llty, - ); - - llvm::set_linkage(new_g, linkage); - llvm::set_visibility(new_g, visibility); - - // The old global has had its name removed but is returned by - // get_static since it is in the instance cache. Provide an - // alternative lookup that points to the new global so that - // global_asm! can compute the correct mangled symbol name - // for the global. - self.renamed_statics.borrow_mut().insert(def_id, new_g); - - // To avoid breaking any invariants, we leave around the old - // global for the moment; we'll replace all references to it - // with the new global later. (See base::codegen_backend.) - self.statics_to_rauw.borrow_mut().push((g, new_g)); - new_g - }; - set_global_alignment(self, g, alloc.align); - llvm::set_initializer(g, v); - - self.assume_dso_local(g, true); - - // Forward the allocation's mutability (picked by the const interner) to LLVM. - if alloc.mutability.is_not() { - llvm::LLVMSetGlobalConstant(g, llvm::True); - } + let g = if val_llty == llty { + g + } else { + // codegen_static_initializer creates the global value just from the + // `Allocation` data by generating one big struct value that is just + // all the bytes and pointers after each other. This will almost never + // match the type that the static was declared with. Unfortunately + // we can't just LLVMConstBitCast our way out of it because that has very + // specific rules on what can be cast. So instead of adding a new way to + // generate static initializers that match the static's type, we picked + // the easier option and retroactively change the type of the static item itself. + let name = String::from_utf8(llvm::get_value_name(g)) + .expect("we declare our statics with a utf8-valid name"); + llvm::set_value_name(g, b""); + + let linkage = llvm::get_linkage(g); + let visibility = llvm::get_visibility(g); + + let new_g = self.declare_global(&name, val_llty); + + llvm::set_linkage(new_g, linkage); + llvm::set_visibility(new_g, visibility); + + // The old global has had its name removed but is returned by + // get_static since it is in the instance cache. Provide an + // alternative lookup that points to the new global so that + // global_asm! can compute the correct mangled symbol name + // for the global. + self.renamed_statics.borrow_mut().insert(def_id, new_g); + + // To avoid breaking any invariants, we leave around the old + // global for the moment; we'll replace all references to it + // with the new global later. (See base::codegen_backend.) + self.statics_to_rauw.borrow_mut().push((g, new_g)); + new_g + }; + set_global_alignment(self, g, alloc.align); + llvm::set_initializer(g, v); - debuginfo::build_global_var_di_node(self, def_id, g); + self.assume_dso_local(g, true); - if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { - llvm::set_thread_local_mode(g, self.tls_model); - } + // Forward the allocation's mutability (picked by the const interner) to LLVM. + if alloc.mutability.is_not() { + llvm::set_global_constant(g, true); + } - // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. The exception to this - // is the `.init_array` section which are treated specially by the wasm linker. - if self.tcx.sess.target.is_like_wasm - && attrs - .link_section - .map(|link_section| !link_section.as_str().starts_with(".init_array")) - .unwrap_or(true) - { - if let Some(section) = attrs.link_section { - let section = llvm::LLVMMDStringInContext2( - self.llcx, - section.as_str().as_c_char_ptr(), - section.as_str().len(), - ); - assert!(alloc.provenance().ptrs().is_empty()); - - // The `inspect` method is okay here because we checked for provenance, and - // because we are doing this access to inspect the final interpreter state (not - // as part of the interpreter execution). - let bytes = - alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()); - let alloc = - llvm::LLVMMDStringInContext2(self.llcx, bytes.as_c_char_ptr(), bytes.len()); - let data = [section, alloc]; - let meta = llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()); - let val = self.get_metadata_value(meta); + debuginfo::build_global_var_di_node(self, def_id, g); + + if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { + llvm::set_thread_local_mode(g, self.tls_model); + } + + // Wasm statics with custom link sections get special treatment as they + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { + if let Some(section) = attrs.link_section { + let section = self.create_metadata(section.as_str().as_bytes()); + assert!(alloc.provenance().ptrs().is_empty()); + + // The `inspect` method is okay here because we checked for provenance, and + // because we are doing this access to inspect the final interpreter state (not + // as part of the interpreter execution). + let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()); + let alloc = self.create_metadata(bytes); + let data = [section, alloc]; + let meta = + unsafe { llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len()) }; + let val = self.get_metadata_value(meta); + unsafe { llvm::LLVMAddNamedMetadataOperand( self.llmod, c"wasm.custom_sections".as_ptr(), val, - ); - } - } else { - base::set_link_section(g, attrs); + ) + }; } + } else { + base::set_link_section(g, attrs); + } - base::set_variable_sanitizer_attrs(g, attrs); - - if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) { - // `USED` and `USED_LINKER` can't be used together. - assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); - - // The semantics of #[used] in Rust only require the symbol to make it into the - // object file. It is explicitly allowed for the linker to strip the symbol if it - // is dead, which means we are allowed to use `llvm.compiler.used` instead of - // `llvm.used` here. - // - // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique - // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs - // in the handling of `.init_array` (the static constructor list) in versions of - // the gold linker (prior to the one released with binutils 2.36). - // - // That said, we only ever emit these when `#[used(compiler)]` is explicitly - // requested. This is to avoid similar breakage on other targets, in particular - // MachO targets have *their* static constructor lists broken if `llvm.compiler.used` - // is emitted rather than `llvm.used`. However, that check happens when assigning - // the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to - // take care of it here. - self.add_compiler_used_global(g); - } - if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { - // `USED` and `USED_LINKER` can't be used together. - assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)); + base::set_variable_sanitizer_attrs(g, attrs); + + if attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); + + // The semantics of #[used] in Rust only require the symbol to make it into the + // object file. It is explicitly allowed for the linker to strip the symbol if it + // is dead, which means we are allowed to use `llvm.compiler.used` instead of + // `llvm.used` here. + // + // Additionally, https://reviews.llvm.org/D97448 in LLVM 13 started emitting unique + // sections with SHF_GNU_RETAIN flag for llvm.used symbols, which may trigger bugs + // in the handling of `.init_array` (the static constructor list) in versions of + // the gold linker (prior to the one released with binutils 2.36). + // + // That said, we only ever emit these when `#[used(compiler)]` is explicitly + // requested. This is to avoid similar breakage on other targets, in particular + // MachO targets have *their* static constructor lists broken if `llvm.compiler.used` + // is emitted rather than `llvm.used`. However, that check happens when assigning + // the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to + // take care of it here. + self.add_compiler_used_global(g); + } + if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)); - self.add_used_global(g); - } + self.add_used_global(g); } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 90582e23b04..6a23becaa96 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -34,7 +34,6 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; -use crate::common::AsCCharPtr; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::Metadata; use crate::type_::Type; @@ -169,6 +168,8 @@ pub(crate) unsafe fn create_module<'ll>( let mod_name = SmallCStr::new(mod_name); let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) }; + let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); + let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); @@ -473,18 +474,14 @@ pub(crate) unsafe fn create_module<'ll>( #[allow(clippy::option_env_unwrap)] let rustc_producer = format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); - let name_metadata = unsafe { - llvm::LLVMMDStringInContext2( - llcx, - rustc_producer.as_c_char_ptr(), - rustc_producer.as_bytes().len(), - ) - }; + + let name_metadata = cx.create_metadata(rustc_producer.as_bytes()); + unsafe { llvm::LLVMAddNamedMetadataOperand( llmod, c"llvm.ident".as_ptr(), - &llvm::LLVMMetadataAsValue(llcx, llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)), + &cx.get_metadata_value(llvm::LLVMMDNodeInContext2(llcx, &name_metadata, 1)), ); } @@ -698,10 +695,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { } } - pub(crate) fn create_metadata(&self, name: String) -> Option<&'ll Metadata> { - Some(unsafe { + pub(crate) fn create_metadata(&self, name: &[u8]) -> &'ll Metadata { + unsafe { llvm::LLVMMDStringInContext2(self.llcx(), name.as_ptr() as *const c_char, name.len()) - }) + } } pub(crate) fn get_functions(&self) -> Vec<&'ll Value> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 49ee96a41d6..61555ac2f6f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -74,7 +74,7 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>( llvm::set_section(section_var, c".debug_gdb_scripts"); llvm::set_initializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); - llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); + llvm::set_unnamed_address(section_var, llvm::UnnamedAddr::Global); llvm::set_linkage(section_var, llvm::Linkage::LinkOnceODRLinkage); // This should make sure that the whole section is not larger than // the string it contains. Otherwise we get a warning from GDB. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9b4736e50e6..0e9dbfba658 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{iter, ptr}; -use libc::{c_char, c_longlong, c_uint}; +use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Size}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; @@ -1582,13 +1582,9 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( }; let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref); + let typeid = cx.create_metadata(trait_ref_typeid.as_bytes()); unsafe { - let typeid = llvm::LLVMMDStringInContext2( - cx.llcx, - trait_ref_typeid.as_ptr() as *const c_char, - trait_ref_typeid.as_bytes().len(), - ); let v = [llvm::LLVMValueAsMetadata(cx.const_usize(0)), typeid]; llvm::LLVMRustGlobalAddMetadata( vtable, @@ -1630,7 +1626,7 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>( // When full debuginfo is enabled, we want to try and prevent vtables from being // merged. Otherwise debuggers will have a hard time mapping from dyn pointer // to concrete type. - llvm::SetUnnamedAddress(vtable, llvm::UnnamedAddr::No); + llvm::set_unnamed_address(vtable, llvm::UnnamedAddr::No); let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 2419ec1f888..eb75716d768 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -49,7 +49,7 @@ pub(crate) fn declare_simple_fn<'ll>( }; llvm::SetFunctionCallConv(llfn, callconv); - llvm::SetUnnamedAddress(llfn, unnamed); + llvm::set_unnamed_address(llfn, unnamed); llvm::set_visibility(llfn, visibility); llfn @@ -176,7 +176,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { { let typeid = cfi::typeid_for_instance(self.tcx, instance, options); if typeids.insert(typeid.clone()) { - self.add_type_metadata(llfn, typeid); + self.add_type_metadata(llfn, typeid.as_bytes()); } } } else { @@ -189,7 +189,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { .map(cfi::TypeIdOptions::from_iter) { let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); - self.add_type_metadata(llfn, typeid); + self.add_type_metadata(llfn, typeid.as_bytes()); } } } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index d50ad8a1a9c..31d49e86319 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -38,10 +38,6 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_lto)] -pub(crate) struct AutoDiffWithoutLTO; - -#[derive(Diagnostic)] #[diag(codegen_llvm_autodiff_without_enable)] pub(crate) struct AutoDiffWithoutEnable; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 63ca51b006d..6db4e122ad6 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -26,11 +26,11 @@ use std::mem::ManuallyDrop; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use context::SimpleCx; -use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig}; +use errors::ParseTargetMachineConfig; use llvm_util::target_config; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; -use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; @@ -43,7 +43,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{Lto, OptLevel, OutputFilenames, PrintKind, PrintRequest}; +use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; mod back { @@ -174,18 +174,29 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::write::link(cgcx, dcx, modules) } - fn run_fat_lto( + fn run_and_optimize_fat_lto( cgcx: &CodegenContext<Self>, modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<LtoModuleCodegen<Self>, FatalError> { - back::lto::run_fat(cgcx, modules, cached_modules) + diff_fncs: Vec<AutoDiffItem>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError> { + let mut module = back::lto::run_fat(cgcx, modules, cached_modules)?; + + if !diff_fncs.is_empty() { + builder::autodiff::differentiate(&module, cgcx, diff_fncs)?; + } + + let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); + back::lto::run_pass_manager(cgcx, dcx, &mut module, false)?; + + Ok(module) } fn run_thin_lto( cgcx: &CodegenContext<Self>, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> { + ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError> { back::lto::run_thin(cgcx, modules, cached_modules) } fn optimize( @@ -196,14 +207,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<(), FatalError> { back::write::optimize(cgcx, dcx, module, config) } - fn optimize_fat( - cgcx: &CodegenContext<Self>, - module: &mut ModuleCodegen<Self::Module>, - ) -> Result<(), FatalError> { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - back::lto::run_pass_manager(cgcx, dcx, module, false) - } fn optimize_thin( cgcx: &CodegenContext<Self>, thin: ThinModule<Self>, @@ -212,11 +215,10 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn codegen( cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<CompiledModule, FatalError> { - back::write::codegen(cgcx, dcx, module, config) + back::write::codegen(cgcx, module, config) } fn prepare_thin( module: ModuleCodegen<Self::Module>, @@ -227,19 +229,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) } - /// Generate autodiff rules - fn autodiff( - cgcx: &CodegenContext<Self>, - module: &ModuleCodegen<Self::Module>, - diff_fncs: Vec<AutoDiffItem>, - config: &ModuleConfig, - ) -> Result<(), FatalError> { - if cgcx.lto != Lto::Fat { - let dcx = cgcx.create_dcx(); - return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); - } - builder::autodiff::differentiate(module, cgcx, diff_fncs, config) - } } impl LlvmCodegenBackend { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 5c34ab2e304..0b1e632cbc4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1009,7 +1009,7 @@ unsafe extern "C" { ModuleID: *const c_char, C: &Context, ) -> &Module; - pub(crate) fn LLVMCloneModule(M: &Module) -> &Module; + pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; @@ -1168,18 +1168,18 @@ unsafe extern "C" { pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables - pub(crate) fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; + pub(crate) safe fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; pub(crate) fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>; pub(crate) fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>; pub(crate) fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>; pub(crate) fn LLVMDeleteGlobal(GlobalVar: &Value); - pub(crate) fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; + pub(crate) safe fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; pub(crate) fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value); - pub(crate) fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; + pub(crate) safe fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; pub(crate) fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); - pub(crate) fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; - pub(crate) fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); + pub(crate) safe fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; + pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); // Operations on attributes @@ -1718,7 +1718,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; - pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); + pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 3fc83fca352..154ba4fd690 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -217,10 +217,8 @@ pub(crate) fn SetUniqueComdat(llmod: &Module, val: &Value) { set_comdat(llmod, val, &name); } -pub(crate) fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { - unsafe { - LLVMSetUnnamedAddress(global, unnamed); - } +pub(crate) fn set_unnamed_address(global: &Value, unnamed: UnnamedAddr) { + LLVMSetUnnamedAddress(global, unnamed); } pub(crate) fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) { @@ -260,9 +258,7 @@ pub(crate) fn set_initializer(llglobal: &Value, constant_val: &Value) { } pub(crate) fn set_global_constant(llglobal: &Value, is_constant: bool) { - unsafe { - LLVMSetGlobalConstant(llglobal, if is_constant { ffi::True } else { ffi::False }); - } + LLVMSetGlobalConstant(llglobal, if is_constant { ffi::True } else { ffi::False }); } pub(crate) fn get_linkage(llglobal: &Value) -> Linkage { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 3f38e1e191b..f9edaded60d 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -55,8 +55,8 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - base::set_link_section(lldecl, attrs); + let attrs = self.tcx.codegen_instance_attrs(instance.def); + base::set_link_section(lldecl, &attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() { @@ -131,8 +131,8 @@ impl CodegenCx<'_, '_> { } // Thread-local variables generally don't support copy relocations. - let is_thread_local_var = unsafe { llvm::LLVMIsAGlobalVariable(llval) } - .is_some_and(|v| unsafe { llvm::LLVMIsThreadLocal(v) } == llvm::True); + let is_thread_local_var = llvm::LLVMIsAGlobalVariable(llval) + .is_some_and(|v| llvm::LLVMIsThreadLocal(v) == llvm::True); if is_thread_local_var { return false; } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index ee472e75ed4..89365503138 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -2,7 +2,7 @@ use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::{fmt, ptr}; -use libc::{c_char, c_uint}; +use libc::c_uint; use rustc_abi::{AddressSpace, Align, Integer, Reg, Size}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -298,8 +298,8 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { - fn add_type_metadata(&self, function: &'ll Value, typeid: String) { - let typeid_metadata = self.typeid_metadata(typeid).unwrap(); + fn add_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { + let typeid_metadata = self.create_metadata(typeid); unsafe { let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMRustGlobalAddMetadata( @@ -310,8 +310,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - fn set_type_metadata(&self, function: &'ll Value, typeid: String) { - let typeid_metadata = self.typeid_metadata(typeid).unwrap(); + fn set_type_metadata(&self, function: &'ll Value, typeid: &[u8]) { + let typeid_metadata = self.create_metadata(typeid); unsafe { let v = [llvm::LLVMValueAsMetadata(self.const_usize(0)), typeid_metadata]; llvm::LLVMGlobalSetMetadata( @@ -322,10 +322,8 @@ impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - fn typeid_metadata(&self, typeid: String) -> Option<&'ll Metadata> { - Some(unsafe { - llvm::LLVMMDStringInContext2(self.llcx, typeid.as_ptr() as *const c_char, typeid.len()) - }) + fn typeid_metadata(&self, typeid: &[u8]) -> Option<&'ll Metadata> { + Some(self.create_metadata(typeid)) } fn add_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b46773396fc..5ce301c0eb9 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2542,12 +2542,7 @@ fn add_order_independent_options( // sections to ensure we have all the data for PGO. let keep_metadata = crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); - if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols - { - cmd.gc_sections(keep_metadata); - } else { - cmd.no_gc_sections(); - } + cmd.gc_sections(keep_metadata); } cmd.set_output_kind(link_output_kind, crate_type, out_filename); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e0a3ad55be0..050797354b4 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -326,7 +326,6 @@ pub(crate) trait Linker { link_or_cc_args(self, &[path]); } fn gc_sections(&mut self, keep_metadata: bool); - fn no_gc_sections(&mut self); fn full_relro(&mut self); fn partial_relro(&mut self); fn no_relro(&mut self); @@ -688,12 +687,6 @@ impl<'a> Linker for GccLinker<'a> { } } - fn no_gc_sections(&mut self) { - if self.is_gnu || self.sess.target.is_like_wasm { - self.link_arg("--no-gc-sections"); - } - } - fn optimize(&mut self) { if !self.is_gnu && !self.sess.target.is_like_wasm { return; @@ -1010,10 +1003,6 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn no_gc_sections(&mut self) { - self.link_arg("/OPT:NOREF,NOICF"); - } - fn full_relro(&mut self) { // noop } @@ -1243,10 +1232,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn no_gc_sections(&mut self) { - // noop - } - fn optimize(&mut self) { // Emscripten performs own optimizations self.cc_arg(match self.sess.opts.optimize { @@ -1418,10 +1403,6 @@ impl<'a> Linker for WasmLd<'a> { self.link_arg("--gc-sections"); } - fn no_gc_sections(&mut self) { - self.link_arg("--no-gc-sections"); - } - fn optimize(&mut self) { // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and // only differentiates -O0 and -O1. It does not apply to LTO. @@ -1567,10 +1548,6 @@ impl<'a> Linker for L4Bender<'a> { } } - fn no_gc_sections(&mut self) { - self.link_arg("--no-gc-sections"); - } - fn optimize(&mut self) { // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. @@ -1734,10 +1711,6 @@ impl<'a> Linker for AixLinker<'a> { self.link_arg("-bgc"); } - fn no_gc_sections(&mut self) { - self.link_arg("-bnogc"); - } - fn optimize(&mut self) {} fn pgo_gen(&mut self) { @@ -1982,8 +1955,6 @@ impl<'a> Linker for PtxLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} @@ -2057,8 +2028,6 @@ impl<'a> Linker for LlbcLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} @@ -2139,8 +2108,6 @@ impl<'a> Linker for BpfLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ce6fe8a191b..b49b6783bbd 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -1,13 +1,8 @@ use std::ffi::CString; use std::sync::Arc; -use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::memmap::Mmap; -use rustc_errors::FatalError; -use super::write::CodegenContext; -use crate::ModuleCodegen; -use crate::back::write::ModuleConfig; use crate::traits::*; pub struct ThinModule<B: WriteBackendMethods> { @@ -42,61 +37,6 @@ pub struct ThinShared<B: WriteBackendMethods> { pub module_names: Vec<CString>, } -pub enum LtoModuleCodegen<B: WriteBackendMethods> { - Fat(ModuleCodegen<B::Module>), - Thin(ThinModule<B>), -} - -impl<B: WriteBackendMethods> LtoModuleCodegen<B> { - pub fn name(&self) -> &str { - match *self { - LtoModuleCodegen::Fat(_) => "everything", - LtoModuleCodegen::Thin(ref m) => m.name(), - } - } - - /// Optimize this module within the given codegen context. - pub fn optimize( - self, - cgcx: &CodegenContext<B>, - ) -> Result<ModuleCodegen<B::Module>, FatalError> { - match self { - LtoModuleCodegen::Fat(mut module) => { - B::optimize_fat(cgcx, &mut module)?; - Ok(module) - } - LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), - } - } - - /// A "gauge" of how costly it is to optimize this module, used to sort - /// biggest modules first. - pub fn cost(&self) -> u64 { - match *self { - // Only one module with fat LTO, so the cost doesn't matter. - LtoModuleCodegen::Fat(_) => 0, - LtoModuleCodegen::Thin(ref m) => m.cost(), - } - } - - /// Run autodiff on Fat LTO module - pub fn autodiff( - self, - cgcx: &CodegenContext<B>, - diff_fncs: Vec<AutoDiffItem>, - config: &ModuleConfig, - ) -> Result<LtoModuleCodegen<B>, FatalError> { - match &self { - LtoModuleCodegen::Fat(module) => { - B::autodiff(cgcx, &module, diff_fncs, config)?; - } - _ => panic!("autodiff called with non-fat LTO module"), - } - - Ok(self) - } -} - pub enum SerializedModule<M: ModuleBufferMethods> { Local(M), FromRlib(Vec<u8>), diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index d2a64ec2993..50a7cba300b 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -397,50 +397,31 @@ impl<B: WriteBackendMethods> CodegenContext<B> { } } -fn generate_lto_work<B: ExtraBackendMethods>( +fn generate_thin_lto_work<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - autodiff: Vec<AutoDiffItem>, - needs_fat_lto: Vec<FatLtoInput<B>>, 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_generate_lto_work"); - - if !needs_fat_lto.is_empty() { - assert!(needs_thin_lto.is_empty()); - let mut module = - B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - if cgcx.lto == Lto::Fat && !autodiff.is_empty() { - let config = cgcx.config(ModuleKind::Regular); - module = module.autodiff(cgcx, autodiff, config).unwrap_or_else(|e| e.raise()); - } - // We are adding a single work item, so the cost doesn't matter. - vec![(WorkItem::LTO(module), 0)] - } else { - if !autodiff.is_empty() { - let dcx = cgcx.create_dcx(); - dcx.handle().emit_fatal(AutodiffWithoutLto {}); - } - assert!(needs_fat_lto.is_empty()); - let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules) - .unwrap_or_else(|e| e.raise()); - lto_modules - .into_iter() - .map(|module| { - let cost = module.cost(); - (WorkItem::LTO(module), cost) - }) - .chain(copy_jobs.into_iter().map(|wp| { - ( - WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { - name: wp.cgu_name.clone(), - source: wp, - }), - 0, // copying is very cheap - ) - })) - .collect() - } + 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()); + lto_modules + .into_iter() + .map(|module| { + let cost = module.cost(); + (WorkItem::ThinLto(module), cost) + }) + .chain(copy_jobs.into_iter().map(|wp| { + ( + WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { + name: wp.cgu_name.clone(), + source: wp, + }), + 0, // copying is very cheap + ) + })) + .collect() } struct CompiledModules { @@ -470,6 +451,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, + autodiff_items: &[AutoDiffItem], ) -> OngoingCodegen<B> { let (coordinator_send, coordinator_receive) = channel(); @@ -488,6 +470,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend.clone(), tcx, &crate_info, + autodiff_items, shared_emitter, codegen_worker_send, coordinator_receive, @@ -736,15 +719,23 @@ pub(crate) enum WorkItem<B: WriteBackendMethods> { /// Copy the post-LTO artifacts from the incremental cache to the output /// directory. CopyPostLtoArtifacts(CachedModuleCodegen), - /// Performs (Thin)LTO on the given module. - LTO(lto::LtoModuleCodegen<B>), + /// Performs fat LTO on the given module. + FatLto { + needs_fat_lto: Vec<FatLtoInput<B>>, + import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, + autodiff: Vec<AutoDiffItem>, + }, + /// Performs thin-LTO on the given module. + ThinLto(lto::ThinModule<B>), } impl<B: WriteBackendMethods> WorkItem<B> { fn module_kind(&self) -> ModuleKind { match *self { WorkItem::Optimize(ref m) => m.kind, - WorkItem::CopyPostLtoArtifacts(_) | WorkItem::LTO(_) => ModuleKind::Regular, + WorkItem::CopyPostLtoArtifacts(_) | WorkItem::FatLto { .. } | WorkItem::ThinLto(_) => { + ModuleKind::Regular + } } } @@ -792,7 +783,8 @@ impl<B: WriteBackendMethods> WorkItem<B> { match self { WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name), WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name), - WorkItem::LTO(m) => desc("lto", "LTO module", m.name()), + WorkItem::FatLto { .. } => desc("lto", "fat LTO module", "everything"), + WorkItem::ThinLto(m) => desc("lto", "thin-LTO module", m.name()), } } } @@ -996,12 +988,24 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( }) } -fn execute_lto_work_item<B: ExtraBackendMethods>( +fn execute_fat_lto_work_item<B: ExtraBackendMethods>( + cgcx: &CodegenContext<B>, + 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)?; + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) +} + +fn execute_thin_lto_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - module: lto::LtoModuleCodegen<B>, + module: lto::ThinModule<B>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { - let module = module.optimize(cgcx)?; + let module = B::optimize_thin(cgcx, module)?; finish_intra_module_work(cgcx, module, module_config) } @@ -1010,11 +1014,8 @@ fn finish_intra_module_work<B: ExtraBackendMethods>( module: ModuleCodegen<B::Module>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator { - let module = B::codegen(cgcx, dcx, module, module_config)?; + let module = B::codegen(cgcx, module, module_config)?; Ok(WorkItemResult::Finished(module)) } else { Ok(WorkItemResult::NeedsLink(module)) @@ -1031,9 +1032,6 @@ pub(crate) enum Message<B: WriteBackendMethods> { /// Sent from a backend worker thread. WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize }, - /// A vector containing all the AutoDiff tasks that we have to pass to Enzyme. - AddAutoDiffItems(Vec<AutoDiffItem>), - /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the /// backend. Sent from the main thread. @@ -1100,6 +1098,7 @@ fn start_executing_work<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, crate_info: &CrateInfo, + autodiff_items: &[AutoDiffItem], shared_emitter: SharedEmitter, codegen_worker_send: Sender<CguMessage>, coordinator_receive: Receiver<Box<dyn Any + Send>>, @@ -1109,6 +1108,7 @@ fn start_executing_work<B: ExtraBackendMethods>( ) -> 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(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { @@ -1362,7 +1362,6 @@ fn start_executing_work<B: ExtraBackendMethods>( // This is where we collect codegen units that have gone all the way // through codegen and LLVM. - let mut autodiff_items = Vec::new(); let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; let mut needs_link = Vec::new(); @@ -1474,20 +1473,37 @@ fn start_executing_work<B: ExtraBackendMethods>( let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); - for (work, cost) in generate_lto_work( - &cgcx, - autodiff_items.clone(), - needs_fat_lto, - needs_thin_lto, - import_only_modules, - ) { - let insertion_index = work_items - .binary_search_by_key(&cost, |&(_, cost)| cost) - .unwrap_or_else(|e| e); - work_items.insert(insertion_index, (work, cost)); + if !needs_fat_lto.is_empty() { + assert!(needs_thin_lto.is_empty()); + + work_items.push(( + WorkItem::FatLto { + needs_fat_lto, + import_only_modules, + autodiff: autodiff_items.clone(), + }, + 0, + )); if cgcx.parallel { helper.request_token(); } + } else { + if !autodiff_items.is_empty() { + let dcx = cgcx.create_dcx(); + dcx.handle().emit_fatal(AutodiffWithoutLto {}); + } + + for (work, cost) in + generate_thin_lto_work(&cgcx, needs_thin_lto, import_only_modules) + { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + if cgcx.parallel { + helper.request_token(); + } + } } } @@ -1616,10 +1632,6 @@ fn start_executing_work<B: ExtraBackendMethods>( main_thread_state = MainThreadState::Idle; } - Message::AddAutoDiffItems(mut items) => { - autodiff_items.append(&mut items); - } - Message::CodegenComplete => { if codegen_state != Aborted { codegen_state = Completed; @@ -1702,7 +1714,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let dcx = dcx.handle(); let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; let module = - B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; + B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; compiled_modules.push(module); } @@ -1842,10 +1854,22 @@ fn spawn_work<'a, B: ExtraBackendMethods>( ); Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config)) } - WorkItem::LTO(m) => { + WorkItem::FatLto { 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, + needs_fat_lto, + import_only_modules, + autodiff, + module_config, + ) + } + WorkItem::ThinLto(m) => { let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()); - execute_lto_work_item(&cgcx, m, module_config) + execute_thin_lto_work_item(&cgcx, m, module_config) } }) }; @@ -2082,10 +2106,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>))); } - pub(crate) fn submit_autodiff_items(&self, items: Vec<AutoDiffItem>) { - drop(self.coordinator.sender.send(Box::new(Message::<B>::AddAutoDiffItems(items)))); - } - pub(crate) fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 18581f854b6..833456abb8a 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -647,7 +647,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ) -> OngoingCodegen<B> { // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu); + let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, &[]); ongoing_codegen.codegen_finished(tcx); @@ -667,7 +667,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // codegen units. let MonoItemPartitions { codegen_units, autodiff_items, .. } = tcx.collect_and_partition_mono_items(()); - let autodiff_fncs = autodiff_items.to_vec(); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -680,7 +679,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } } - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu); + let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items); // Codegen an allocator shim, if necessary. if let Some(kind) = allocator_kind_for_codegen(tcx) { @@ -710,10 +709,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ); } - if !autodiff_fncs.is_empty() { - ongoing_codegen.submit_autodiff_items(autodiff_fncs); - } - // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 2aa5c3c27ea..34ad35a729b 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -139,7 +139,8 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( && bx.cx().sess().lto() == Lto::Fat { if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) { - let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap(); + let typeid = + bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref).as_bytes()).unwrap(); let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); return func; } else if nonnull { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 025f5fb54f4..b8f635ab781 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -356,7 +356,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Operand(operand) => { // Don't spill operands onto the stack in naked functions. // See: https://github.com/rust-lang/rust/issues/42779 - let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id()); + let attrs = bx.tcx().codegen_instance_attrs(self.instance.def); if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index fa69820d5d2..50d0f910744 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -390,9 +390,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut num_untupled = None; - let codegen_fn_attrs = bx.tcx().codegen_fn_attrs(fx.instance.def_id()); - let naked = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED); - if naked { + let codegen_fn_attrs = bx.tcx().codegen_instance_attrs(fx.instance.def); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return vec![]; } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index beaf8950978..42e435cf0a3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -128,7 +128,7 @@ fn prefix_and_suffix<'tcx>( let is_arm = tcx.sess.target.arch == "arm"; let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); - let attrs = tcx.codegen_fn_attrs(instance.def_id()); + let attrs = tcx.codegen_instance_attrs(instance.def); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); // If no alignment is specified, an alignment of 4 bytes is used. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e90463aacc8..2587e89417a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -611,11 +611,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { - Some(bx.tcx().codegen_fn_attrs(instance.def_id())) + Some(bx.tcx().codegen_instance_attrs(instance.def)) } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance)) + bx.call( + fn_ty, + fn_attrs.as_deref(), + Some(fn_abi), + fn_ptr, + &[], + None, + Some(instance), + ) } else { bx.get_static(def_id) }; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 7b4268abe4b..b9040c330fb 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -41,12 +41,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { base::codegen_global_asm(cx, item_id); } MonoItem::Fn(instance) => { - if cx - .tcx() - .codegen_fn_attrs(instance.def_id()) - .flags - .contains(CodegenFnAttrFlags::NAKED) - { + let flags = cx.tcx().codegen_instance_attrs(instance.def).flags; + if flags.contains(CodegenFnAttrFlags::NAKED) { naked_asm::codegen_naked_asm::<Bx::CodegenCx>(cx, instance, item_data); } else { base::codegen_instance::<Bx>(cx, instance); @@ -75,7 +71,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.predefine_static(def_id, linkage, visibility, symbol_name); } MonoItem::Fn(instance) => { - let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); + let attrs = cx.tcx().codegen_instance_attrs(instance.def); if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { // do not define this function; it will become a global assembly block diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index dcd9e25b2c9..32c24965e1b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -154,9 +154,9 @@ pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes { // For backends that support CFI using type membership (i.e., testing whether a given pointer is // associated with a type identifier). pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes { - fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {} - fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {} - fn typeid_metadata(&self, _typeid: String) -> Option<Self::Metadata> { + fn add_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} + fn set_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} + fn typeid_metadata(&self, _typeid: &[u8]) -> Option<Self::Metadata> { None } fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 07a0609fda1..5e993640472 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; -use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use crate::back::lto::{SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; @@ -20,13 +20,14 @@ pub trait WriteBackendMethods: Clone + 'static { dcx: DiagCtxtHandle<'_>, modules: Vec<ModuleCodegen<Self::Module>>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; - /// Performs fat LTO by merging all modules into a single one and returning it - /// for further optimization. - fn run_fat_lto( + /// 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>, modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<LtoModuleCodegen<Self>, FatalError>; + diff_fncs: Vec<AutoDiffItem>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two /// lists, one of the modules that need optimization and another for modules that /// can simply be copied over from the incr. comp. cache. @@ -34,7 +35,7 @@ pub trait WriteBackendMethods: Clone + 'static { cgcx: &CodegenContext<Self>, modules: Vec<(String, Self::ThinBuffer)>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, - ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>; + ) -> Result<(Vec<ThinModule<Self>>, Vec<WorkProduct>), FatalError>; fn print_pass_timings(&self); fn print_statistics(&self); fn optimize( @@ -43,17 +44,12 @@ pub trait WriteBackendMethods: Clone + 'static { module: &mut ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<(), FatalError>; - fn optimize_fat( - cgcx: &CodegenContext<Self>, - llmod: &mut ModuleCodegen<Self::Module>, - ) -> Result<(), FatalError>; fn optimize_thin( cgcx: &CodegenContext<Self>, thin: ThinModule<Self>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; fn codegen( cgcx: &CodegenContext<Self>, - dcx: DiagCtxtHandle<'_>, module: ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<CompiledModule, FatalError>; @@ -62,12 +58,6 @@ pub trait WriteBackendMethods: Clone + 'static { want_summary: bool, ) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer); - fn autodiff( - cgcx: &CodegenContext<Self>, - module: &ModuleCodegen<Self::Module>, - diff_fncs: Vec<AutoDiffItem>, - config: &ModuleConfig, - ) -> Result<(), FatalError>; } pub trait ThinBufferMethods: Send + Sync { diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index b767ca9a3c2..aa0bc42d448 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -56,6 +56,17 @@ const_eval_const_context = {$kind -> *[other] {""} } +const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global + .note = use `const_make_global` to make allocated pointers immutable before returning + +const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} + +const_eval_const_make_global_ptr_is_non_heap = pointer passed to `const_make_global` does not point to a heap allocation: {$ptr} + +const_eval_const_make_global_with_dangling_ptr = pointer passed to `const_make_global` is dangling: {$ptr} + +const_eval_const_make_global_with_offset = making {$ptr} global which does not point to the beginning of an object + const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges @@ -296,19 +307,22 @@ const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` const_eval_pointer_out_of_bounds = - {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$inbounds_size_is_neg -> - [false] {$alloc_size_minus_ptr_offset -> - [0] is at or beyond the end of the allocation of size {$alloc_size -> - [1] 1 byte - *[x] {$alloc_size} bytes + {const_eval_bad_pointer_op_attempting}, but got {$pointer} which {$ptr_offset_is_neg -> + [true] points to before the beginning of the allocation + *[false] {$inbounds_size_is_neg -> + [false] {$alloc_size_minus_ptr_offset -> + [0] is at or beyond the end of the allocation of size {$alloc_size -> + [1] 1 byte + *[x] {$alloc_size} bytes + } + [1] is only 1 byte from the end of the allocation + *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation + } + *[true] {$ptr_offset_abs -> + [0] is at the beginning of the allocation + *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation } - [1] is only 1 byte from the end of the allocation - *[x] is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation - } - *[true] {$ptr_offset_abs -> - [0] is at the beginning of the allocation - *[other] is only {$ptr_offset_abs} bytes from the beginning of the allocation - } + } } const_eval_pointer_use_after_free = diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 9ab8e0692e1..ebf18c6f2ac 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -93,7 +93,7 @@ pub fn rustc_allow_const_fn_unstable( /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is const-stable when the trait is const-stable. + // A default body in a `const trait` is const-stable when the trait is const-stable. if tcx.is_const_default_method(def_id) { return is_fn_or_trait_safe_to_expose_on_stable(tcx, tcx.parent(def_id)); } diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index b6e2682af36..438aed41b8b 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -49,7 +49,6 @@ impl HasStaticRootDefId for DummyMachine { impl<'tcx> interpret::Machine<'tcx> for DummyMachine { interpret::compile_time_machine!(<'tcx>); - type MemoryKind = !; const PANIC_ON_ALLOC_FAIL: bool = true; // We want to just eval random consts in the program, so `eval_mir_const` can fail. diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 08fc03d9c46..3e880d02001 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,7 +2,7 @@ use std::mem; use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg}; use rustc_middle::mir::AssertKind; -use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo}; +use rustc_middle::mir::interpret::{AllocId, Provenance, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{ConstInt, TyCtxt}; @@ -11,8 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, - err_machine_stop, + CtfeProvenance, ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, + Pointer, err_inval, err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -22,8 +22,22 @@ pub enum ConstEvalErrKind { ModifiedGlobal, RecursiveStatic, AssertFailure(AssertKind<ConstInt>), - Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, + Panic { + msg: Symbol, + line: u32, + col: u32, + file: Symbol, + }, WriteThroughImmutablePointer, + /// Called `const_make_global` twice. + ConstMakeGlobalPtrAlreadyMadeGlobal(AllocId), + /// Called `const_make_global` on a non-heap pointer. + ConstMakeGlobalPtrIsNonHeap(Pointer<Option<CtfeProvenance>>), + /// Called `const_make_global` on a dangling pointer. + ConstMakeGlobalWithDanglingPtr(Pointer<Option<CtfeProvenance>>), + /// Called `const_make_global` on a pointer that does not start at the + /// beginning of an object. + ConstMakeGlobalWithOffset(Pointer<Option<CtfeProvenance>>), } impl MachineStopType for ConstEvalErrKind { @@ -38,6 +52,12 @@ impl MachineStopType for ConstEvalErrKind { RecursiveStatic => const_eval_recursive_static, AssertFailure(x) => x.diagnostic_message(), WriteThroughImmutablePointer => const_eval_write_through_immutable_pointer, + ConstMakeGlobalPtrAlreadyMadeGlobal { .. } => { + const_eval_const_make_global_ptr_already_made_global + } + ConstMakeGlobalPtrIsNonHeap(_) => const_eval_const_make_global_ptr_is_non_heap, + ConstMakeGlobalWithDanglingPtr(_) => const_eval_const_make_global_with_dangling_ptr, + ConstMakeGlobalWithOffset(_) => const_eval_const_make_global_with_offset, } } fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)) { @@ -51,6 +71,14 @@ impl MachineStopType for ConstEvalErrKind { Panic { msg, .. } => { adder("msg".into(), msg.into_diag_arg(&mut None)); } + ConstMakeGlobalPtrIsNonHeap(ptr) + | ConstMakeGlobalWithOffset(ptr) + | ConstMakeGlobalWithDanglingPtr(ptr) => { + adder("ptr".into(), format!("{ptr:?}").into_diag_arg(&mut None)); + } + ConstMakeGlobalPtrAlreadyMadeGlobal(alloc) => { + adder("alloc".into(), alloc.into_diag_arg(&mut None)); + } } } } 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 4bd4b493009..ce72d59b8b0 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, + CtfeValidationMode, GlobalId, Immediate, InternError, InternKind, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, ReturnContinuation, create_static_alloc, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -93,25 +93,30 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( // Since evaluation had no errors, validate the resulting constant. const_validate_mplace(ecx, &ret, cid)?; - // Only report this after validation, as validaiton produces much better diagnostics. + // Only report this after validation, as validation produces much better diagnostics. // FIXME: ensure validation always reports this and stop making interning care about it. match intern_result { Ok(()) => {} - Err(InternResult::FoundDanglingPointer) => { + Err(InternError::DanglingPointer) => { throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( ecx.tcx .dcx() .emit_err(errors::DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), ))); } - Err(InternResult::FoundBadMutablePointer) => { + Err(InternError::BadMutablePointer) => { throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( ecx.tcx .dcx() .emit_err(errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }), ))); } + Err(InternError::ConstAllocNotGlobal) => { + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }), + ))); + } } interp_ok(R::make_result(ret, ecx)) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 52fc898192a..f24fb18f83b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -169,13 +169,19 @@ pub type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryKind { - Heap, + Heap { + /// Indicates whether `make_global` was called on this allocation. + /// If this is `true`, the allocation must be immutable. + was_made_global: bool, + }, } impl fmt::Display for MemoryKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - MemoryKind::Heap => write!(f, "heap allocation"), + MemoryKind::Heap { was_made_global } => { + write!(f, "heap allocation{}", if *was_made_global { " (made global)" } else { "" }) + } } } } @@ -184,7 +190,7 @@ impl interpret::MayLeak for MemoryKind { #[inline(always)] fn may_leak(self) -> bool { match self { - MemoryKind::Heap => false, + MemoryKind::Heap { was_made_global } => was_made_global, } } } @@ -314,8 +320,6 @@ impl<'tcx> CompileTimeMachine<'tcx> { impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { compile_time_machine!(<'tcx>); - type MemoryKind = MemoryKind; - const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error #[inline(always)] @@ -359,8 +363,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { if let ty::InstanceKind::Item(def) = instance.def { // Execution might have wandered off into other crates, so we cannot do a stability- // sensitive check here. But we can at least rule out functions that are not const at - // all. That said, we have to allow calling functions inside a trait marked with - // #[const_trait]. These *are* const-checked! + // all. That said, we have to allow calling functions inside a `const trait`. These + // *are* const-checked! if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { // We certainly do *not* want to actually call the fn // though, so be sure we return here. @@ -420,7 +424,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let ptr = ecx.allocate_ptr( Size::from_bytes(size), align, - interpret::MemoryKind::Machine(MemoryKind::Heap), + interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }), AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; @@ -453,10 +457,17 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.deallocate_ptr( ptr, Some((size, align)), - interpret::MemoryKind::Machine(MemoryKind::Heap), + interpret::MemoryKind::Machine(MemoryKind::Heap { was_made_global: false }), )?; } } + + sym::const_make_global => { + let ptr = ecx.read_pointer(&args[0])?; + ecx.make_const_heap_ptr_global(ptr)?; + ecx.write_pointer(ptr, dest)?; + } + // The intrinsic represents whether the value is known to the optimizer (LLVM). // We're not doing any optimizations here, so there is no optimizer that could know the value. // (We know the value here in the machine of course, but this is the runtime of that code, diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 49cd7138748..b6a64035261 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -44,6 +44,14 @@ pub(crate) struct MutablePtrInFinal { } #[derive(Diagnostic)] +#[diag(const_eval_const_heap_ptr_in_final)] +#[note] +pub(crate) struct ConstHeapPtrInFinal { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(const_eval_unstable_in_stable_exposed)] pub(crate) struct UnstableInStableExposed { pub gate: String, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index ad3e02580f3..1503f3bcd99 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; use rustc_hir::def_id::DefId; -use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, TyAndLayout}; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::sym; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 41fc8d47cd3..11e7706fe60 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ - self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers, - TyAndLayout, + self, FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, + LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance}; use rustc_middle::{mir, span_bug}; @@ -92,20 +92,6 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } } -impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { - /// This inherent method takes priority over the trait method with the same name in LayoutOf, - /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span. - /// See [LayoutOf::layout_of] for the original documentation. - #[inline(always)] - pub fn layout_of( - &self, - ty: Ty<'tcx>, - ) -> <InterpCx<'tcx, M> as LayoutOfHelpers<'tcx>>::LayoutOfResult { - let _span = enter_trace_span!(M, "InterpCx::layout_of", "ty = {:?}", ty.kind()); - LayoutOf::layout_of(self, ty) - } -} - impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; @@ -121,6 +107,43 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { } } +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// This inherent method takes priority over the trait method with the same name in LayoutOf, + /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span. + /// See [LayoutOf::layout_of] for the original documentation. + #[inline(always)] + pub fn layout_of(&self, ty: Ty<'tcx>) -> <Self as LayoutOfHelpers<'tcx>>::LayoutOfResult { + let _span = enter_trace_span!(M, "InterpCx::layout_of", ty = ?ty.kind()); + LayoutOf::layout_of(self, ty) + } + + /// This inherent method takes priority over the trait method with the same name in FnAbiOf, + /// and allows wrapping the actual [FnAbiOf::fn_abi_of_fn_ptr] with a tracing span. + /// See [FnAbiOf::fn_abi_of_fn_ptr] for the original documentation. + #[inline(always)] + pub fn fn_abi_of_fn_ptr( + &self, + sig: ty::PolyFnSig<'tcx>, + extra_args: &'tcx ty::List<Ty<'tcx>>, + ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult { + let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_fn_ptr", ?sig, ?extra_args); + FnAbiOf::fn_abi_of_fn_ptr(self, sig, extra_args) + } + + /// This inherent method takes priority over the trait method with the same name in FnAbiOf, + /// and allows wrapping the actual [FnAbiOf::fn_abi_of_instance] with a tracing span. + /// See [FnAbiOf::fn_abi_of_instance] for the original documentation. + #[inline(always)] + pub fn fn_abi_of_instance( + &self, + instance: ty::Instance<'tcx>, + extra_args: &'tcx ty::List<Ty<'tcx>>, + ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult { + let _span = enter_trace_span!(M, "InterpCx::fn_abi_of_instance", ?instance, ?extra_args); + FnAbiOf::fn_abi_of_instance(self, instance, extra_args) + } +} + /// Test if it is valid for a MIR assignment to assign `src`-typed place to `dest`-typed value. /// This test should be symmetric, as it is primarily about layout compatibility. pub(super) fn mir_assign_valid_types<'tcx>( diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index f0f958d069e..bb59b9f5418 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -26,21 +26,18 @@ use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use tracing::{instrument, trace}; -use super::{ - AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, err_ub, interp_ok, -}; -use crate::const_eval; +use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, interp_ok}; use crate::const_eval::DummyMachine; -use crate::errors::NestedStaticInThreadLocal; +use crate::{const_eval, errors}; -pub trait CompileTimeMachine<'tcx, T> = Machine< +pub trait CompileTimeMachine<'tcx> = Machine< 'tcx, - MemoryKind = T, + MemoryKind = const_eval::MemoryKind, Provenance = CtfeProvenance, ExtraFnVal = !, FrameExtra = (), AllocExtra = (), - MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>, + MemoryMap = FxIndexMap<AllocId, (MemoryKind<const_eval::MemoryKind>, Allocation)>, > + HasStaticRootDefId; pub trait HasStaticRootDefId { @@ -62,18 +59,32 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( +fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, disambiguator: Option<&mut DisambiguatorState>, -) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> { +) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, InternError> { trace!("intern_shallow {:?}", alloc_id); // remove allocation // FIXME(#120456) - is `swap_remove` correct? - let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { - return Err(()); + let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { + return Err(InternError::DanglingPointer); }; + + match kind { + MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { + if !was_made_global { + // Attempting to intern a `const_allocate`d pointer that was not made global via + // `const_make_global`. We want to error here, but we have to first put the + // allocation back into the `alloc_map` to keep things in a consistent state. + ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + return Err(InternError::ConstAllocNotGlobal); + } + } + MemoryKind::Stack | MemoryKind::CallerLocation => {} + } + // Set allocation mutability as appropriate. This is used by LLVM to put things into // read-only memory, and also by Miri when evaluating other globals that // access this one. @@ -99,7 +110,7 @@ fn intern_shallow<'tcx, T, M: CompileTimeMachine<'tcx, T>>( } else { ecx.tcx.set_alloc_id_memory(alloc_id, alloc); } - Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov)) + Ok(alloc.inner().provenance().ptrs().iter().map(|&(_, prov)| prov)) } /// Creates a new `DefId` and feeds all the right queries to make this `DefId` @@ -125,7 +136,7 @@ fn intern_as_new_static<'tcx>( tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); if tcx.is_thread_local_static(static_id.into()) { - tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); + tcx.dcx().emit_err(errors::NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); } // These do not inherit the codegen attrs of the parent static allocation, since @@ -151,9 +162,10 @@ pub enum InternKind { } #[derive(Debug)] -pub enum InternResult { - FoundBadMutablePointer, - FoundDanglingPointer, +pub enum InternError { + BadMutablePointer, + DanglingPointer, + ConstAllocNotGlobal, } /// Intern `ret` and everything it references. @@ -163,11 +175,11 @@ pub enum InternResult { /// /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval::MemoryKind>>( +pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, intern_kind: InternKind, ret: &MPlaceTy<'tcx>, -) -> Result<(), InternResult> { +) -> Result<(), InternError> { let mut disambiguator = DisambiguatorState::new(); // We are interning recursively, and for mutability we are distinguishing the "root" allocation @@ -181,7 +193,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval } InternKind::Static(Mutability::Not) => { ( - // Outermost allocation is mutable if `!Freeze`. + // Outermost allocation is mutable if `!Freeze` i.e. contains interior mutable types. if ret.layout.ty.is_freeze(*ecx.tcx, ecx.typing_env) { Mutability::Not } else { @@ -224,6 +236,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. let mut result = Ok(()); + let mut found_bad_mutable_ptr = false; // Keep interning as long as there are things to intern. // We show errors if there are dangling pointers, or mutable pointers in immutable contexts @@ -278,18 +291,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval // when there is memory there that someone might expect to be mutable, but we make it immutable. let dangling = !is_already_global && !ecx.memory.alloc_map.contains_key(&alloc_id); if !dangling { - // Found a mutable pointer inside a const where inner allocations should be - // immutable. - if !ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { - span_bug!( - ecx.tcx.span, - "the static const safety checks accepted a mutable pointer they should not have accepted" - ); - } - // Prefer dangling pointer errors over mutable pointer errors - if result.is_ok() { - result = Err(InternResult::FoundBadMutablePointer); - } + found_bad_mutable_ptr = true; } } if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { @@ -310,18 +312,31 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval just_interned.insert(alloc_id); match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { Ok(nested) => todo.extend(nested), - Err(()) => { - ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning"); - result = Err(InternResult::FoundDanglingPointer); + Err(err) => { + ecx.tcx.dcx().delayed_bug("error during const interning"); + result = Err(err); } } } + if found_bad_mutable_ptr && result.is_ok() { + // We found a mutable pointer inside a const where inner allocations should be immutable, + // and there was no other error. This should usually never happen! However, this can happen + // in unleash-miri mode, so report it as a normal error then. + if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { + result = Err(InternError::BadMutablePointer); + } else { + span_bug!( + ecx.tcx.span, + "the static const safety checks accepted a mutable pointer they should not have accepted" + ); + } + } result } /// Intern `ret`. This function assumes that `ret` references no other allocation. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>( +pub fn intern_const_alloc_for_constprop<'tcx, M: CompileTimeMachine<'tcx>>( ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, ) -> InterpResult<'tcx, ()> { @@ -330,10 +345,7 @@ pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>> return interp_ok(()); } // Move allocation to `tcx`. - if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None) - .map_err(|()| err_ub!(DeadLocal))? - .next() - { + if let Some(_) = intern_shallow(ecx, alloc_id, Mutability::Not, None).unwrap().next() { // We are not doing recursive interning, so we don't currently support provenance. // (If this assertion ever triggers, we should just implement a // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 22d29eda913..e24a355891d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -4,8 +4,9 @@ use std::assert_matches::assert_matches; -use rustc_abi::{FieldIdx, Size}; +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::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -30,7 +31,7 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Generates a value of `TypeId` for `ty` in-place. - pub(crate) fn write_type_id( + fn write_type_id( &mut self, ty: Ty<'tcx>, dest: &PlaceTy<'tcx, M::Provenance>, @@ -48,8 +49,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Here we rely on `TypeId` being a newtype around an array of pointers, so we // first project to its only field and then the array elements. let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); - let first = self.project_field(dest, FieldIdx::ZERO)?; - let mut elem_iter = self.project_array_fields(&first)?; + let arr = self.project_field(dest, FieldIdx::ZERO)?; + let mut elem_iter = self.project_array_fields(&arr)?; while let Some((_, elem)) = elem_iter.next(self)? { // Decorate this part of the hash with provenance; leave the integer part unchanged. let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?; @@ -61,6 +62,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + /// Read a value of type `TypeId`, returning the type it represents. + pub(crate) fn read_type_id( + &self, + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Ty<'tcx>> { + // `TypeId` is a newtype around an array of pointers. All pointers must have the same + // provenance, and that provenance represents the type. + let ptr_size = self.pointer_size().bytes_usize(); + let arr = self.project_field(op, FieldIdx::ZERO)?; + + let mut ty_and_hash = None; + let mut elem_iter = self.project_array_fields(&arr)?; + while let Some((idx, elem)) = elem_iter.next(self)? { + let elem = self.read_pointer(&elem)?; + let (elem_ty, elem_hash) = self.get_ptr_type_id(elem)?; + // If this is the first element, remember the type and its hash. + // If this is not the first element, ensure it is consistent with the previous ones. + let full_hash = match ty_and_hash { + None => { + let hash = self.tcx.type_id_hash(elem_ty).as_u128(); + let mut hash_bytes = [0u8; 16]; + write_target_uint(self.data_layout().endian, &mut hash_bytes, hash).unwrap(); + ty_and_hash = Some((elem_ty, hash_bytes)); + hash_bytes + } + Some((ty, hash_bytes)) => { + if ty != elem_ty { + throw_ub_format!( + "invalid `TypeId` value: not all bytes carry the same type id metadata" + ); + } + hash_bytes + } + }; + // Ensure the elem_hash matches the corresponding part of the full hash. + let hash_frag = &full_hash[(idx as usize) * ptr_size..][..ptr_size]; + if read_target_uint(self.data_layout().endian, hash_frag).unwrap() != elem_hash.into() { + throw_ub_format!( + "invalid `TypeId` value: the hash does not match the type id metadata" + ); + } + } + + interp_ok(ty_and_hash.unwrap().0) + } + /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own /// intrinsic handling. @@ -97,47 +144,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_type_id(tp_ty, dest)?; } sym::type_id_eq => { - // Both operands are `TypeId`, which is a newtype around an array of pointers. - // Project until we have the array elements. - let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?; - let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?; - - let mut a_fields = self.project_array_fields(&a_fields)?; - let mut b_fields = self.project_array_fields(&b_fields)?; - - let mut provenance_a = None; - let mut provenance_b = None; - let mut provenance_matches = true; - - while let Some((i, a)) = a_fields.next(self)? { - let (_, b) = b_fields.next(self)?.unwrap(); - - let a = self.deref_pointer(&a)?; - let (a, offset_a) = self.get_ptr_type_id(a.ptr())?; - - let b = self.deref_pointer(&b)?; - let (b, offset_b) = self.get_ptr_type_id(b.ptr())?; - - if *provenance_a.get_or_insert(a) != a { - throw_ub_format!( - "type_id_eq: the first TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's" - ) - } - if *provenance_b.get_or_insert(b) != b { - throw_ub_format!( - "type_id_eq: the second TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's" - ) - } - provenance_matches &= a == b; - - if offset_a != offset_b && provenance_matches { - throw_ub_format!( - "type_id_eq: one of the TypeId arguments is invalid, chunk {i} of the hash does not match the type it represents" - ) - } - } - - self.write_scalar(Scalar::from_bool(provenance_matches), dest)?; + let a_ty = self.read_type_id(&args[0])?; + let b_ty = self.read_type_id(&args[1])?; + self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?; } sym::variant_count => { let tp_ty = instance.args.type_at(0); diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index d150ed69250..e981f3973ae 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -649,6 +649,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { type ExtraFnVal = !; + type MemoryKind = $crate::const_eval::MemoryKind; type MemoryMap = rustc_data_structures::fx::FxIndexMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>; const GLOBAL_KIND: Option<Self::MemoryKind> = None; // no copying of globals from `tcx` to machine memory diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6414821e21d..47228de5213 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -26,6 +26,7 @@ use super::{ Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; +use crate::const_eval::ConstEvalErrKind; use crate::fluent_generated as fluent; #[derive(Debug, PartialEq, Copy, Clone)] @@ -311,6 +312,51 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(new_ptr) } + /// Mark the `const_allocate`d allocation `ptr` points to as immutable so we can intern it. + pub fn make_const_heap_ptr_global( + &mut self, + ptr: Pointer<Option<CtfeProvenance>>, + ) -> InterpResult<'tcx> + where + M: Machine<'tcx, MemoryKind = crate::const_eval::MemoryKind, Provenance = CtfeProvenance>, + { + let (alloc_id, offset, _) = self.ptr_get_alloc_id(ptr, 0)?; + if offset.bytes() != 0 { + return Err(ConstEvalErrKind::ConstMakeGlobalWithOffset(ptr)).into(); + } + + if matches!(self.tcx.try_get_global_alloc(alloc_id), Some(_)) { + // This points to something outside the current interpreter. + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); + } + + // If we can't find it in `alloc_map` it must be dangling (because we don't use + // `extra_fn_ptr_map` in const-eval). + let (kind, alloc) = self + .memory + .alloc_map + .get_mut_or(alloc_id, || Err(ConstEvalErrKind::ConstMakeGlobalWithDanglingPtr(ptr)))?; + + // Ensure this is actually a *heap* allocation, and record it as made-global. + match kind { + MemoryKind::Stack | MemoryKind::CallerLocation => { + return Err(ConstEvalErrKind::ConstMakeGlobalPtrIsNonHeap(ptr)).into(); + } + MemoryKind::Machine(crate::const_eval::MemoryKind::Heap { was_made_global }) => { + if *was_made_global { + return Err(ConstEvalErrKind::ConstMakeGlobalPtrAlreadyMadeGlobal(alloc_id)) + .into(); + } + *was_made_global = true; + } + } + + // Prevent further mutation, this is now an immutable global. + alloc.mutability = Mutability::Not; + + interp_ok(()) + } + #[instrument(skip(self), level = "debug")] pub fn deallocate_ptr( &mut self, @@ -890,7 +936,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(fn_val) = self.get_fn_alloc(id) { let align = match fn_val { FnVal::Instance(instance) => { - self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE) + self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE) } // Machine-specific extra functions currently do not support alignment restrictions. FnVal::Other(_) => Align::ONE, @@ -951,12 +997,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn get_ptr_type_id( &self, ptr: Pointer<Option<M::Provenance>>, - ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> { + ) -> 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 { - throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id") + throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata") }; - interp_ok((ty, offset)) + interp_ok((ty, offset.bytes())) } pub fn get_ptr_fn( diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2fc372dd019..2f365ec77b3 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -26,7 +26,7 @@ pub use self::call::FnArg; pub use self::eval_context::{InterpCx, format_interp_error}; use self::eval_context::{from_known_layout, mir_assign_valid_types}; pub use self::intern::{ - HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, + HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop, intern_const_alloc_recursive, }; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index b6ba069526c..2e99bb4209f 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -12,6 +12,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_mir_dataflow::impls::always_storage_live_locals; use rustc_span::Span; +use tracing::field::Empty; use tracing::{info_span, instrument, trace}; use super::{ @@ -396,7 +397,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Finish things up. M::after_stack_push(self)?; self.frame_mut().loc = Left(mir::Location::START); - let span = info_span!("frame", "{}", instance); + // `tracing_separate_thread` is used to instruct the chrome_tracing [tracing::Layer] in Miri + // to put the "frame" span on a separate trace thread/line than other spans, to make the + // visualization in https://ui.perfetto.dev easier to interpret. It is set to a value of + // [tracing::field::Empty] so that other tracing layers (e.g. the logger) will ignore it. + let span = info_span!("frame", tracing_separate_thread = Empty, "{}", instance); self.frame_mut().tracing_span.enter(span); interp_ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 833fcc38817..629dcc3523c 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -5,7 +5,6 @@ use either::Either; use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_index::IndexSlice; -use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc44490c96d..62f591ceaa9 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -558,7 +558,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { { // Everything should be already interned. let Some(global_alloc) = self.ecx.tcx.try_get_global_alloc(alloc_id) else { - assert!(self.ecx.memory.alloc_map.get(alloc_id).is_none()); + if self.ecx.memory.alloc_map.contains_key(&alloc_id) { + // This can happen when interning didn't complete due to, e.g. + // missing `make_global`. This must mean other errors are already + // being reported. + self.ecx.tcx.dcx().delayed_bug( + "interning did not complete, there should be an error", + ); + return interp_ok(()); + } // We can't have *any* references to non-existing allocations in const-eval // as the rest of rustc isn't happy with them... so we throw an error, even // though for zero-sized references this isn't really UB. diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 4ca39bbc68e..b1f29598750 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -52,9 +52,9 @@ fn check_validity_requirement_strict<'tcx>( let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); - let allocated = cx - .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) - .expect("OOM: failed to allocate for uninit check"); + // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong. + let allocated = + cx.allocate(ty, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check"); if kind == ValidityRequirement::Zero { cx.write_bytes_ptr( @@ -184,9 +184,10 @@ pub(crate) fn validate_scalar_in_layout<'tcx>( let Ok(layout) = cx.layout_of(ty) else { bug!("could not compute layout of {scalar:?}:{ty:?}") }; - let allocated = cx - .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) - .expect("OOM: failed to allocate for uninit check"); + + // It doesn't really matter which `MemoryKind` we use here, `Stack` is the least wrong. + let allocated = + cx.allocate(layout, MemoryKind::Stack).expect("OOM: failed to allocate for uninit check"); cx.write_scalar(scalar, &allocated).unwrap(); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 18490385455..f3ed6042105 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1237,9 +1237,55 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto return None; } + warn_on_confusing_output_filename_flag(early_dcx, &matches, args); + Some(matches) } +/// Warn if `-o` is used without a space between the flag name and the value +/// and the value is a high-value confusables, +/// e.g. `-optimize` instead of `-o optimize`, see issue #142812. +fn warn_on_confusing_output_filename_flag( + early_dcx: &EarlyDiagCtxt, + matches: &getopts::Matches, + args: &[String], +) { + fn eq_ignore_separators(s1: &str, s2: &str) -> bool { + let s1 = s1.replace('-', "_"); + let s2 = s2.replace('-', "_"); + s1 == s2 + } + + if let Some(name) = matches.opt_str("o") + && let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o") + { + let filename = suspect.strip_prefix("-").unwrap_or(suspect); + let optgroups = config::rustc_optgroups(); + let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"]; + + // Check if provided filename might be confusing in conjunction with `-o` flag, + // i.e. consider `-o{filename}` such as `-optimize` with `filename` being `ptimize`. + // There are high-value confusables, for example: + // - Long name of flags, e.g. `--out-dir` vs `-out-dir` + // - C compiler flag, e.g. `optimize`, `o0`, `o1`, `o2`, `o3`, `ofast`. + // - Codegen flags, e.g. `pt-level` of `-opt-level`. + if optgroups.iter().any(|option| eq_ignore_separators(option.long_name(), filename)) + || config::CG_OPTIONS.iter().any(|option| eq_ignore_separators(option.name(), filename)) + || fake_args.iter().any(|arg| eq_ignore_separators(arg, filename)) + { + early_dcx.early_warn( + "option `-o` has no space between flag name and value, which can be confusing", + ); + early_dcx.early_note(format!( + "output filename `-o {name}` is applied instead of a flag named `o{name}`" + )); + early_dcx.early_help(format!( + "insert a space between `-o` and `{name}` if this is intentional: `-o {name}`" + )); + } + } +} + fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { Input::File(file) => new_parser_from_file(&sess.psess, file, None), diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ce3006c2604..1928cfd9048 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1062,7 +1062,7 @@ pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; fn invocation_parent(&self, id: LocalExpnId) -> LocalDefId; - fn resolve_dollar_crates(&mut self); + fn resolve_dollar_crates(&self); fn visit_ast_fragment_with_placeholders( &mut self, expn_id: LocalExpnId, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 85683c1a03f..51d6e43ab67 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -3,8 +3,8 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::literal; use rustc_ast::{ - self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, - attr, token, tokenstream, + self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, + UnOp, attr, token, tokenstream, }; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -766,4 +766,10 @@ impl<'a> ExtCtxt<'a> { span, ) } + + // Builds an attribute fully manually. + pub fn attr_nested(&self, inner: AttrItem, span: Span) -> ast::Attribute { + let g = &self.sess.psess.attr_id_generator; + attr::mk_attr_from_item(g, inner, None, ast::AttrStyle::Outer, span) + } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 6922ddfd6bd..83a8d601afe 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -12,7 +12,7 @@ use rustc_ast::{ }; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ - AttributeParser, CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr, + AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -167,7 +167,9 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) .take_while(|attr| { !is_cfg(attr) - || strip_unconfigured.cfg_true(attr, strip_unconfigured.lint_node_id).as_bool() + || strip_unconfigured + .cfg_true(attr, strip_unconfigured.lint_node_id, ShouldEmit::Nothing) + .as_bool() }) .collect() } @@ -401,10 +403,18 @@ impl<'a> StripUnconfigured<'a> { /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr, self.lint_node_id).as_bool()) + attrs.iter().all(|attr| { + !is_cfg(attr) + || self.cfg_true(attr, self.lint_node_id, ShouldEmit::ErrorsAndLints).as_bool() + }) } - pub(crate) fn cfg_true(&self, attr: &Attribute, node: NodeId) -> EvalConfigResult { + pub(crate) fn cfg_true( + &self, + attr: &Attribute, + node: NodeId, + emit_errors: ShouldEmit, + ) -> EvalConfigResult { // We need to run this to do basic validation of the attribute, such as that lits are valid, etc // FIXME(jdonszelmann) this should not be necessary in the future match validate_attr::parse_meta(&self.sess.psess, attr) { @@ -428,7 +438,7 @@ impl<'a> StripUnconfigured<'a> { attr.span, node, self.features, - true, + emit_errors, parse_cfg_attr, &CFG_TEMPLATE, ) else { @@ -436,7 +446,7 @@ impl<'a> StripUnconfigured<'a> { return EvalConfigResult::True; }; - eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features) + eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features, emit_errors) } /// If attributes are not allowed on expressions, emit an error for `attr` diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index f99060e9a21..79ec79a2fdf 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,7 +13,7 @@ use rustc_ast::{ MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::EvalConfigResult; +use rustc_attr_parsing::{EvalConfigResult, ShouldEmit}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::PResult; use rustc_feature::Features; @@ -2171,7 +2171,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr: ast::Attribute, pos: usize, ) -> EvalConfigResult { - let res = self.cfg().cfg_true(&attr, node.node_id()); + let res = self.cfg().cfg_true(&attr, node.node_id(), ShouldEmit::ErrorsAndLints); if res.as_bool() { // A trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7d9915d7f68..74872504b79 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -614,6 +614,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC 2632 + // FIXME(const_trait_impl) remove this gated!( const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl, "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \ @@ -684,6 +685,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes ), ungated!( + unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk, EncodeCrossCrate::Yes ), diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index df010f87098..ca57c4f3164 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -295,6 +295,12 @@ impl DefKind { } } + /// This is a "module" in name resolution sense. + #[inline] + pub fn is_module_like(self) -> bool { + matches!(self, DefKind::Mod | DefKind::Enum | DefKind::Trait) + } + #[inline] pub fn is_fn_like(self) -> bool { matches!( @@ -720,6 +726,15 @@ impl<Id> Res<Id> { } } + /// If this is a "module" in name resolution sense, return its `DefId`. + #[inline] + pub fn module_like_def_id(&self) -> Option<DefId> { + match self { + Res::Def(def_kind, def_id) if def_kind.is_module_like() => Some(*def_id), + _ => None, + } + } + /// A human readable name for the res kind ("function", "module", etc.). pub fn descr(&self) -> &'static str { match *self { diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index f93b9e5af53..698406d53a4 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -140,7 +140,9 @@ impl DefKey { pub(crate) fn compute_stable_hash(&self, parent: DefPathHash) -> DefPathHash { let mut hasher = StableHasher::new(); - parent.hash(&mut hasher); + // The new path is in the same crate as `parent`, and will contain the stable_crate_id. + // Therefore, we only need to include information of the parent's local hash. + parent.local_hash().hash(&mut hasher); let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; @@ -181,32 +183,26 @@ pub struct DisambiguatedDefPathData { } impl DisambiguatedDefPathData { - pub fn fmt_maybe_verbose(&self, writer: &mut impl Write, verbose: bool) -> fmt::Result { + pub fn as_sym(&self, verbose: bool) -> Symbol { match self.data.name() { DefPathDataName::Named(name) => { if verbose && self.disambiguator != 0 { - write!(writer, "{}#{}", name, self.disambiguator) + Symbol::intern(&format!("{}#{}", name, self.disambiguator)) } else { - writer.write_str(name.as_str()) + name } } DefPathDataName::Anon { namespace } => { if let DefPathData::AnonAssocTy(method) = self.data { - write!(writer, "{}::{{{}#{}}}", method, namespace, self.disambiguator) + Symbol::intern(&format!("{}::{{{}#{}}}", method, namespace, self.disambiguator)) } else { - write!(writer, "{{{}#{}}}", namespace, self.disambiguator) + Symbol::intern(&format!("{{{}#{}}}", namespace, self.disambiguator)) } } } } } -impl fmt::Display for DisambiguatedDefPathData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt_maybe_verbose(f, true) - } -} - #[derive(Clone, Debug, Encodable, Decodable)] pub struct DefPath { /// The path leading from the crate root to the item. @@ -250,7 +246,7 @@ impl DefPath { let mut s = String::with_capacity(self.data.len() * 16); for component in &self.data { - write!(s, "::{component}").unwrap(); + write!(s, "::{}", component.as_sym(true)).unwrap(); } s @@ -266,7 +262,7 @@ impl DefPath { for component in &self.data { s.extend(opt_delimiter); opt_delimiter = Some('-'); - write!(s, "{component}").unwrap(); + write!(s, "{}", component.as_sym(true)).unwrap(); } s @@ -361,8 +357,16 @@ impl Definitions { }, }; - let parent_hash = DefPathHash::new(stable_crate_id, Hash64::ZERO); - let def_path_hash = key.compute_stable_hash(parent_hash); + // We want *both* halves of a DefPathHash to depend on the crate-id of the defining crate. + // The crate-id can be more easily changed than the DefPath of an item, so, in the case of + // a crate-local DefPathHash collision, the user can simply "roll the dice again" for all + // DefPathHashes in the crate by changing the crate disambiguator (e.g. via bumping the + // crate's version number). + // + // Children paths will only hash the local portion, and still inherit the change to the + // root hash. + let def_path_hash = + DefPathHash::new(stable_crate_id, Hash64::new(stable_crate_id.as_u64())); // Create the root definition. let mut table = DefPathTable::new(stable_crate_id); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 159518a8d87..e7898648c2b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{ self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, - LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, + LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, join_path_idents, }; pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, @@ -1168,7 +1168,7 @@ impl AttrPath { impl fmt::Display for AttrPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::<Vec<_>>().join("::")) + write!(f, "{}", join_path_idents(&self.segments)) } } @@ -4163,6 +4163,7 @@ impl<'hir> Item<'hir> { expect_trait, ( + Constness, IsAuto, Safety, Ident, @@ -4170,8 +4171,8 @@ impl<'hir> Item<'hir> { GenericBounds<'hir>, &'hir [TraitItemId] ), - ItemKind::Trait(is_auto, safety, ident, generics, bounds, items), - (*is_auto, *safety, *ident, generics, bounds, items); + ItemKind::Trait(constness, is_auto, safety, ident, generics, bounds, items), + (*constness, *is_auto, *safety, *ident, generics, bounds, items); expect_trait_alias, (Ident, &'hir Generics<'hir>, GenericBounds<'hir>), ItemKind::TraitAlias(ident, generics, bounds), (*ident, generics, bounds); @@ -4341,7 +4342,15 @@ pub enum ItemKind<'hir> { /// A union definition, e.g., `union Foo<A, B> {x: A, y: B}`. Union(Ident, &'hir Generics<'hir>, VariantData<'hir>), /// A trait definition. - Trait(IsAuto, Safety, Ident, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemId]), + Trait( + Constness, + IsAuto, + Safety, + Ident, + &'hir Generics<'hir>, + GenericBounds<'hir>, + &'hir [TraitItemId], + ), /// A trait alias. TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>), @@ -4385,7 +4394,7 @@ impl ItemKind<'_> { | ItemKind::Enum(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Union(ident, ..) - | ItemKind::Trait(_, _, ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) | ItemKind::TraitAlias(ident, ..) => Some(ident), ItemKind::Use(_, UseKind::Glob | UseKind::ListStem) @@ -4403,7 +4412,7 @@ impl ItemKind<'_> { | ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _) | ItemKind::Union(_, generics, _) - | ItemKind::Trait(_, _, _, generics, _, _) + | ItemKind::Trait(_, _, _, _, generics, _, _) | ItemKind::TraitAlias(_, generics, _) | ItemKind::Impl(Impl { generics, .. }) => generics, _ => return None, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 3edb94c28da..f33915d5b07 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -618,7 +618,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_variant_data(struct_definition)); } - ItemKind::Trait(_is_auto, _safety, ident, ref generics, bounds, trait_item_refs) => { + ItemKind::Trait( + _constness, + _is_auto, + _safety, + ident, + ref generics, + bounds, + trait_item_refs, + ) => { try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); walk_list!(visitor, visit_param_bound, bounds); diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 18c2bfdac8c..3b797c1f103 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -28,7 +28,8 @@ fn def_path_hash_depends_on_crate_id() { assert_ne!(h0.local_hash(), h1.local_hash()); fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash { - let parent_hash = DefPathHash::new(stable_crate_id, Hash64::ZERO); + let parent_hash = + DefPathHash::new(stable_crate_id, Hash64::new(stable_crate_id.as_u64())); let key = DefKey { parent: None, diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 529d3578985..e38ca9e80ce 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -117,15 +117,15 @@ hir_analysis_coercion_between_struct_same_note = expected coercion between the s hir_analysis_coercion_between_struct_single_note = expected a single field to be coerced, none found -hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `#[const_trait]` traits +hir_analysis_const_bound_for_non_const_trait = `{$modifier}` can only be applied to `const` traits .label = can't be applied to `{$trait_name}` - .note = `{$trait_name}` can't be used with `{$modifier}` because it isn't annotated with `#[const_trait]` - .suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations + .note = `{$trait_name}` can't be used with `{$modifier}` because it isn't `const` + .suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations -hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]` +hir_analysis_const_impl_for_non_const_trait = const `impl` for trait `{$trait_name}` which is not `const` .label = this trait is not `const` - .suggestion = {$suggestion_pre}mark `{$trait_name}` as `#[const_trait]` to allow it to have `const` implementations - .note = marking a trait with `#[const_trait]` ensures all default method bodies are `const` + .suggestion = {$suggestion_pre}mark `{$trait_name}` as `const` to allow it to have `const` implementations + .note = marking a trait with `const` ensures all default method bodies are `const` .adding = adding a non-const method body in the future would be a breaking change hir_analysis_const_param_ty_impl_on_non_adt = @@ -158,7 +158,7 @@ hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be imp hir_analysis_drop_impl_negative = negative `Drop` impls are not supported hir_analysis_drop_impl_on_wrong_item = - the `Drop` trait may only be implemented for local structs, enums, and unions + the `{$trait_}` trait may only be implemented for local structs, enums, and unions .label = must be a struct, enum, or union in the current crate hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 70fbb8a543e..03c026cd6c8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -889,8 +889,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), tcx.ensure_ok().predicates_of(def_id); tcx.ensure_ok().explicit_item_bounds(def_id); tcx.ensure_ok().explicit_item_self_bounds(def_id); - tcx.ensure_ok().item_bounds(def_id); - tcx.ensure_ok().item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { tcx.ensure_ok().explicit_implied_const_bounds(def_id); tcx.ensure_ok().const_conditions(def_id); @@ -1044,8 +1042,12 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let has_type = match assoc_item.container { ty::AssocItemContainer::Impl => true, ty::AssocItemContainer::Trait => { - tcx.ensure_ok().item_bounds(def_id); - tcx.ensure_ok().item_self_bounds(def_id); + tcx.ensure_ok().explicit_item_bounds(def_id); + tcx.ensure_ok().explicit_item_self_bounds(def_id); + if tcx.is_conditionally_const(def_id) { + tcx.ensure_ok().explicit_implied_const_bounds(def_id); + tcx.ensure_ok().const_conditions(def_id); + } res = res.and(check_trait_item(tcx, def_id)); assoc_item.defaultness(tcx).has_value() } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index dcab6ef1c5a..6e5fe3823ab 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -422,6 +422,9 @@ pub(crate) fn check_intrinsic_type( vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize], tcx.types.unit, ), + sym::const_make_global => { + (0, 0, vec![Ty::new_mut_ptr(tcx, tcx.types.u8)], Ty::new_imm_ptr(tcx, tcx.types.u8)) + } sym::ptr_offset_from => ( 1, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 607028f4d9a..14ec82ede1c 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { let implied_obligations = traits::elaborate(tcx, predicates_with_span); for (pred, obligation_span) in implied_obligations { - // We lower empty bounds like `Vec<dyn Copy>:` as - // `WellFormed(Vec<dyn Copy>)`, which will later get checked by - // regular WF checking - if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() { - continue; + match pred.kind().skip_binder() { + // We lower empty bounds like `Vec<dyn Copy>:` as + // `WellFormed(Vec<dyn Copy>)`, which will later get checked by + // regular WF checking + ty::ClauseKind::WellFormed(..) + // Unstable feature goals cannot be proven in an empty environment so skip them + | ty::ClauseKind::UnstableFeature(..) => continue, + _ => {} } + // Match the existing behavior. if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) { let pred = self.normalize(span, None, pred); diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8356a0af63c..27948f50a4a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -37,6 +37,7 @@ pub(super) fn check_trait<'tcx>( let lang_items = tcx.lang_items(); let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header }; checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; + checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) @@ -83,7 +84,10 @@ fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaran let impl_ = tcx.hir_expect_item(impl_did).expect_impl(); - Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span })) + Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { + span: impl_.self_ty.span, + trait_: tcx.item_name(checker.impl_header.trait_ref.skip_binder().def_id), + })) } fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 28a8758178f..0728b24eb14 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -844,15 +844,20 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let item = tcx.hir_expect_item(def_id); - let (is_alias, is_auto, safety) = match item.kind { - hir::ItemKind::Trait(is_auto, safety, ..) => (false, is_auto == hir::IsAuto::Yes, safety), - hir::ItemKind::TraitAlias(..) => (true, false, hir::Safety::Safe), + let (constness, is_alias, is_auto, safety) = match item.kind { + hir::ItemKind::Trait(constness, is_auto, safety, ..) => { + (constness, false, is_auto == hir::IsAuto::Yes, safety) + } + hir::ItemKind::TraitAlias(..) => (hir::Constness::NotConst, true, false, hir::Safety::Safe), _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), }; let attrs = tcx.get_all_attrs(def_id); // Only regular traits can be const. - let constness = if !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_)) { + // FIXME(const_trait_impl): remove this + let constness = if constness == hir::Constness::Const + || !is_alias && find_attr!(attrs, AttributeKind::ConstTrait(_)) + { hir::Constness::Const } else { hir::Constness::NotConst diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index e51ef46afb7..548ba343aae 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -38,12 +38,13 @@ fn associated_type_bounds<'tcx>( let icx = ItemCtxt::new(tcx, assoc_item_def_id); let mut bounds = Vec::new(); icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); - // Implicit bounds are added to associated types unless a `?Trait` bound is found + match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { + // Implicit bounds are added to associated types unless a `?Trait` bound is found. icx.lowerer().add_sizedness_bounds( &mut bounds, item_ty, @@ -53,37 +54,48 @@ fn associated_type_bounds<'tcx>( span, ); icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span); + + // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses. + let trait_def_id = tcx.local_parent(assoc_item_def_id); + let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); + + let item_trait_ref = + ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); + bounds.extend(trait_predicates.predicates.iter().copied().filter_map( + |(clause, span)| { + remap_gat_vars_and_recurse_into_nested_projections( + tcx, + filter, + item_trait_ref, + assoc_item_def_id, + span, + clause, + ) + }, + )); } // `ConstIfConst` is only interested in `[const]` bounds. - PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => { + // FIXME(const_trait_impl): We *could* uplift the + // `where Self::Assoc: [const] Trait` bounds from the parent trait + // here too, but we'd need to split `const_conditions` into two + // queries (like we do for `trait_explicit_predicates_and_bounds`) + // since we need to also filter the predicates *out* of the const + // conditions or they lead to cycles in the trait solver when + // utilizing these bounds. For now, let's do nothing. + } } - let trait_def_id = tcx.local_parent(assoc_item_def_id); - let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); - - let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); - let bounds_from_parent = - trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { - remap_gat_vars_and_recurse_into_nested_projections( - tcx, - filter, - item_trait_ref, - assoc_item_def_id, - span, - clause, - ) - }); - - let all_bounds = tcx.arena.alloc_from_iter(bounds.into_iter().chain(bounds_from_parent)); + let bounds = tcx.arena.alloc_from_iter(bounds); debug!( "associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id.to_def_id()), - all_bounds + bounds ); - assert_only_contains_predicates_from(filter, all_bounds, item_ty); + assert_only_contains_predicates_from(filter, bounds, item_ty); - all_bounds + bounds }) } @@ -112,6 +124,7 @@ fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( ty::ClauseKind::Trait(tr) => tr.self_ty(), ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty(), ty::ClauseKind::TypeOutlives(outlives) => outlives.0, + ty::ClauseKind::HostEffect(host) => host.self_ty(), _ => return None, }; diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a93e58b101f..f2f1560d8b2 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,6 +1,7 @@ use std::assert_matches::assert_matches; use hir::Node; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -162,7 +163,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .map(|t| ty::Binder::dummy(t.instantiate_identity())); } } - ItemKind::Trait(_, _, _, _, self_bounds, ..) + ItemKind::Trait(_, _, _, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, _, self_bounds) => { is_trait = Some((self_bounds, item.span)); } @@ -333,6 +334,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen predicates.extend(const_evaluatable_predicates_of(tcx, def_id, &predicates)); } + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + // FIXME(staged_api): We might want to look at the normal stability attributes too but + // first we would need a way to let std/core use APIs with unstable feature bounds from + // within stable APIs. + let allow_unstable_feature_attr = + find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + + for (feat_name, span) in allow_unstable_feature_attr { + predicates.insert((ty::ClauseKind::UnstableFeature(*feat_name).upcast(tcx), *span)); + } + let mut predicates: Vec<_> = predicates.into_iter().collect(); // Subtle: before we store the predicates into the tcx, we @@ -764,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::ConstEvaluatable(_) => { bug!( "unexpected non-`Self` predicate when computing \ @@ -791,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>( | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => { bug!( "unexpected non-`Self` predicate when computing \ @@ -1006,7 +1022,7 @@ pub(super) fn const_conditions<'tcx>( Node::Item(item) => match item.kind { hir::ItemKind::Impl(impl_) => (impl_.generics, None, false), hir::ItemKind::Fn { generics, .. } => (generics, None, false), - hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => { + hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => { (generics, Some((item.owner_id.def_id, supertraits)), false) } _ => bug!("const_conditions called on wrong item: {def_id:?}"), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 8d7ac7db67b..77e63e38c8c 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -634,7 +634,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::Enum(_, generics, _) | hir::ItemKind::Struct(_, generics, _) | hir::ItemKind::Union(_, generics, _) - | hir::ItemKind::Trait(_, _, _, generics, ..) + | hir::ItemKind::Trait(_, _, _, _, generics, ..) | hir::ItemKind::TraitAlias(_, generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c1c82839212..fbd21f8b100 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -205,6 +205,7 @@ pub(crate) struct DropImplOnWrongItem { #[primary_span] #[label] pub span: Span, + pub trait_: Symbol, } #[derive(Diagnostic)] @@ -525,6 +526,7 @@ pub(crate) struct ConstImplForNonConstTrait { pub trait_name: String, #[suggestion( applicability = "machine-applicable", + // FIXME(const_trait_impl) fix this suggestion code = "#[const_trait] ", style = "verbose" )] @@ -548,6 +550,7 @@ pub(crate) struct ConstBoundForNonConstTrait { pub suggestion_pre: &'static str, #[suggestion( applicability = "machine-applicable", + // FIXME(const_trait_impl) fix this suggestion code = "#[const_trait] ", style = "verbose" )] 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 3f928fd056e..2d60c9561a9 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 @@ -635,7 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { self.suggest_adding_type_and_const_args(err); } ExcessTypesOrConsts { .. } => { - // this can happen with `[const] T` where T isn't a const_trait. + // this can happen with `[const] T` where T isn't a `const trait`. } _ => unreachable!(), } 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 4784cfb5235..9a752aeccdd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -334,7 +334,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let (trait_generics, trait_bounds) = match parent_trait.kind { - hir::ItemKind::Trait(_, _, _, generics, supertraits, _) => (generics, supertraits), + hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits), hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits), _ => unreachable!(), }; diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 574d19a5aa5..0043f0c7117 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -499,6 +499,7 @@ fn trait_specialization_kind<'tcx>( | ty::ClauseKind::ConstArgHasType(..) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(..) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => None, } } diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 2c1d443f951..d3a57a4d8e5 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => {} } } diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 301f5de9424..99a633e2b7d 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -7,8 +7,7 @@ use smallvec::smallvec; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. -pub(crate) type RequiredPredicates<'tcx> = - FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>; +pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 2c13c9ef438..bda02042aa6 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -735,8 +735,17 @@ impl<'a> State<'a> { } self.bclose(item.span, cb); } - hir::ItemKind::Trait(is_auto, safety, ident, generics, bounds, trait_items) => { + hir::ItemKind::Trait( + constness, + is_auto, + safety, + ident, + generics, + bounds, + trait_items, + ) => { let (cb, ib) = self.head(""); + self.print_constness(constness); self.print_is_auto(is_auto); self.print_safety(safety); self.word_nbsp("trait"); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 459c0498d50..a413f805873 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -161,16 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resume type defaults to `()` if the coroutine has no argument. let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); - // In the new solver, we can just instantiate this eagerly - // with the witness. This will ensure that goals that don't need - // to stall on interior types will get processed eagerly. - let interior = if self.next_trait_solver() { - Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args) - } else { - self.next_ty_var(expr_span) - }; - - self.deferred_coroutine_interiors.borrow_mut().push((expr_def_id, interior)); + let interior = Ty::new_coroutine_witness(tcx, expr_def_id.to_def_id(), parent_args); // Coroutines that come from coroutine closures have not yet determined // their kind ty, so make a fresh infer var which will be constrained diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index af1fc045ac8..0b3d50ff219 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -625,50 +625,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // trigger query cycle ICEs, as doing so requires MIR. self.select_obligations_where_possible(|_| {}); - let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut()); - debug!(?coroutines); - - let mut obligations = vec![]; - - if !self.next_trait_solver() { - for &(coroutine_def_id, interior) in coroutines.iter() { - debug!(?coroutine_def_id); - - // Create the `CoroutineWitness` type that we will unify with `interior`. - let args = ty::GenericArgs::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(coroutine_def_id.to_def_id()), - ); - let witness = - Ty::new_coroutine_witness(self.tcx, coroutine_def_id.to_def_id(), args); - - // Unify `interior` with `witness` and collect all the resulting obligations. - let span = self.tcx.hir_body_owned_by(coroutine_def_id).value.span; - let ty::Infer(ty::InferTy::TyVar(_)) = interior.kind() else { - span_bug!(span, "coroutine interior witness not infer: {:?}", interior.kind()) - }; - let ok = self - .at(&self.misc(span), self.param_env) - // Will never define opaque types, as all we do is instantiate a type variable. - .eq(DefineOpaqueTypes::Yes, interior, witness) - .expect("Failed to unify coroutine interior type"); - - obligations.extend(ok.obligations); - } - } + let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() + else { + bug!(); + }; - if !coroutines.is_empty() { - obligations.extend( + if defining_opaque_types_and_generators + .iter() + .any(|def_id| self.tcx.is_coroutine(def_id.to_def_id())) + { + self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend( self.fulfillment_cx .borrow_mut() - .drain_stalled_obligations_for_coroutines(&self.infcx), + .drain_stalled_obligations_for_coroutines(&self.infcx) + .into_iter() + .map(|o| (o.predicate, o.cause)), ); } - - self.typeck_results - .borrow_mut() - .coroutine_stalled_predicates - .extend(obligations.into_iter().map(|o| (o.predicate, o.cause))); } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index e4c62bf027b..367e2b6b372 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -54,6 +54,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::Ambiguous => false, } } diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 0037d5ac042..38413cca633 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use hir::def_id::DefId; use hir::{HirId, ItemKind}; +use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; @@ -383,13 +384,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All that is left is `_`! We need to use the full path. It doesn't matter which one we // pick, so just take the first one. match import_items[0].kind { - ItemKind::Use(path, _) => Some( - path.segments - .iter() - .map(|segment| segment.ident.to_string()) - .collect::<Vec<_>>() - .join("::"), - ), + ItemKind::Use(path, _) => { + Some(join_path_idents(path.segments.iter().map(|seg| seg.ident))) + } _ => { span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 94c93e73627..3261327a9fd 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -921,6 +921,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => None, } }); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2815621ffde..dbfa7e6273c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1189,7 +1189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.1.insert((self_ty.span, "")); } Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..), + kind: hir::ItemKind::Trait(_, rustc_ast::ast::IsAuto::Yes, ..), span: item_span, .. })) => { @@ -1201,7 +1201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some( Node::Item(hir::Item { kind: - hir::ItemKind::Trait(_, _, ident, ..) + hir::ItemKind::Trait(_, _, _, ident, ..) | hir::ItemKind::TraitAlias(ident, ..), .. }) @@ -4084,7 +4084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, ident, _, bounds, _), + kind: hir::ItemKind::Trait(_, _, _, ident, _, bounds, _), .. }) => { let (sp, sep, article) = if bounds.is_empty() { diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 26be5fc6d19..9f4ab8ca5d4 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -63,8 +63,6 @@ pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, HirId)>>, - pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, Ty<'tcx>)>>, - pub(super) deferred_repeat_expr_checks: RefCell<Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>, ty::Const<'tcx>)>>, @@ -103,7 +101,6 @@ impl<'tcx> TypeckRootCtxt<'tcx> { deferred_cast_checks: RefCell::new(Vec::new()), deferred_transmute_checks: RefCell::new(Vec::new()), deferred_asm_checks: RefCell::new(Vec::new()), - deferred_coroutine_interiors: RefCell::new(Vec::new()), deferred_repeat_expr_checks: RefCell::new(Vec::new()), diverging_type_vars: RefCell::new(Default::default()), infer_var_info: RefCell::new(Default::default()), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index d5d49e3188a..6be53c948c8 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -116,9 +116,15 @@ impl<'tcx> InferCtxt<'tcx> { } let region_obligations = self.take_registered_region_obligations(); + let region_assumptions = self.take_registered_region_assumptions(); debug!(?region_obligations); let region_constraints = self.with_region_constraints(|region_constraints| { - make_query_region_constraints(tcx, region_obligations, region_constraints) + make_query_region_constraints( + tcx, + region_obligations, + region_constraints, + region_assumptions, + ) }); debug!(?region_constraints); @@ -169,6 +175,11 @@ impl<'tcx> InferCtxt<'tcx> { self.register_outlives_constraint(predicate, cause); } + for assumption in &query_response.value.region_constraints.assumptions { + let assumption = instantiate_value(self.tcx, &result_args, *assumption); + self.register_region_assumption(assumption); + } + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -292,6 +303,18 @@ impl<'tcx> InferCtxt<'tcx> { }), ); + // FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions + // at once, rather than calling `instantiate_value` repeatedly which may + // create more universes. + output_query_region_constraints.assumptions.extend( + query_response + .value + .region_constraints + .assumptions + .iter() + .map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)), + ); + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>( tcx: TyCtxt<'tcx>, outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>, region_constraints: &RegionConstraintData<'tcx>, + assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, ) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys } = region_constraints; @@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>( })) .collect(); - QueryRegionConstraints { outlives } + QueryRegionConstraints { outlives, assumptions } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index cc3ad921489..2d269e320b6 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> { /// that all type inference variables have been bound and so forth. region_obligations: Vec<TypeOutlivesConstraint<'tcx>>, + /// The outlives bounds that we assume must hold about placeholders that + /// come from instantiating the binder of coroutine-witnesses. These bounds + /// are deduced from the well-formedness of the witness's types, and are + /// necessary because of the way we anonymize the regions in a coroutine, + /// which may cause types to no longer be considered well-formed. + region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, + /// Caches for opaque type inference. opaque_type_storage: OpaqueTypeStorage<'tcx>, } @@ -164,7 +171,8 @@ impl<'tcx> InferCtxtInner<'tcx> { int_unification_storage: Default::default(), float_unification_storage: Default::default(), region_constraint_storage: Some(Default::default()), - region_obligations: vec![], + region_obligations: Default::default(), + region_assumptions: Default::default(), opaque_type_storage: Default::default(), } } @@ -175,6 +183,11 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] + pub fn region_assumptions(&self) -> &[ty::ArgOutlivesPredicate<'tcx>] { + &self.region_assumptions + } + + #[inline] pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> { self.projection_cache.with_log(&mut self.undo_log) } diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index cb5a33c5c97..47b738a4079 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_middle::{bug, ty}; use tracing::debug; @@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> { /// optimized in the future, though. region_bound_pairs: RegionBoundPairs<'tcx>, known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>, + /// Assumptions that come from the well-formedness of coroutines that we prove + /// auto trait bounds for during the type checking of this body. + higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>, } /// "Region-bound pairs" tracks outlives relations that are known to @@ -52,6 +55,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { param_env: ty::ParamEnv<'tcx>, known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>, extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>, + higher_ranked_assumptions: FxHashSet<ty::ArgOutlivesPredicate<'tcx>>, ) -> Self { let mut region_relation = TransitiveRelationBuilder::default(); let mut region_bound_pairs = RegionBoundPairs::default(); @@ -88,6 +92,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { known_type_outlives, free_region_map: FreeRegionMap { relation: region_relation.freeze() }, region_bound_pairs, + higher_ranked_assumptions, } } @@ -102,4 +107,8 @@ impl<'tcx> OutlivesEnvironment<'tcx> { pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] { &self.known_type_outlives } + + pub fn higher_ranked_assumptions(&self) -> &FxHashSet<ty::ArgOutlivesPredicate<'tcx>> { + &self.higher_ranked_assumptions + } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 2a4544c1140..19911bfbd48 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -10,6 +10,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog}; use super::{InferCtxt, RegionResolutionError, SubregionOrigin}; use crate::infer::free_regions::RegionRelations; use crate::infer::lexical_region_resolve; +use crate::infer::region_constraints::Constraint; pub mod env; pub mod for_liveness; @@ -54,18 +55,29 @@ impl<'tcx> InferCtxt<'tcx> { } }; - let storage = { + let mut storage = { let mut inner = self.inner.borrow_mut(); let inner = &mut *inner; assert!( self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(), "region_obligations not empty: {:#?}", - inner.region_obligations + inner.region_obligations, ); assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log)); inner.region_constraint_storage.take().expect("regions already resolved") }; + // Filter out any region-region outlives assumptions that are implied by + // coroutine well-formedness. + if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions { + storage.data.constraints.retain(|(constraint, _)| match *constraint { + Constraint::RegSubReg(r1, r2) => !outlives_env + .higher_ranked_assumptions() + .contains(&ty::OutlivesPredicate(r2.into(), r1)), + _ => true, + }); + } + let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map()); let (lexical_region_resolutions, errors) = @@ -93,6 +105,11 @@ impl<'tcx> InferCtxt<'tcx> { "region_obligations not empty: {:#?}", self.inner.borrow().region_obligations ); + assert!( + self.inner.borrow().region_assumptions.is_empty(), + "region_assumptions not empty: {:#?}", + self.inner.borrow().region_assumptions + ); self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data() } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 43504bd77c1..a8520c0e71d 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -84,7 +84,7 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; impl<'tcx> InferCtxt<'tcx> { pub fn register_outlives_constraint( &self, - ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, + ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>, cause: &ObligationCause<'tcx>, ) { match arg.kind() { @@ -170,6 +170,16 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } + pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) { + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushRegionAssumption); + inner.region_assumptions.push(assumption); + } + + pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> { + std::mem::take(&mut self.inner.borrow_mut().region_assumptions) + } + /// Process the region obligations that must be proven (during /// `regionck`) for the given `body_id`, given information about /// the region bounds in scope and so forth. @@ -220,6 +230,14 @@ impl<'tcx> InferCtxt<'tcx> { let (sup_type, sub_region) = (sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self)); + if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions + && outlives_env + .higher_ranked_assumptions() + .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region)) + { + continue; + } + debug!(?sup_type, ?sub_region, ?origin); let outlives = &mut TypeOutlives::new( diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 34e649afedc..40e4c329446 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> { RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, + PushRegionAssumption, } macro_rules! impl_from { @@ -77,6 +78,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> { let popped = self.region_obligations.pop(); assert_matches!(popped, Some(_), "pushed region constraint but could not pop it"); } + UndoLog::PushRegionAssumption => { + self.region_assumptions.pop(); + } } } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 8d9f2385b71..1a1cfc9fa6f 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -440,6 +440,7 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly .help = only local labels of the form `<number>:` should be used in inline asm .note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro + lint_invalid_crate_type_value = invalid `crate_type` value .suggestion = did you mean @@ -508,27 +509,50 @@ lint_metavariable_still_repeating = variable `{$name}` is still repeating at thi lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator -lint_mismatched_lifetime_syntaxes = - lifetime flowing from input to output with different syntax can be confusing - .label_mismatched_lifetime_syntaxes_inputs = - {$n_inputs -> - [one] this lifetime flows - *[other] these lifetimes flow - } to the output - .label_mismatched_lifetime_syntaxes_outputs = - the {$n_outputs -> - [one] lifetime gets - *[other] lifetimes get - } resolved as `{$lifetime_name}` +lint_mismatched_lifetime_syntaxes_eliding_while_named = + eliding a lifetime that's named elsewhere is confusing + +lint_mismatched_lifetime_syntaxes_help = + the same lifetime is referred to in inconsistent ways, making the signature confusing + +lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named = + hiding or eliding a lifetime that's named elsewhere is confusing + +lint_mismatched_lifetime_syntaxes_hiding_while_elided = + hiding a lifetime that's elided elsewhere is confusing + +lint_mismatched_lifetime_syntaxes_hiding_while_named = + hiding a lifetime that's named elsewhere is confusing + +lint_mismatched_lifetime_syntaxes_input_elided = + the lifetime is elided here + +lint_mismatched_lifetime_syntaxes_input_hidden = + the lifetime is hidden here + +lint_mismatched_lifetime_syntaxes_input_named = + the lifetime is named here + +lint_mismatched_lifetime_syntaxes_output_elided = + the same lifetime is elided here + +lint_mismatched_lifetime_syntaxes_output_hidden = + the same lifetime is hidden here + +lint_mismatched_lifetime_syntaxes_output_named = + the same lifetime is named here lint_mismatched_lifetime_syntaxes_suggestion_explicit = - one option is to consistently use `{$lifetime_name}` + consistently use `{$lifetime_name}` lint_mismatched_lifetime_syntaxes_suggestion_implicit = - one option is to consistently remove the lifetime + remove the lifetime name from references lint_mismatched_lifetime_syntaxes_suggestion_mixed = - one option is to remove the lifetime for references and use the anonymous lifetime for paths + remove the lifetime name from references and use `'_` for type paths + +lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths = + use `'_` for type paths lint_missing_unsafe_on_extern = extern blocks should be unsafe .suggestion = needs `unsafe` before the extern keyword @@ -744,6 +768,9 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> *[false] this semicolon } +lint_reexport_private_dependency = + {$kind} `{$name}` from private dependency '{$krate}' is re-exported + lint_remove_mut_from_pattern = remove `mut` from the parameter lint_removed_lint = lint `{$name}` has been removed: {$reason} @@ -790,6 +817,9 @@ lint_supertrait_as_deref_target = this `Deref` implementation is covered by an i .label2 = target type is a supertrait of `{$self_ty}` .help = consider removing this implementation or replacing it with a method instead +lint_surrogate_char_cast = surrogate values are not valid for `char` + .note = `0xD800..=0xDFFF` are reserved for Unicode surrogates and are not valid `char` values + lint_suspicious_double_ref_clone = using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type @@ -799,6 +829,9 @@ lint_suspicious_double_ref_deref = lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal .help = consider adding the symbol to `compiler/rustc_span/src/symbol.rs` +lint_too_large_char_cast = value exceeds maximum `char` value + .note = maximum valid `char` value is `0x10FFFF` + lint_trailing_semi_macro = trailing semicolon in macro used in expression position .note1 = macro invocations at the end of a block are treated as expressions .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 255ff56f62b..73e68834232 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1519,8 +1519,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { ClauseKind::TypeOutlives(..) | ClauseKind::RegionOutlives(..) => "lifetime", + ClauseKind::UnstableFeature(_) // `ConstArgHasType` is never global as `ct` is always a param - ClauseKind::ConstArgHasType(..) + | ClauseKind::ConstArgHasType(..) // Ignore projections, as they can only be global // if the trait bound is global | ClauseKind::Projection(..) @@ -1584,6 +1585,8 @@ impl EarlyLintPass for DoubleNegations { if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _)) + // Don't lint if this jumps macro expansion boundary (Issue #143980) + && expr.span.eq_ctxt(inner.span) { cx.emit_span_lint( DOUBLE_NEGATIONS, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 653559009cc..f0fbf5bc81e 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -351,6 +351,9 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } + BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => { + lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag); + } BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 419124d5144..f06757b3c23 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -55,7 +55,7 @@ mod invalid_from_utf8; mod late; mod let_underscore; mod levels; -mod lifetime_syntax; +pub mod lifetime_syntax; mod lints; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 5465968e984..2a5a34cdc6e 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -140,43 +140,115 @@ fn report_mismatches<'tcx>( } } -fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool { - // Categorize lifetimes into source/syntax buckets. - let mut n_hidden = 0; - let mut n_elided = 0; - let mut n_named = 0; +#[derive(Debug, Copy, Clone, PartialEq)] +enum LifetimeSyntaxCategory { + Hidden, + Elided, + Named, +} - for info in input_info.iter().chain(output_info) { +impl LifetimeSyntaxCategory { + fn new(syntax_source: (hir::LifetimeSyntax, LifetimeSource)) -> Option<Self> { use LifetimeSource::*; use hir::LifetimeSyntax::*; - let syntax_source = (info.lifetime.syntax, info.lifetime.source); - match syntax_source { - // Ignore any other kind of lifetime. - (_, Other) => continue, - // E.g. `&T`. - (Implicit, Reference | OutlivesBound | PreciseCapturing) | + (Implicit, Reference) | // E.g. `&'_ T`. - (ExplicitAnonymous, Reference | OutlivesBound | PreciseCapturing) | + (ExplicitAnonymous, Reference) | // E.g. `ContainsLifetime<'_>`. - (ExplicitAnonymous, Path { .. }) => n_elided += 1, + (ExplicitAnonymous, Path { .. }) | + // E.g. `+ '_`, `+ use<'_>`. + (ExplicitAnonymous, OutlivesBound | PreciseCapturing) => { + Some(Self::Elided) + } // E.g. `ContainsLifetime`. - (Implicit, Path { .. }) => n_hidden += 1, + (Implicit, Path { .. }) => { + Some(Self::Hidden) + } // E.g. `&'a T`. - (ExplicitBound, Reference | OutlivesBound | PreciseCapturing) | + (ExplicitBound, Reference) | // E.g. `ContainsLifetime<'a>`. - (ExplicitBound, Path { .. }) => n_named += 1, - }; + (ExplicitBound, Path { .. }) | + // E.g. `+ 'a`, `+ use<'a>`. + (ExplicitBound, OutlivesBound | PreciseCapturing) => { + Some(Self::Named) + } + + (Implicit, OutlivesBound | PreciseCapturing) | + (_, Other) => { + None + } + } + } +} + +#[derive(Debug, Default)] +pub struct LifetimeSyntaxCategories<T> { + pub hidden: T, + pub elided: T, + pub named: T, +} + +impl<T> LifetimeSyntaxCategories<T> { + fn select(&mut self, category: LifetimeSyntaxCategory) -> &mut T { + use LifetimeSyntaxCategory::*; + + match category { + Elided => &mut self.elided, + Hidden => &mut self.hidden, + Named => &mut self.named, + } + } +} + +impl<T> LifetimeSyntaxCategories<Vec<T>> { + pub fn len(&self) -> LifetimeSyntaxCategories<usize> { + LifetimeSyntaxCategories { + hidden: self.hidden.len(), + elided: self.elided.len(), + named: self.named.len(), + } + } + + pub fn flatten(&self) -> impl Iterator<Item = &T> { + let Self { hidden, elided, named } = self; + [hidden.iter(), elided.iter(), named.iter()].into_iter().flatten() + } +} + +impl std::ops::Add for LifetimeSyntaxCategories<usize> { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + hidden: self.hidden + rhs.hidden, + elided: self.elided + rhs.elided, + named: self.named + rhs.named, + } + } +} + +fn lifetimes_use_matched_syntax(input_info: &[Info<'_>], output_info: &[Info<'_>]) -> bool { + let mut syntax_counts = LifetimeSyntaxCategories::<usize>::default(); + + for info in input_info.iter().chain(output_info) { + if let Some(category) = info.lifetime_syntax_category() { + *syntax_counts.select(category) += 1; + } } - let syntax_counts = (n_hidden, n_elided, n_named); tracing::debug!(?syntax_counts); - matches!(syntax_counts, (_, 0, 0) | (0, _, 0) | (0, 0, _)) + matches!( + syntax_counts, + LifetimeSyntaxCategories { hidden: _, elided: 0, named: 0 } + | LifetimeSyntaxCategories { hidden: 0, elided: _, named: 0 } + | LifetimeSyntaxCategories { hidden: 0, elided: 0, named: _ } + ) } fn emit_mismatch_diagnostic<'tcx>( @@ -238,7 +310,7 @@ fn emit_mismatch_diagnostic<'tcx>( use LifetimeSource::*; use hir::LifetimeSyntax::*; - let syntax_source = (info.lifetime.syntax, info.lifetime.source); + let syntax_source = info.syntax_source(); if let (_, Other) = syntax_source { // Ignore any other kind of lifetime. @@ -259,7 +331,6 @@ fn emit_mismatch_diagnostic<'tcx>( // E.g. `&'_ T`. (ExplicitAnonymous, Reference) => { suggest_change_to_implicit.push(info); - suggest_change_to_mixed_implicit.push(info); suggest_change_to_explicit_bound.push(info); } @@ -319,12 +390,22 @@ fn emit_mismatch_diagnostic<'tcx>( } } + let categorize = |infos: &[Info<'_>]| { + let mut categories = LifetimeSyntaxCategories::<Vec<_>>::default(); + for info in infos { + if let Some(category) = info.lifetime_syntax_category() { + categories.select(category).push(info.reporting_span()); + } + } + categories + }; + + let inputs = categorize(input_info); + let outputs = categorize(output_info); + let make_implicit_suggestions = |infos: &[&Info<'_>]| infos.iter().map(|i| i.removing_span()).collect::<Vec<_>>(); - let inputs = input_info.iter().map(|info| info.reporting_span()).collect(); - let outputs = output_info.iter().map(|info| info.reporting_span()).collect(); - let explicit_bound_suggestion = bound_lifetime.map(|info| { build_mismatch_suggestion(info.lifetime_name(), &suggest_change_to_explicit_bound) }); @@ -399,8 +480,6 @@ fn emit_mismatch_diagnostic<'tcx>( ?explicit_anonymous_suggestion, ); - let lifetime_name = bound_lifetime.map(|info| info.lifetime_name()).unwrap_or("'_").to_owned(); - // We can produce a number of suggestions which may overwhelm // the user. Instead, we order the suggestions based on Rust // idioms. The "best" choice is shown to the user and the @@ -413,8 +492,8 @@ fn emit_mismatch_diagnostic<'tcx>( cx.emit_span_lint( MISMATCHED_LIFETIME_SYNTAXES, - Vec::clone(&inputs), - lints::MismatchedLifetimeSyntaxes { lifetime_name, inputs, outputs, suggestions }, + inputs.flatten().copied().collect::<Vec<_>>(), + lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions }, ); } @@ -422,12 +501,12 @@ fn build_mismatch_suggestion( lifetime_name: &str, infos: &[&Info<'_>], ) -> lints::MismatchedLifetimeSyntaxesSuggestion { - let lifetime_name_sugg = lifetime_name.to_owned(); + let lifetime_name = lifetime_name.to_owned(); let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect(); lints::MismatchedLifetimeSyntaxesSuggestion::Explicit { - lifetime_name_sugg, + lifetime_name, suggestions, tool_only: false, } @@ -441,6 +520,14 @@ struct Info<'tcx> { } impl<'tcx> Info<'tcx> { + fn syntax_source(&self) -> (hir::LifetimeSyntax, LifetimeSource) { + (self.lifetime.syntax, self.lifetime.source) + } + + fn lifetime_syntax_category(&self) -> Option<LifetimeSyntaxCategory> { + LifetimeSyntaxCategory::new(self.syntax_source()) + } + fn lifetime_name(&self) -> &str { self.lifetime.ident.as_str() } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 21148833eaf..fd8d0f832aa 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -21,6 +21,7 @@ use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; +use crate::lifetime_syntax::LifetimeSyntaxCategories; use crate::{LateContext, fluent_generated as fluent}; // array_into_iter.rs @@ -1747,6 +1748,20 @@ pub(crate) struct OverflowingLiteral<'a> { } #[derive(LintDiagnostic)] +#[diag(lint_surrogate_char_cast)] +#[note] +pub(crate) struct SurrogateCharCast { + pub literal: u128, +} + +#[derive(LintDiagnostic)] +#[diag(lint_too_large_char_cast)] +#[note] +pub(crate) struct TooLargeCharCast { + pub literal: u128, +} + +#[derive(LintDiagnostic)] #[diag(lint_uses_power_alignment)] pub(crate) struct UsesPowerAlignment; @@ -3081,6 +3096,14 @@ pub(crate) struct HiddenGlobReexports { } #[derive(LintDiagnostic)] +#[diag(lint_reexport_private_dependency)] +pub(crate) struct ReexportPrivateDependency { + pub name: String, + pub kind: String, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] #[diag(lint_unnecessary_qualification)] pub(crate) struct UnusedQualifications { #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] @@ -3194,31 +3217,60 @@ pub(crate) struct ReservedMultihash { #[derive(Debug)] pub(crate) struct MismatchedLifetimeSyntaxes { - pub lifetime_name: String, - pub inputs: Vec<Span>, - pub outputs: Vec<Span>, + pub inputs: LifetimeSyntaxCategories<Vec<Span>>, + pub outputs: LifetimeSyntaxCategories<Vec<Span>>, pub suggestions: Vec<MismatchedLifetimeSyntaxesSuggestion>, } impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSyntaxes { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { - diag.primary_message(fluent::lint_mismatched_lifetime_syntaxes); + let counts = self.inputs.len() + self.outputs.len(); + let message = match counts { + LifetimeSyntaxCategories { hidden: 0, elided: 0, named: 0 } => { + panic!("No lifetime mismatch detected") + } - diag.arg("lifetime_name", self.lifetime_name); + LifetimeSyntaxCategories { hidden: _, elided: _, named: 0 } => { + fluent::lint_mismatched_lifetime_syntaxes_hiding_while_elided + } + + LifetimeSyntaxCategories { hidden: _, elided: 0, named: _ } => { + fluent::lint_mismatched_lifetime_syntaxes_hiding_while_named + } + + LifetimeSyntaxCategories { hidden: 0, elided: _, named: _ } => { + fluent::lint_mismatched_lifetime_syntaxes_eliding_while_named + } + + LifetimeSyntaxCategories { hidden: _, elided: _, named: _ } => { + fluent::lint_mismatched_lifetime_syntaxes_hiding_and_eliding_while_named + } + }; + diag.primary_message(message); - diag.arg("n_inputs", self.inputs.len()); - for input in self.inputs { - let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_inputs); - diag.span_label(input, a); + for s in self.inputs.hidden { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_hidden); + } + for s in self.inputs.elided { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_elided); + } + for s in self.inputs.named { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_input_named); } - diag.arg("n_outputs", self.outputs.len()); - for output in self.outputs { - let a = diag.eagerly_translate(fluent::lint_label_mismatched_lifetime_syntaxes_outputs); - diag.span_label(output, a); + for s in self.outputs.hidden { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_hidden); + } + for s in self.outputs.elided { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_elided); + } + for s in self.outputs.named { + diag.span_label(s, fluent::lint_mismatched_lifetime_syntaxes_output_named); } + diag.help(fluent::lint_mismatched_lifetime_syntaxes_help); + let mut suggestions = self.suggestions.into_iter(); if let Some(s) = suggestions.next() { diag.subdiagnostic(s); @@ -3245,7 +3297,7 @@ pub(crate) enum MismatchedLifetimeSyntaxesSuggestion { }, Explicit { - lifetime_name_sugg: String, + lifetime_name: String, suggestions: Vec<(Span, String)>, tool_only: bool, }, @@ -3285,6 +3337,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => { + let message = if implicit_suggestions.is_empty() { + fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths + } else { + fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed + }; + let implicit_suggestions = implicit_suggestions.into_iter().map(|s| (s, String::new())); @@ -3292,19 +3350,19 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { implicit_suggestions.chain(explicit_anonymous_suggestions).collect(); diag.multipart_suggestion_with_style( - fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed, + message, suggestions, Applicability::MaybeIncorrect, style(tool_only), ); } - Explicit { lifetime_name_sugg, suggestions, tool_only } => { - diag.arg("lifetime_name_sugg", lifetime_name_sugg); + Explicit { lifetime_name, suggestions, tool_only } => { + diag.arg("lifetime_name", lifetime_name); let msg = diag.eagerly_translate( fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit, ); - diag.remove_arg("lifetime_name_sugg"); + diag.remove_arg("lifetime_name"); diag.multipart_suggestion_with_style( msg, suggestions, diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 3cc55eaa0f2..5513c703f1d 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { let def_id = item.owner_id.to_def_id(); // NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because // the latter will report `where_clause_object_safety` lint. - if let hir::ItemKind::Trait(_, _, ident, ..) = item.kind + if let hir::ItemKind::Trait(_, _, _, ident, ..) = item.kind && cx.tcx.is_dyn_compatible(def_id) { let direct_super_traits_iter = cx diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index d44f45177bd..2bac58ba23d 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -12,7 +12,7 @@ use crate::context::LintContext; use crate::lints::{ OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, - RangeEndpointOutOfRange, UseInclusiveRange, + RangeEndpointOutOfRange, SurrogateCharCast, TooLargeCharCast, UseInclusiveRange, }; use crate::types::{OVERFLOWING_LITERALS, TypeLimits}; @@ -38,12 +38,18 @@ fn lint_overflowing_range_endpoint<'tcx>( // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { return false }; - let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; + let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { + return false; + }; + let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { + return false; + }; if !is_range_literal(struct_expr) { return false; }; - let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; + let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { + return false; + }; // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is @@ -61,7 +67,9 @@ fn lint_overflowing_range_endpoint<'tcx>( }; let sub_sugg = if span.lo() == lit_span.lo() { - let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; + let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { + return false; + }; UseInclusiveRange::WithoutParen { sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), start, @@ -316,11 +324,25 @@ fn lint_uint_literal<'tcx>( match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - par_e.span, - OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, - ); + if lit_val > 0x10FFFF { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + TooLargeCharCast { literal: lit_val }, + ); + } else if (0xD800..=0xDFFF).contains(&lit_val) { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + SurrogateCharCast { literal: lit_val }, + ); + } else { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, + ); + } return; } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index cd402c9234f..fe068d96b74 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -739,6 +739,11 @@ pub enum BuiltinLintDiag { /// The local binding that shadows the glob reexport. private_item_span: Span, }, + ReexportPrivateDependency { + name: String, + kind: String, + krate: Symbol, + }, UnusedQualifications { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 19954459cb5..57a672c45f7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -3,7 +3,7 @@ use std::mem; use std::sync::Arc; use rustc_attr_data_structures::Deprecation; -use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::arena::ArenaAllocatable; @@ -510,10 +510,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } Entry::Vacant(entry) => { entry.insert(parent); - if matches!( - child.res, - Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, _) - ) { + if child.res.module_like_def_id().is_some() { bfs_queue.push_back(def_id); } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b50453cb0df..5cd98038fc6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1131,7 +1131,7 @@ fn should_encode_mir( && reachable_set.contains(&def_id) && (generics.requires_monomorphization(tcx) || tcx.cross_crate_inlinable(def_id))); - // The function has a `const` modifier or is in a `#[const_trait]`. + // The function has a `const` modifier or is in a `const trait`. let is_const_fn = tcx.is_const_fn(def_id.to_def_id()) || tcx.is_const_default_method(def_id.to_def_id()); (is_const_fn, opt) diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 84710e5e636..42a1e7377f4 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -940,7 +940,7 @@ impl<'tcx> TyCtxt<'tcx> { }) => until_within(*outer_span, ty.span), // With generics and bounds. Node::Item(Item { - kind: ItemKind::Trait(_, _, _, generics, bounds, _), + kind: ItemKind::Trait(_, _, _, _, generics, bounds, _), span: outer_span, .. }) diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 2bbc48b633c..4fe4c2dadee 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> { #[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec<QueryOutlivesConstraint<'tcx>>, + pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, } impl QueryRegionConstraints<'_> { - /// Represents an empty (trivially true) set of region - /// constraints. + /// Represents an empty (trivially true) set of region constraints. + /// + /// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions? + /// Because I don't expect for us to get cases where an assumption from one query would + /// discharge a requirement from another query, which is a potential problem if we did throw + /// away these assumptions because there were no constraints. pub fn is_empty(&self) -> bool { - self.outlives.is_empty() + self.outlives.is_empty() && self.assumptions.is_empty() } } @@ -130,8 +135,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } } -pub type QueryOutlivesConstraint<'tcx> = - (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>); +pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>); #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 2f16d385efb..6eae3b51e29 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use rustc_abi::Align; use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr}; @@ -6,6 +8,26 @@ use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use crate::mir::mono::Linkage; +use crate::ty::{InstanceKind, TyCtxt}; + +impl<'tcx> TyCtxt<'tcx> { + pub fn codegen_instance_attrs( + self, + instance_kind: InstanceKind<'_>, + ) -> Cow<'tcx, CodegenFnAttrs> { + let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id())); + + // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that + // are generated for indirect function calls. + if !matches!(instance_kind, InstanceKind::Item(_)) { + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED); + } + } + + attrs + } +} #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct CodegenFnAttrs { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 71e0c943fbb..3e68afbfabd 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -257,7 +257,7 @@ pub enum InvalidProgramInfo<'tcx> { /// Details of why a pointer had to be in-bounds. #[derive(Debug, Copy, Clone)] pub enum CheckInAllocMsg { - /// We are access memory. + /// We are accessing memory. MemoryAccess, /// We are doing pointer arithmetic. InboundsPointerArithmetic, diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 47ba850d50d..2d7ddd105bd 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -152,7 +152,7 @@ impl<'tcx> MonoItem<'tcx> { // If the function is #[naked] or contains any other attribute that requires exactly-once // instantiation: // We emit an unused_attributes lint for this case, which should be kept in sync if possible. - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def); if codegen_fn_attrs.contains_extern_indicator() || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { @@ -219,7 +219,7 @@ impl<'tcx> MonoItem<'tcx> { // functions the same as those that unconditionally get LocalCopy codegen. It's only when // we get here that we can at least not codegen a #[inline(never)] generic function in all // of our CGUs. - if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline + if let InlineAttr::Never = codegen_fn_attrs.inline && self.is_generic_fn() { return InstantiationMode::GloballyShared { may_conflict: true }; @@ -234,14 +234,13 @@ impl<'tcx> MonoItem<'tcx> { } pub fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option<Linkage> { - let def_id = match *self { - MonoItem::Fn(ref instance) => instance.def_id(), - MonoItem::Static(def_id) => def_id, + let instance_kind = match *self { + MonoItem::Fn(ref instance) => instance.def, + MonoItem::Static(def_id) => InstanceKind::Item(def_id), MonoItem::GlobalAsm(..) => return None, }; - let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); - codegen_fn_attrs.linkage + tcx.codegen_instance_attrs(instance_kind).linkage } /// Returns `true` if this instance is instantiable - whether it has no unsatisfied diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 935cc889574..d5c2b6de4ae 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -988,7 +988,7 @@ rustc_queries! { } query coroutine_hidden_types( - def_id: DefId + def_id: DefId, ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> { desc { "looking up the hidden types stored across await points in a coroutine" } } @@ -1505,6 +1505,15 @@ rustc_queries! { separate_provide_extern } + /// Returns the `CodegenFnAttrs` for the item at `def_id`. + /// + /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the + /// instance kind into account. + /// + /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`, + /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`. + /// Using this query would include the attribute regardless of the actual instance + /// kind at the call site. query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs { desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } arena_cache diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 98b2ce01d89..81c13e55dd8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -137,6 +137,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type FnInputTys = &'tcx [Ty<'tcx>]; type ParamTy = ParamTy; type BoundTy = ty::BoundTy; + type Symbol = Symbol; type PlaceholderTy = ty::PlaceholderType; type ErrorGuaranteed = ErrorGuaranteed; @@ -162,6 +163,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundRegion = ty::BoundRegion; type PlaceholderRegion = ty::PlaceholderRegion; + type RegionAssumptions = &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>; + type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; @@ -713,17 +716,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, defining_anchor: Self::LocalDefId, ) -> Self::LocalDefIds { - if self.next_trait_solver_globally() { - let coroutines_defined_by = self - .nested_bodies_within(defining_anchor) - .iter() - .filter(|def_id| self.is_coroutine(def_id.to_def_id())); - self.mk_local_def_ids_from_iter( - self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), - ) - } else { - self.opaque_types_defined_by(defining_anchor) - } + let coroutines_defined_by = self + .nested_bodies_within(defining_anchor) + .iter() + .filter(|def_id| self.is_coroutine(def_id.to_def_id())); + self.mk_local_def_ids_from_iter( + self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by), + ) } } @@ -833,6 +832,13 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu fn associated_const_equality(self) -> bool { self.associated_const_equality() } + + fn feature_bound_holds_in_crate(self, symbol: Symbol) -> bool { + // We don't consider feature bounds to hold in the crate when `staged_api` feature is + // enabled, even if it is enabled through `#[feature]`. + // This is to prevent accidentally leaking unstable APIs to stable. + !self.staged_api() && self.enabled(symbol) + } } impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span { @@ -874,6 +880,7 @@ pub struct CtxtInterners<'tcx> { offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>, patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>, + outlives: InternedSet<'tcx, List<ty::ArgOutlivesPredicate<'tcx>>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -911,6 +918,7 @@ impl<'tcx> CtxtInterners<'tcx> { offset_of: InternedSet::with_capacity(N), valtree: InternedSet::with_capacity(N), patterns: InternedSet::with_capacity(N), + outlives: InternedSet::with_capacity(N), } } @@ -2692,6 +2700,7 @@ slice_interners!( captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), patterns: pub mk_patterns(Pattern<'tcx>), + outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>), ); impl<'tcx> TyCtxt<'tcx> { @@ -3107,6 +3116,17 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs)) } + pub fn mk_outlives_from_iter<I, T>(self, iter: I) -> T::Output + where + I: Iterator<Item = T>, + T: CollectAndApply< + ty::ArgOutlivesPredicate<'tcx>, + &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>, + >, + { + T::collect_and_apply(iter, |xs| self.mk_outlives(xs)) + } + /// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`, /// typically generated by `#[derive(LintDiagnostic)]`). #[track_caller] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6e8f1e8fdd5..a7cde2ad485 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -88,7 +88,7 @@ pub use self::opaque_types::OpaqueTypeKey; pub use self::parameterized::ParameterizedOverTcx; pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ - AliasTerm, Clause, ClauseKind, CoercePredicate, ExistentialPredicate, + AliasTerm, ArgOutlivesPredicate, Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialPredicateStableCmpExt, ExistentialProjection, ExistentialTraitRef, HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index bc2ac42b6b1..46f254e9d30 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -26,6 +26,7 @@ pub type SubtypePredicate<'tcx> = ir::SubtypePredicate<TyCtxt<'tcx>>; pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate<TyCtxt<'tcx>, T>; pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>; pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>; +pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>; pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; @@ -131,6 +132,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) | PredicateKind::Clause(ClauseKind::Projection(_)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::DynCompatible(_) | PredicateKind::Subtype(_) | PredicateKind::Coerce(_) @@ -649,6 +651,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(ClauseKind::Projection(..)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) @@ -670,6 +673,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(ClauseKind::Trait(..)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) | PredicateKind::NormalizesTo(..) | PredicateKind::AliasRelate(..) | PredicateKind::Subtype(..) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7c48a4b0885..2eb530f328d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2430,7 +2430,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } let verbose = self.should_print_verbose(); - disambiguated_data.fmt_maybe_verbose(self, verbose)?; + write!(self, "{}", disambiguated_data.as_sym(verbose))?; self.empty_path = false; @@ -3237,6 +3237,7 @@ define_print! { ty::ClauseKind::ConstEvaluatable(ct) => { p!("the constant `", print(ct), "` can be evaluated") } + ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)), } } @@ -3454,9 +3455,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N collect_fn(&child.ident, ns, def_id); } - if matches!(defkind, DefKind::Mod | DefKind::Enum | DefKind::Trait) - && seen_defs.insert(def_id) - { + if defkind.is_module_like() && seen_defs.insert(def_id) { queue.push(def_id); } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index af9c98bd87d..ab31d943408 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -802,4 +802,5 @@ list_fold! { &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates, &'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems, &'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns, + &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives, } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index ea25ce65f77..59e2b2a034d 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -20,7 +20,7 @@ pub struct TraitDef { pub safety: hir::Safety, - /// Whether this trait has been annotated with `#[const_trait]`. + /// Whether this trait is `const`. pub constness: hir::Constness, /// If `true`, then this trait had the `#[rustc_paren_sugar]` diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index ccf76dc7108..986c001de5e 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,3 +1,4 @@ +use rustc_attr_data_structures::{AttributeKind, CoverageStatus, 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}; @@ -5,7 +6,6 @@ use rustc_middle::mir::{Body, Statement, StatementKind}; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::def_id::LocalDefId; -use rustc_span::sym; use tracing::trace; use crate::coverage::counters::node_flow::make_node_counters; @@ -58,26 +58,20 @@ 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(attr) = tcx.get_attr(def_id, sym::coverage) { - match attr.meta_item_list().as_deref() { - Some([item]) if item.has_name(sym::off) => return false, - Some([item]) if item.has_name(sym::on) => return true, - Some(_) | None => { - // Other possibilities should have been rejected by `rustc_parse::validate_attr`. - // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). - tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute"); - } + if let Some(coverage_status) = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status) + { + *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 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, - } } /// Query implementation for `coverage_ids_info`. diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 6b11706d2b5..07717b7c069 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -105,7 +105,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::layout::HasTypingEnv; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use rustc_span::def_id::DefId; use smallvec::SmallVec; use tracing::{debug, instrument, trace}; @@ -130,7 +129,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { - let opaque = state.new_opaque(); + let opaque = state.new_opaque(body.local_decls[local].ty); state.assign(local, opaque); } @@ -155,22 +154,6 @@ newtype_index! { struct VnIndex {} } -/// Computing the aggregate's type can be quite slow, so we only keep the minimal amount of -/// information to reconstruct it when needed. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AggregateTy<'tcx> { - /// Invariant: this must not be used for an empty array. - Array, - Tuple, - Def(DefId, ty::GenericArgsRef<'tcx>), - RawPtr { - /// Needed for cast propagation. - data_pointer_ty: Ty<'tcx>, - /// The data pointer can be anything thin, so doesn't determine the output. - output_pointer_ty: Ty<'tcx>, - }, -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -193,7 +176,14 @@ enum Value<'tcx> { }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. - Aggregate(AggregateTy<'tcx>, VariantIdx, Vec<VnIndex>), + Aggregate(VariantIdx, Vec<VnIndex>), + /// A raw pointer aggregate built from a thin pointer and metadata. + RawPtr { + /// Thin pointer component. This is field 0 in MIR. + pointer: VnIndex, + /// Metadata component. This is field 1 in MIR. + metadata: VnIndex, + }, /// This corresponds to a `[value; count]` expression. Repeat(VnIndex, ty::Const<'tcx>), /// The address of a place. @@ -206,7 +196,7 @@ enum Value<'tcx> { // Extractions. /// This is the *value* obtained by projecting another value. - Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>), + Projection(VnIndex, ProjectionElem<VnIndex, ()>), /// Discriminant of the given value. Discriminant(VnIndex), /// Length of an array or slice. @@ -219,8 +209,6 @@ enum Value<'tcx> { Cast { kind: CastKind, value: VnIndex, - from: Ty<'tcx>, - to: Ty<'tcx>, }, } @@ -228,12 +216,13 @@ struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, local_decls: &'body LocalDecls<'tcx>, + is_coroutine: bool, /// Value stored in each local. locals: IndexVec<Local, Option<VnIndex>>, /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>, - values: FxIndexSet<Value<'tcx>>, + values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, /// Values evaluated as constants if possible. evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>, /// Counter to generate different values. @@ -265,6 +254,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { tcx, ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine), local_decls, + is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), @@ -282,8 +272,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn insert(&mut self, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full(value); + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { + let (index, new) = self.values.insert_full((value, ty)); let index = VnIndex::from_usize(index); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. @@ -305,20 +295,33 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_opaque(&mut self) -> VnIndex { + fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { let value = Value::Opaque(self.next_opaque()); - self.insert(value) + self.insert(ty, value) } /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex { + let pty = place.ty(self.local_decls, self.tcx).ty; + let ty = match kind { + AddressKind::Ref(bk) => { + Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy()) + } + AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), + }; let value = Value::Address { place, kind, provenance: self.next_opaque() }; - self.insert(value) + self.insert(ty, value) } + #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - self.values.get_index(index.as_usize()).unwrap() + &self.values.get_index(index.as_usize()).unwrap().0 + } + + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.values.get_index(index.as_usize()).unwrap().1 } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -341,29 +344,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { debug_assert_ne!(disambiguator, 0); disambiguator }; - self.insert(Value::Constant { value, disambiguator }) + self.insert(value.ty(), Value::Constant { value, disambiguator }) } fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) } - fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex { + fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: 0 }) } - fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex { - self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) + fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex { + self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values)) } - fn insert_deref(&mut self, value: VnIndex) -> VnIndex { - let value = self.insert(Value::Projection(value, ProjectionElem::Deref)); + fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { + let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref)); self.derefs.push(value); value } @@ -371,14 +374,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { let opaque = self.next_opaque(); - *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque); + self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); } } #[instrument(level = "trace", skip(self), ret)] fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> { use Value::*; + let ty = self.ty(value); + // Avoid computing layouts inside a coroutine, as that can cause cycles. + let ty = if !self.is_coroutine || ty.is_scalar() { + self.ecx.layout_of(ty).ok()? + } else { + return None; + }; let op = match *self.get(value) { + _ if ty.is_zst() => ImmTy::uninit(ty).into(), + Opaque(_) => return None, // Do not bother evaluating repeat expressions. This would uselessly consume memory. Repeat(..) => return None, @@ -386,42 +398,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Constant { ref value, disambiguator: _ } => { self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } - Aggregate(kind, variant, ref fields) => { + Aggregate(variant, ref fields) => { let fields = fields .iter() .map(|&f| self.evaluated[f].as_ref()) .collect::<Option<Vec<_>>>()?; - let ty = match kind { - AggregateTy::Array => { - assert!(fields.len() > 0); - Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64) - } - AggregateTy::Tuple => { - Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty)) - } - AggregateTy::Def(def_id, args) => { - self.tcx.type_of(def_id).instantiate(self.tcx, args) - } - AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, - }; - let variant = if ty.is_enum() { Some(variant) } else { None }; - let ty = self.ecx.layout_of(ty).ok()?; - if ty.is_zst() { - ImmTy::uninit(ty).into() - } else if matches!(kind, AggregateTy::RawPtr { .. }) { - // Pointers don't have fields, so don't `project_field` them. - let data = self.ecx.read_pointer(fields[0]).discard_err()?; - let meta = if fields[1].layout.is_zst() { - MemPlaceMeta::None - } else { - MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?) - }; - let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); - ImmTy::from_immediate(ptr_imm, ty).into() - } else if matches!( - ty.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) - ) { + let variant = if ty.ty.is_enum() { Some(variant) } else { None }; + if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + { let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; let variant_dest = if let Some(variant) = variant { self.ecx.project_downcast(&dest, variant).discard_err()? @@ -446,32 +430,46 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } + RawPtr { pointer, metadata } => { + let pointer = self.evaluated[pointer].as_ref()?; + let metadata = self.evaluated[metadata].as_ref()?; + + // Pointers don't have fields, so don't `project_field` them. + let data = self.ecx.read_pointer(pointer).discard_err()?; + let meta = if metadata.layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); + ImmTy::from_immediate(ptr_imm, ty).into() + } Projection(base, elem) => { - let value = self.evaluated[base].as_ref()?; + let base = self.evaluated[base].as_ref()?; let elem = match elem { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Downcast(name, read_variant) => { ProjectionElem::Downcast(name, read_variant) } - ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty), + ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty), ProjectionElem::ConstantIndex { offset, min_length, from_end } => { ProjectionElem::ConstantIndex { offset, min_length, from_end } } ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), - ProjectionElem::UnwrapUnsafeBinder(ty) => { - ProjectionElem::UnwrapUnsafeBinder(ty) + ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty), + ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty), + ProjectionElem::UnwrapUnsafeBinder(()) => { + ProjectionElem::UnwrapUnsafeBinder(ty.ty) } // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; - self.ecx.project(value, elem).discard_err()? + self.ecx.project(base, elem).discard_err()? } - Address { place, kind, provenance: _ } => { + Address { place, kind: _, provenance: _ } => { if !place.is_indirect_first_projection() { return None; } @@ -487,19 +485,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { mplace = self.ecx.project(&mplace, proj).discard_err()?; } let pointer = mplace.to_ref(&self.ecx); - let ty = match kind { - AddressKind::Ref(bk) => Ty::new_ref( - self.tcx, - self.tcx.lifetimes.re_erased, - mplace.layout.ty, - bk.to_mutbl_lossy(), - ), - AddressKind::Address(mutbl) => { - Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy()) - } - }; - let layout = self.ecx.layout_of(ty).ok()?; - ImmTy::from_immediate(pointer, layout).into() + ImmTy::from_immediate(pointer, ty).into() } Discriminant(base) => { @@ -511,32 +497,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } Len(slice) => { let slice = self.evaluated[slice].as_ref()?; - let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let len = slice.len(&self.ecx).discard_err()?; - let imm = ImmTy::from_uint(len, usize_layout); - imm.into() + ImmTy::from_uint(len, ty).into() } - NullaryOp(null_op, ty) => { - let layout = self.ecx.layout_of(ty).ok()?; + NullaryOp(null_op, arg_ty) => { + let arg_layout = self.ecx.layout_of(arg_ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op - && layout.is_unsized() + && arg_layout.is_unsized() { return None; } let val = match null_op { - NullOp::SizeOf => layout.size.bytes(), - NullOp::AlignOf => layout.align.abi.bytes(), + NullOp::SizeOf => arg_layout.size.bytes(), + NullOp::AlignOf => arg_layout.align.abi.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx - .offset_of_subfield(self.typing_env(), layout, fields.iter()) + .offset_of_subfield(self.typing_env(), arg_layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, NullOp::ContractChecks => return None, }; - let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let imm = ImmTy::from_uint(val, usize_layout); - imm.into() + ImmTy::from_uint(val, ty).into() } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; @@ -552,30 +534,27 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } - Cast { kind, value, from: _, to } => match kind { + Cast { kind, value } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; let value = self.ecx.read_immediate(value).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; + let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.evaluated[value].as_ref()?; let value = self.ecx.read_immediate(value).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; + let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?; res.into() } CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do // limited transmutes: it only works between types with the same layout, and // cannot transmute pointers to integers. if value.as_mplace_or_imm().is_right() { - let can_transmute = match (value.layout.backend_repr, to.backend_repr) { + let can_transmute = match (value.layout.backend_repr, ty.backend_repr) { (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => { s1.size(&self.ecx) == s2.size(&self.ecx) && !matches!(s1.primitive(), Primitive::Pointer(..)) @@ -595,13 +574,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } - value.offset(Size::ZERO, to, &self.ecx).discard_err()? + value.offset(Size::ZERO, ty, &self.ecx).discard_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; - let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?; - self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?; + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; + self.ecx.unsize_into(src, ty, &dest.clone().into()).discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) .discard_err()?; @@ -610,15 +588,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { CastKind::FnPtrToPtr | CastKind::PtrToPtr => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?; + let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - ImmTy::from_immediate(*src, to).into() + ImmTy::from_immediate(*src, ty).into() } _ => return None, }, @@ -628,31 +604,30 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn project( &mut self, - place: PlaceRef<'tcx>, + place_ty: PlaceTy<'tcx>, value: VnIndex, proj: PlaceElem<'tcx>, from_non_ssa_index: &mut bool, - ) -> Option<VnIndex> { + ) -> Option<(PlaceTy<'tcx>, VnIndex)> { + let projection_ty = place_ty.projection_ty(self.tcx, proj); let proj = match proj { ProjectionElem::Deref => { - let ty = place.ty(self.local_decls, self.tcx).ty; - if let Some(Mutability::Not) = ty.ref_mutability() - && let Some(pointee_ty) = ty.builtin_deref(true) - && pointee_ty.is_freeze(self.tcx, self.typing_env()) + if let Some(Mutability::Not) = place_ty.ty.ref_mutability() + && projection_ty.ty.is_freeze(self.tcx, self.typing_env()) { // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. - return Some(self.insert_deref(value)); + return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); } else { return None; } } ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), - ProjectionElem::Field(f, ty) => { - if let Value::Aggregate(_, _, fields) = self.get(value) { - return Some(fields[f.as_usize()]); + ProjectionElem::Field(f, _) => { + if let Value::Aggregate(_, fields) = self.get(value) { + return Some((projection_ty, fields[f.as_usize()])); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value) + && let Value::Aggregate(written_variant, fields) = self.get(*outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -670,14 +645,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // a downcast to an inactive variant. && written_variant == read_variant { - return Some(fields[f.as_usize()]); + return Some((projection_ty, fields[f.as_usize()])); } - ProjectionElem::Field(f, ty) + ProjectionElem::Field(f, ()) } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { *from_non_ssa_index |= self.locals[idx].is_none(); - return Some(*inner); + return Some((projection_ty, *inner)); } let idx = self.locals[idx]?; ProjectionElem::Index(idx) @@ -685,15 +660,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { match self.get(value) { Value::Repeat(inner, _) => { - return Some(*inner); + return Some((projection_ty, *inner)); } - Value::Aggregate(AggregateTy::Array, _, operands) => { + Value::Aggregate(_, operands) => { let offset = if from_end { operands.len() - offset as usize } else { offset as usize }; - return operands.get(offset).copied(); + let value = operands.get(offset).copied()?; + return Some((projection_ty, value)); } _ => {} }; @@ -702,12 +678,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), - ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), + ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()), + ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()), + ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()), }; - Some(self.insert(Value::Projection(value, proj))) + let value = self.insert(projection_ty.ty, Value::Projection(value, proj)); + Some((projection_ty, value)) } /// Simplify the projection chain if we know better. @@ -769,6 +746,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Invariant: `value` holds the value up-to the `index`th projection excluded. let mut value = self.locals[place.local]?; + // Invariant: `value` has type `place_ty`, with optional downcast variant if needed. + let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -786,8 +765,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place_ref = PlaceRef { local, projection: &place.projection[index..] }; } - let base = PlaceRef { local: place.local, projection: &place.projection[..index] }; - value = self.project(base, value, proj, &mut from_non_ssa_index)?; + (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; } if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -864,14 +842,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.simplify_place_projection(place, location); return Some(self.new_pointer(*place, AddressKind::Address(mutbl))); } - Rvalue::WrapUnsafeBinder(ref mut op, ty) => { + Rvalue::WrapUnsafeBinder(ref mut op, _) => { let value = self.simplify_operand(op, location)?; - Value::Cast { - kind: CastKind::Transmute, - value, - from: op.ty(self.local_decls, self.tcx), - to: ty, - } + Value::Cast { kind: CastKind::Transmute, value } } // Operations. @@ -896,18 +869,17 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Unsupported values. Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None, }; - debug!(?value); - Some(self.insert(value)) + let ty = rvalue.ty(self.local_decls, self.tcx); + Some(self.insert(ty, value)) } fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> { - if let Value::Aggregate(enum_ty, variant, _) = *self.get(place) - && let AggregateTy::Def(enum_did, enum_args) = enum_ty - && let DefKind::Enum = self.tcx.def_kind(enum_did) + let enum_ty = self.ty(place); + if enum_ty.is_enum() + && let Value::Aggregate(variant, _) = *self.get(place) { - let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; - return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty)); + return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar())); } None @@ -915,12 +887,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn try_as_place_elem( &mut self, - proj: ProjectionElem<VnIndex, Ty<'tcx>>, + ty: Ty<'tcx>, + proj: ProjectionElem<VnIndex, ()>, loc: Location, ) -> Option<PlaceElem<'tcx>> { Some(match proj { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty), + ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty), ProjectionElem::Index(idx) => { let Some(local) = self.try_as_local(idx, loc) else { return None; @@ -935,9 +908,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Subslice { from, to, from_end } } ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx), - ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx), - ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx), - ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), + ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty), + ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty), }) } @@ -983,8 +956,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Allow introducing places with non-constant offsets, as those are still better than // reconstructing an aggregate. - if let Some(place) = self.try_as_place(copy_from_local_value, location, true) - && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx) + && let Some(place) = self.try_as_place(copy_from_local_value, location, true) { // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. @@ -1004,9 +977,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option<VnIndex> { + let tcx = self.tcx; + let ty = rvalue.ty(self.local_decls, tcx); + let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() }; - let tcx = self.tcx; if field_ops.is_empty() { let is_zst = match *kind { AggregateKind::Array(..) @@ -1021,87 +996,72 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }; if is_zst { - let ty = rvalue.ty(self.local_decls, tcx); return Some(self.insert_constant(Const::zero_sized(ty))); } } - let (mut ty, variant_index) = match *kind { - AggregateKind::Array(..) => { - assert!(!field_ops.is_empty()); - (AggregateTy::Array, FIRST_VARIANT) - } - AggregateKind::Tuple => { + let fields: Vec<_> = field_ops + .iter_mut() + .map(|op| { + self.simplify_operand(op, location) + .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) + }) + .collect(); + + let variant_index = match *kind { + AggregateKind::Array(..) | AggregateKind::Tuple => { assert!(!field_ops.is_empty()); - (AggregateTy::Tuple, FIRST_VARIANT) - } - AggregateKind::Closure(did, args) - | AggregateKind::CoroutineClosure(did, args) - | AggregateKind::Coroutine(did, args) => (AggregateTy::Def(did, args), FIRST_VARIANT), - AggregateKind::Adt(did, variant_index, args, _, None) => { - (AggregateTy::Def(did, args), variant_index) + FIRST_VARIANT } + AggregateKind::Closure(..) + | AggregateKind::CoroutineClosure(..) + | AggregateKind::Coroutine(..) => FIRST_VARIANT, + AggregateKind::Adt(_, variant_index, _, _, None) => variant_index, // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, - AggregateKind::RawPtr(pointee_ty, mtbl) => { + AggregateKind::RawPtr(..) => { assert_eq!(field_ops.len(), 2); - let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); - let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl); - (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT) - } - }; - - let mut fields: Vec<_> = field_ops - .iter_mut() - .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque())) - .collect(); - - if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty { - let mut was_updated = false; + let [mut pointer, metadata] = fields.try_into().unwrap(); + + // Any thin pointer of matching mutability is fine as the data pointer. + let mut was_updated = false; + while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } = + self.get(pointer) + && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind() + && let ty::RawPtr(_, output_mtbl) = ty.kind() + && from_mtbl == output_mtbl + && from_pointee_ty.is_sized(self.tcx, self.typing_env()) + { + pointer = *cast_value; + was_updated = true; + } - // Any thin pointer of matching mutability is fine as the data pointer. - while let Value::Cast { - kind: CastKind::PtrToPtr, - value: cast_value, - from: cast_from, - to: _, - } = self.get(fields[0]) - && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() - && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind() - && from_mtbl == output_mtbl - && from_pointee_ty.is_sized(self.tcx, self.typing_env()) - { - fields[0] = *cast_value; - *data_pointer_ty = *cast_from; - was_updated = true; - } + if was_updated && let Some(op) = self.try_as_operand(pointer, location) { + field_ops[FieldIdx::ZERO] = op; + } - if was_updated && let Some(op) = self.try_as_operand(fields[0], location) { - field_ops[FieldIdx::ZERO] = op; + return Some(self.insert(ty, Value::RawPtr { pointer, metadata })); } - } + }; - if let AggregateTy::Array = ty - && fields.len() > 4 - { + if ty.is_array() && fields.len() > 4 { let first = fields[0]; if fields.iter().all(|&v| v == first) { let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); if let Some(op) = self.try_as_operand(first, location) { *rvalue = Rvalue::Repeat(op, len); } - return Some(self.insert(Value::Repeat(first, len))); + return Some(self.insert(ty, Value::Repeat(first, len))); } } - if let AggregateTy::Def(_, _) = ty - && let Some(value) = - self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) + if let Some(value) = + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } - Some(self.insert(Value::Aggregate(ty, variant_index, fields))) + Some(self.insert(ty, Value::Aggregate(variant_index, fields))) } #[instrument(level = "trace", skip(self), ret)] @@ -1112,6 +1072,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { location: Location, ) -> Option<VnIndex> { let mut arg_index = self.simplify_operand(arg_op, location)?; + let arg_ty = self.ty(arg_index); + let ret_ty = op.ty(self.tcx, arg_ty); // PtrMetadata doesn't care about *const vs *mut vs & vs &mut, // so start by removing those distinctions so we can update the `Operand` @@ -1127,8 +1089,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // we can't always know exactly what the metadata are. // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`, // it's fine to get a projection as the type. - Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to } - if self.pointers_have_same_metadata(*from, *to) => + Value::Cast { kind: CastKind::PtrToPtr, value: inner } + if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => { arg_index = *inner; was_updated = true; @@ -1165,26 +1127,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { Value::BinaryOp(BinOp::Eq, *lhs, *rhs) } - (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => { - return Some(fields[1]); - } + (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata), // We have an unsizing cast, which assigns the length to wide pointer metadata. ( UnOp::PtrMetadata, Value::Cast { kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), - from, - to, - .. + value: inner, }, - ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() - && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => + ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind() + && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() => { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } _ => Value::UnaryOp(op, arg_index), }; - Some(self.insert(value)) + Some(self.insert(ret_ty, value)) } #[instrument(level = "trace", skip(self), ret)] @@ -1197,25 +1155,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ) -> Option<VnIndex> { let lhs = self.simplify_operand(lhs_operand, location); let rhs = self.simplify_operand(rhs_operand, location); + // Only short-circuit options after we called `simplify_operand` // on both operands for side effect. let mut lhs = lhs?; let mut rhs = rhs?; - let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx); + let lhs_ty = self.ty(lhs); // If we're comparing pointers, remove `PtrToPtr` casts if the from // types of both casts and the metadata all match. if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op && lhs_ty.is_any_ptr() - && let Value::Cast { - kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, .. - } = self.get(lhs) - && let Value::Cast { - kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, .. - } = self.get(rhs) - && lhs_from == rhs_from - && self.pointers_have_same_metadata(*lhs_from, lhs_ty) + && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs) + && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs) + && let lhs_from = self.ty(*lhs_value) + && lhs_from == self.ty(*rhs_value) + && self.pointers_have_same_metadata(lhs_from, lhs_ty) { lhs = *lhs_value; rhs = *rhs_value; @@ -1230,8 +1186,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) { return Some(value); } + let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs)); let value = Value::BinaryOp(op, lhs, rhs); - Some(self.insert(value)) + Some(self.insert(ty, value)) } fn simplify_binary_inner( @@ -1323,19 +1280,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { | BinOp::Shr, Left(0), _, - ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty), + ) => self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)), // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`. (BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _) if ones == layout.size.truncate(u128::MAX) || (layout.ty.is_bool() && ones == 1) => { - self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty) + self.insert_scalar(lhs_ty, Scalar::from_uint(ones, layout.size)) } // Sub/Xor with itself. (BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b) if a == b => { - self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty) + self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)) } // Comparison: // - if both operands can be computed as bits, just compare the bits; @@ -1349,8 +1306,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }; if op.is_overflowing() { + let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]); let false_val = self.insert_bool(false); - Some(self.insert_tuple(vec![result, false_val])) + Some(self.insert_tuple(ty, vec![result, false_val])) } else { Some(result) } @@ -1366,9 +1324,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { use CastKind::*; use rustc_middle::ty::adjustment::PointerCoercion::*; - let mut from = initial_operand.ty(self.local_decls, self.tcx); let mut kind = *initial_kind; let mut value = self.simplify_operand(initial_operand, location)?; + let mut from = self.ty(value); if from == to { return Some(value); } @@ -1376,7 +1334,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind { // Each reification of a generic fn may get a different pointer. // Do not try to merge them. - return Some(self.new_opaque()); + return Some(self.new_opaque(to)); } let mut was_ever_updated = false; @@ -1399,23 +1357,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // If a cast just casts away the metadata again, then we can get it by // casting the original thin pointer passed to `from_raw_parts` if let PtrToPtr = kind - && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = - self.get(value) + && let Value::RawPtr { pointer, .. } = self.get(value) && let ty::RawPtr(to_pointee, _) = to.kind() && to_pointee.is_sized(self.tcx, self.typing_env()) { - from = *data_pointer_ty; - value = fields[0]; + from = self.ty(*pointer); + value = *pointer; was_updated_this_iteration = true; - if *data_pointer_ty == to { - return Some(fields[0]); + if from == to { + return Some(*pointer); } } // Aggregate-then-Transmute can just transmute the original field value, // so long as the bytes of a value from only from a single field. if let Transmute = kind - && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value) + && let Value::Aggregate(variant_idx, field_values) = self.get(value) && let Some((field_idx, field_ty)) = self.value_is_all_in_one_field(from, *variant_idx) { @@ -1428,13 +1385,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Various cast-then-cast cases can be simplified. - if let Value::Cast { - kind: inner_kind, - value: inner_value, - from: inner_from, - to: inner_to, - } = *self.get(value) - { + if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) { + let inner_from = self.ty(inner_value); let new_kind = match (inner_kind, kind) { // Even if there's a narrowing cast in here that's fine, because // things like `*mut [i32] -> *mut i32 -> *const i32` and @@ -1443,9 +1395,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity: // `*const T -> *mut T -> NonNull<T>` is fine, but we need to check for narrowing // to skip things like `*const [i32] -> *const i32 -> NonNull<T>`. - (PtrToPtr, Transmute) - if self.pointers_have_same_metadata(inner_from, inner_to) => - { + (PtrToPtr, Transmute) if self.pointers_have_same_metadata(inner_from, from) => { Some(Transmute) } // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different @@ -1456,7 +1406,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // If would be legal to always do this, but we don't want to hide information // from the backend that it'd otherwise be able to use for optimizations. (Transmute, Transmute) - if !self.type_may_have_niche_of_interest_to_backend(inner_to) => + if !self.type_may_have_niche_of_interest_to_backend(from) => { Some(Transmute) } @@ -1485,7 +1435,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { *initial_kind = kind; } - Some(self.insert(Value::Cast { kind, value, from, to })) + Some(self.insert(to, Value::Cast { kind, value })) } fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> { @@ -1507,18 +1457,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // We have an unsizing cast, which assigns the length to wide pointer metadata. - if let Value::Cast { kind, from, to, .. } = self.get(inner) + if let Value::Cast { kind, value: from } = self.get(inner) && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind - && let Some(from) = from.builtin_deref(true) + && let Some(from) = self.ty(*from).builtin_deref(true) && let ty::Array(_, len) = from.kind() - && let Some(to) = to.builtin_deref(true) + && let Some(to) = self.ty(inner).builtin_deref(true) && let ty::Slice(..) = to.kind() { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } // Fallback: a symbolic `Len`. - Some(self.insert(Value::Len(inner))) + Some(self.insert(self.tcx.types.usize, Value::Len(inner))) } fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { @@ -1727,7 +1677,7 @@ impl<'tcx> VnState<'_, 'tcx> { return Some(place); } else if let Value::Projection(pointer, proj) = *self.get(index) && (allow_complex_projection || proj.is_stable_offset()) - && let Some(proj) = self.try_as_place_elem(proj, loc) + && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) { projection.push(proj); index = pointer; @@ -1755,7 +1705,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { self.simplify_place_projection(place, location); - if context.is_mutating_use() && !place.projection.is_empty() { + if context.is_mutating_use() && place.is_indirect() { // Non-local mutation maybe invalidate deref. self.invalidate_derefs(); } @@ -1767,36 +1717,42 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { self.super_operand(operand, location); } - fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { - if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { - self.simplify_place_projection(lhs, location); - - let value = self.simplify_rvalue(lhs, rvalue, location); - let value = if let Some(local) = lhs.as_local() - && self.ssa.is_ssa(local) - // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark - // `local` as reusable if we have an exact type match. - && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx) + fn visit_assign( + &mut self, + lhs: &mut Place<'tcx>, + rvalue: &mut Rvalue<'tcx>, + location: Location, + ) { + self.simplify_place_projection(lhs, location); + + let value = self.simplify_rvalue(lhs, rvalue, location); + if let Some(value) = value { + if let Some(const_) = self.try_as_constant(value) { + *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); + } else if let Some(place) = self.try_as_place(value, location, false) + && *rvalue != Rvalue::Use(Operand::Move(place)) + && *rvalue != Rvalue::Use(Operand::Copy(place)) { - let value = value.unwrap_or_else(|| self.new_opaque()); - self.assign(local, value); - Some(value) - } else { - value - }; - if let Some(value) = value { - if let Some(const_) = self.try_as_constant(value) { - *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); - } else if let Some(place) = self.try_as_place(value, location, false) - && *rvalue != Rvalue::Use(Operand::Move(place)) - && *rvalue != Rvalue::Use(Operand::Copy(place)) - { - *rvalue = Rvalue::Use(Operand::Copy(place)); - self.reused_locals.insert(place.local); - } + *rvalue = Rvalue::Use(Operand::Copy(place)); + self.reused_locals.insert(place.local); } } - self.super_statement(stmt, location); + + if lhs.is_indirect() { + // Non-local mutation maybe invalidate deref. + self.invalidate_derefs(); + } + + if let Some(local) = lhs.as_local() + && self.ssa.is_ssa(local) + && let rvalue_ty = rvalue.ty(self.local_decls, self.tcx) + // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark + // `local` as reusable if we have an exact type match. + && self.local_decls[local].ty == rvalue_ty + { + let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty)); + self.assign(local, value); + } } fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { @@ -1804,7 +1760,8 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let Some(local) = destination.as_local() && self.ssa.is_ssa(local) { - let opaque = self.new_opaque(); + let ty = self.local_decls[local].ty; + let opaque = self.new_opaque(ty); self.assign(local, opaque); } } 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 b42587618b5..ce9b794d40d 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 @@ -22,6 +22,7 @@ use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; +use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalEvaluationKind, GoalSource, GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, @@ -550,6 +551,9 @@ where ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + self.compute_unstable_feature_goal(param_env, symbol) + } ty::PredicateKind::Subtype(predicate) => { self.compute_subtype_goal(Goal { param_env, predicate }) } @@ -1177,6 +1181,14 @@ where ) -> T { BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0 } + + pub(super) fn may_use_unstable_feature( + &self, + param_env: I::ParamEnv, + symbol: I::Symbol, + ) -> bool { + may_use_unstable_feature(&**self.delegate, param_env, symbol) + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index e68ea22c7a2..5ea3f0d1061 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -148,6 +148,20 @@ where } } + fn compute_unstable_feature_goal( + &mut self, + param_env: <I as Interner>::ParamEnv, + symbol: <I as Interner>::Symbol, + ) -> QueryResult<I> { + if self.may_use_unstable_feature(param_env, symbol) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( + MaybeCause::Ambiguity, + )) + } + } + #[instrument(level = "trace", skip(self))] fn compute_const_evaluatable_goal( &mut self, diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index c4a0ae2ce9d..a92012f8329 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -rustc-literal-escaper = "0.0.4" +rustc-literal-escaper = "0.0.5" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index af9f8735549..859118a4ade 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -855,6 +855,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern .suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` +parse_trait_alias_cannot_be_const = trait aliases cannot be `const` parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe` parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7f1b0991f0c..4aaaba01fae 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1962,6 +1962,14 @@ pub(crate) struct TraitAliasCannotBeAuto { } #[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_be_const)] +pub(crate) struct TraitAliasCannotBeConst { + #[primary_span] + #[label(parse_trait_alias_cannot_be_const)] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_trait_alias_cannot_be_unsafe)] pub(crate) struct TraitAliasCannotBeUnsafe { #[primary_span] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d6cc98d505c..b767b0fcf99 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -244,6 +244,9 @@ impl<'a> Parser<'a> { self.bump(); // `static` let mutability = self.parse_mutability(); self.parse_static_item(safety, mutability)? + } else if self.check_keyword(exp!(Trait)) || self.check_trait_front_matter() { + // TRAIT ITEM + self.parse_item_trait(attrs, lo)? } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { @@ -262,9 +265,6 @@ impl<'a> Parser<'a> { define_opaque: None, })) } - } else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() { - // TRAIT ITEM - self.parse_item_trait(attrs, lo)? } else if self.check_keyword(exp!(Impl)) || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Impl]) { @@ -373,7 +373,7 @@ impl<'a> Parser<'a> { pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` || self.is_reuse_path_item() - || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` + || self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } @@ -872,16 +872,19 @@ impl<'a> Parser<'a> { } } - /// Is this an `(unsafe auto? | auto) trait` item? - fn check_auto_or_unsafe_trait_item(&mut self) -> bool { + /// Is this an `(const unsafe? auto?| unsafe auto? | auto) trait` item? + fn check_trait_front_matter(&mut self) -> bool { // auto trait self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) // unsafe auto trait || self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) + || self.check_keyword(exp!(Const)) && ((self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) + || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Trait, kw::Auto])) } /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> { + let constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(exp!(Auto)) { @@ -913,6 +916,9 @@ impl<'a> Parser<'a> { self.expect_semi()?; let whole_span = lo.to(self.prev_token.span); + if let Const::Yes(_) = constness { + self.dcx().emit_err(errors::TraitAliasCannotBeConst { span: whole_span }); + } if is_auto == IsAuto::Yes { self.dcx().emit_err(errors::TraitAliasCannotBeAuto { span: whole_span }); } @@ -927,7 +933,15 @@ impl<'a> Parser<'a> { // It's a normal trait. generics.where_clause = self.parse_where_clause()?; let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; - Ok(ItemKind::Trait(Box::new(Trait { is_auto, safety, ident, generics, bounds, items }))) + Ok(ItemKind::Trait(Box::new(Trait { + constness, + is_auto, + safety, + ident, + generics, + bounds, + items, + }))) } } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 783d79d978a..a476f0db37e 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -318,6 +318,7 @@ pub fn check_builtin_meta_item( | sym::rustc_layout_scalar_valid_range_end | sym::no_implicit_prelude | sym::automatically_derived + | sym::coverage ) { return; } diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index 0666ae29409..d178fcda1fb 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-literal-escaper = "0.0.4" +rustc-literal-escaper = "0.0.5" rustc_lexer = { path = "../rustc_lexer" } # tidy-alphabetical-end diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3418d997b83..d1b856ca415 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -667,6 +667,10 @@ passes_rustc_std_internal_symbol = attribute should be applied to functions or statics .label = not a function or static +passes_rustc_unstable_feature_bound = + attribute should be applied to `impl` or free function outside of any `impl` or trait + .label = not an `impl` or free function + passes_should_be_applied_to_fn = attribute should be applied to a function definition .label = {$on_crate -> @@ -780,7 +784,7 @@ passes_unused_capture_maybe_capture_ref = value captured by `{$name}` is never r .help = did you mean to capture by reference instead? passes_unused_default_method_body_const_note = - `default_method_body_is_const` has been replaced with `#[const_trait]` on traits + `default_method_body_is_const` has been replaced with `const` on traits passes_unused_duplicate = unused attribute diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2009ddc1e2d..2766b14bb8d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast}; +use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast, join_path_syms}; use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; @@ -247,6 +247,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::FfiPure(attr_span)) => { self.check_ffi_pure(attr_span, attrs, target) } + Attribute::Parsed(AttributeKind::UnstableFeatureBound(syms)) => { + self.check_unstable_feature_bound(syms.first().unwrap().1, span, target) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -285,6 +288,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => { self.check_rustc_std_internal_symbol(attr_span, span, target) } + &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => { + self.check_coverage(attr_span, span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -294,7 +300,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } @@ -585,7 +590,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[coverage(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) { + fn check_coverage(&self, attr_span: Span, target_span: Span, target: Target) { let mut not_fn_impl_mod = None; let mut no_body = None; @@ -608,7 +613,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } self.dcx().emit_err(errors::CoverageAttributeNotAllowed { - attr_span: attr.span(), + attr_span, not_fn_impl_mod, no_body, help: (), @@ -678,9 +683,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { allowed_target: Target, ) { if target != allowed_target { - let path = attr.path(); - let path: Vec<_> = path.iter().map(|s| s.as_str()).collect(); - let attr_name = path.join("::"); + let attr_name = join_path_syms(attr.path()); self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, @@ -1159,7 +1162,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match item.kind { ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _) if generics.params.len() != 0 => {} - ItemKind::Trait(_, _, _, generics, _, items) + ItemKind::Trait(_, _, _, _, generics, _, items) if generics.params.len() != 0 || items.iter().any(|item| { matches!(self.tcx.def_kind(item.owner_id), DefKind::AssocTy) @@ -2267,6 +2270,47 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_unstable_feature_bound(&self, attr_span: Span, span: Span, target: Target) { + match target { + // FIXME(staged_api): There's no reason we can't support more targets here. We're just + // being conservative to begin with. + Target::Fn | Target::Impl => {} + Target::ExternCrate + | Target::Use + | Target::Static + | Target::Const + | Target::Closure + | Target::Mod + | Target::ForeignMod + | Target::GlobalAsm + | Target::TyAlias + | Target::Enum + | Target::Variant + | Target::Struct + | Target::Field + | Target::Union + | Target::Trait + | Target::TraitAlias + | Target::Expression + | Target::Statement + | Target::Arm + | Target::AssocConst + | Target::Method(_) + | Target::AssocTy + | Target::ForeignFn + | Target::ForeignStatic + | Target::ForeignTy + | Target::GenericParam(_) + | Target::MacroDef + | Target::Param + | Target::PatField + | Target::ExprField + | Target::WherePredicate => { + self.tcx.dcx().emit_err(errors::RustcUnstableFeatureBound { attr_span, span }); + } + } + } + fn check_rustc_std_internal_symbol(&self, attr_span: Span, span: Span, target: Target) { match target { Target::Fn | Target::Static | Target::ForeignFn | Target::ForeignStatic => {} diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 093a2b38804..37330c0ed6e 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -687,6 +687,15 @@ pub(crate) struct RustcAllowConstFnUnstable { } #[derive(Diagnostic)] +#[diag(passes_rustc_unstable_feature_bound)] +pub(crate) struct RustcUnstableFeatureBound { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(passes_rustc_std_internal_symbol)] pub(crate) struct RustcStdInternalSymbol { #[primary_span] diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index adda94fda8f..e5530d52686 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -802,12 +802,28 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); + let unstable_feature_stab = + find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 + // + // The exception is when there are both #[unstable_feature_bound(..)] and + // #![unstable(feature = "..", issue = "..")] that have the same symbol because + // that can effectively mark an impl as unstable. + // + // For example: + // ``` + // #[unstable_feature_bound(feat_foo)] + // #[unstable(feature = "feat_foo", issue = "none")] + // impl Foo for Bar {} + // ``` if let Some(( - Stability { level: attrs::StabilityLevel::Unstable { .. }, .. }, + Stability { level: attrs::StabilityLevel::Unstable { .. }, feature }, span, )) = stab { @@ -815,9 +831,21 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { c.visit_ty_unambig(self_ty); c.visit_trait_ref(t); + // Skip the lint if the impl is marked as unstable using + // #[unstable_feature_bound(..)] + let mut unstable_feature_bound_in_effect = false; + for (unstable_bound_feat_name, _) in unstable_feature_stab { + if *unstable_bound_feat_name == feature { + unstable_feature_bound_in_effect = true; + } + } + // do not lint when the trait isn't resolved, since resolution error should // be fixed first - if t.path.res != Res::Err && c.fully_stable { + if t.path.res != Res::Err + && c.fully_stable + && !unstable_feature_bound_in_effect + { self.tcx.emit_node_span_lint( INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id(), diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index ab2433234aa..80c13e44d7d 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -156,6 +156,7 @@ where } ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self), ty::ClauseKind::WellFormed(term) => term.visit_with(self), + ty::ClauseKind::UnstableFeature(_) => V::Result::output(), } } diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml index 748fa944e28..762acf9a1eb 100644 --- a/compiler/rustc_proc_macro/Cargo.toml +++ b/compiler/rustc_proc_macro/Cargo.toml @@ -15,7 +15,7 @@ test = false doctest = false [dependencies] -rustc-literal-escaper = "0.0.4" +rustc-literal-escaper = "0.0.5" [features] rustc-dep-of-std = [] diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 75c29788787..6b226b8a24d 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -764,6 +764,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { ClauseKind::HostEffect(..) => { todo!() } + ClauseKind::UnstableFeature(_) => { + todo!() + } } } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 6979d89a8e7..e56aabfd414 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -20,9 +20,9 @@ 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; -use rustc_middle::{bug, ty}; +use rustc_middle::ty::{Feed, Visibility}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; @@ -33,54 +33,42 @@ use crate::imports::{ImportData, ImportKind}; use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::{ BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot, - NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, ResolutionError, - Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError, errors, + NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used, + VisResolutionError, errors, }; type Res = def::Res<NodeId>; -impl<'ra, Id: Into<DefId>> ToNameBinding<'ra> - for (Module<'ra>, ty::Visibility<Id>, Span, LocalExpnId) -{ - fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> { - arenas.alloc_name_binding(NameBindingData { - kind: NameBindingKind::Module(self.0), - ambiguity: None, - warn_ambiguity: false, - vis: self.1.to_def_id(), - span: self.2, - expansion: self.3, - }) - } -} - -impl<'ra, Id: Into<DefId>> ToNameBinding<'ra> for (Res, ty::Visibility<Id>, Span, LocalExpnId) { - fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> { - arenas.alloc_name_binding(NameBindingData { - kind: NameBindingKind::Res(self.0), - ambiguity: None, - warn_ambiguity: false, - vis: self.1.to_def_id(), - span: self.2, - expansion: self.3, - }) - } -} - 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<T>(&mut self, parent: Module<'ra>, ident: Ident, ns: Namespace, def: T) - where - T: ToNameBinding<'ra>, - { - let binding = def.to_name_binding(self.arenas); + pub(crate) fn define_binding( + &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) { self.report_conflict(parent, ident, ns, old_binding, binding); } } + fn define( + &mut self, + parent: Module<'ra>, + ident: Ident, + ns: Namespace, + res: Res, + vis: Visibility<impl Into<DefId>>, + 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) + } + /// Walks up the tree of definitions starting at `def_id`, /// stopping at the first encountered module. /// Parent block modules for arbitrary def-ids are not recorded for the local crate, @@ -97,7 +85,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Reachable macros with block module parents exist due to `#[macro_export] macro_rules!`, /// but they cannot use def-site hygiene, so the assumption holds /// (<https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508>). - pub(crate) fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'ra> { + pub(crate) fn get_nearest_non_block_module(&self, mut def_id: DefId) -> Module<'ra> { loop { match self.get_module(def_id) { Some(module) => return module, @@ -106,44 +94,47 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn expect_module(&mut self, def_id: DefId) -> Module<'ra> { + pub(crate) fn expect_module(&self, def_id: DefId) -> Module<'ra> { self.get_module(def_id).expect("argument `DefId` is not a module") } /// If `def_id` refers to a module (in resolver's sense, i.e. a module item, crate root, enum, /// or trait), then this function returns that module's resolver representation, otherwise it /// returns `None`. - pub(crate) fn get_module(&mut self, def_id: DefId) -> Option<Module<'ra>> { - if let module @ Some(..) = self.module_map.get(&def_id) { - return module.copied(); - } + pub(crate) fn get_module(&self, def_id: DefId) -> Option<Module<'ra>> { + match def_id.as_local() { + Some(local_def_id) => self.local_module_map.get(&local_def_id).copied(), + None => { + if let module @ Some(..) = self.extern_module_map.borrow().get(&def_id) { + return module.copied(); + } - if !def_id.is_local() { - // Query `def_kind` is not used because query system overhead is too expensive here. - let def_kind = self.cstore().def_kind_untracked(def_id); - if let DefKind::Mod | DefKind::Enum | DefKind::Trait = def_kind { - let parent = self - .tcx - .opt_parent(def_id) - .map(|parent_id| self.get_nearest_non_block_module(parent_id)); - // Query `expn_that_defined` is not used because - // hashing spans in its result is expensive. - let expn_id = self.cstore().expn_that_defined_untracked(def_id, self.tcx.sess); - return Some(self.new_module( - parent, - ModuleKind::Def(def_kind, def_id, Some(self.tcx.item_name(def_id))), - expn_id, - self.def_span(def_id), - // FIXME: Account for `#[no_implicit_prelude]` attributes. - parent.is_some_and(|module| module.no_implicit_prelude), - )); + // Query `def_kind` is not used because query system overhead is too expensive here. + let def_kind = self.cstore().def_kind_untracked(def_id); + if def_kind.is_module_like() { + let parent = self + .tcx + .opt_parent(def_id) + .map(|parent_id| self.get_nearest_non_block_module(parent_id)); + // Query `expn_that_defined` is not used because + // hashing spans in its result is expensive. + let expn_id = self.cstore().expn_that_defined_untracked(def_id, self.tcx.sess); + return Some(self.new_extern_module( + parent, + ModuleKind::Def(def_kind, def_id, Some(self.tcx.item_name(def_id))), + expn_id, + self.def_span(def_id), + // FIXME: Account for `#[no_implicit_prelude]` attributes. + parent.is_some_and(|module| module.no_implicit_prelude), + )); + } + + None } } - - None } - pub(crate) fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'ra> { + pub(crate) fn expn_def_scope(&self, expn_id: ExpnId) -> Module<'ra> { match expn_id.expn_data().macro_def_id { Some(def_id) => self.macro_def_scope(def_id), None => expn_id @@ -153,7 +144,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn macro_def_scope(&mut self, def_id: DefId) -> Module<'ra> { + pub(crate) fn macro_def_scope(&self, def_id: DefId) -> Module<'ra> { if let Some(id) = def_id.as_local() { self.local_macro_def_scopes[&id] } else { @@ -223,12 +214,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let expansion = parent_scope.expansion; // Record primary definitions. match res { - Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, def_id) => { - let module = self.expect_module(def_id); - self.define(parent, ident, TypeNS, (module, vis, span, expansion)); - } Res::Def( - DefKind::Struct + DefKind::Mod + | DefKind::Enum + | DefKind::Trait + | DefKind::Struct | DefKind::Union | DefKind::Variant | DefKind::TyAlias @@ -239,7 +229,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _, ) | Res::PrimTy(..) - | Res::ToolMod => self.define(parent, ident, TypeNS, (res, vis, span, expansion)), + | Res::ToolMod => self.define(parent, ident, TypeNS, res, vis, span, expansion), Res::Def( DefKind::Fn | DefKind::AssocFn @@ -248,9 +238,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | DefKind::AssocConst | DefKind::Ctor(..), _, - ) => self.define(parent, ident, ValueNS, (res, vis, span, expansion)), + ) => self.define(parent, ident, ValueNS, res, vis, span, expansion), Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => { - self.define(parent, ident, MacroNS, (res, vis, span, expansion)) + self.define(parent, ident, MacroNS, res, vis, span, expansion) } Res::Def( DefKind::TyParam @@ -294,10 +284,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { Res::Def(self.r.tcx.def_kind(def_id), def_id) } - fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility { + fn resolve_visibility(&mut self, vis: &ast::Visibility) -> Visibility { self.try_resolve_visibility(vis, true).unwrap_or_else(|err| { self.r.report_vis_error(err); - ty::Visibility::Public + Visibility::Public }) } @@ -305,10 +295,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { &mut self, vis: &'ast ast::Visibility, finalize: bool, - ) -> Result<ty::Visibility, VisResolutionError<'ast>> { + ) -> Result<Visibility, VisResolutionError<'ast>> { let parent_scope = &self.parent_scope; match vis.kind { - ast::VisibilityKind::Public => Ok(ty::Visibility::Public), + ast::VisibilityKind::Public => Ok(Visibility::Public), ast::VisibilityKind::Inherited => { Ok(match self.parent_scope.module.kind { // Any inherited visibility resolved directly inside an enum or trait @@ -318,7 +308,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.tcx.visibility(def_id).expect_local() } // Otherwise, the visibility is restricted to the nearest parent `mod` item. - _ => ty::Visibility::Restricted( + _ => Visibility::Restricted( self.parent_scope.module.nearest_parent_mod().expect_local(), ), }) @@ -366,9 +356,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } if module.is_normal() { match res { - Res::Err => Ok(ty::Visibility::Public), + Res::Err => Ok(Visibility::Public), _ => { - let vis = ty::Visibility::Restricted(res.def_id()); + let vis = Visibility::Restricted(res.def_id()); if self.r.is_accessible_from(vis, parent_scope.module) { Ok(vis.expect_local()) } else { @@ -416,7 +406,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.field_visibility_spans.insert(def_id, field_vis); } - fn block_needs_anonymous_module(&mut self, block: &Block) -> bool { + fn block_needs_anonymous_module(&self, block: &Block) -> bool { // If any statements are items, we need to create an anonymous module block .stmts @@ -433,7 +423,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { item: &ast::Item, root_span: Span, root_id: NodeId, - vis: ty::Visibility, + vis: Visibility, ) { let current_module = self.parent_scope.module; let import = self.r.arenas.alloc_import(ImportData { @@ -481,7 +471,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { list_stem: bool, // The whole `use` item item: &Item, - vis: ty::Visibility, + vis: Visibility, root_span: Span, ) { debug!( @@ -690,7 +680,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { true, // The whole `use` item item, - ty::Visibility::Restricted( + Visibility::Restricted( self.parent_scope.module.nearest_parent_mod().expect_local(), ), root_span, @@ -706,7 +696,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ident: Ident, feed: Feed<'tcx, LocalDefId>, adt_res: Res, - adt_vis: ty::Visibility, + adt_vis: Visibility, adt_span: Span, ) { let parent_scope = &self.parent_scope; @@ -714,7 +704,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(parent, ident, TypeNS, adt_res, adt_vis, adt_span, expansion); self.r.feed_visibility(feed, adt_vis); let def_id = feed.key(); @@ -766,7 +756,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ItemKind::Mod(_, ident, ref mod_kind) => { - let module = self.r.new_module( + self.r.define(parent, ident, TypeNS, res, vis, sp, expansion); + + if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind { + self.r.mods_with_parse_errors.insert(def_id); + } + self.parent_scope.module = self.r.new_local_module( Some(parent), ModuleKind::Def(def_kind, def_id, Some(ident.name)), expansion.to_expn_id(), @@ -774,24 +769,16 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { parent.no_implicit_prelude || ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude), ); - self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); - - if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind { - self.r.mods_with_parse_errors.insert(def_id); - } - - // Descend into the module. - self.parent_scope.module = module; } // These items live in the value namespace. 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(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(parent, ident, ValueNS, res, vis, sp, expansion); // Functions introducing procedural macros reserve a slot // in the macro namespace as well (see #52225). @@ -800,19 +787,19 @@ 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(parent, ident, TypeNS, res, vis, sp, expansion); } ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => { - let module = self.r.new_module( + self.r.define(parent, ident, TypeNS, res, vis, sp, expansion); + + self.parent_scope.module = self.r.new_local_module( Some(parent), ModuleKind::Def(def_kind, def_id, Some(ident.name)), expansion.to_expn_id(), item.span, parent.no_implicit_prelude, ); - self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); - self.parent_scope.module = module; } // These items live in both the type and value namespaces. @@ -834,7 +821,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let mut ctor_vis = if vis.is_public() && ast::attr::contains_name(&item.attrs, sym::non_exhaustive) { - ty::Visibility::Restricted(CRATE_DEF_ID) + Visibility::Restricted(CRATE_DEF_ID) } else { vis }; @@ -847,7 +834,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // constructor visibility should still be determined correctly. let field_vis = self .try_resolve_visibility(&field.vis, false) - .unwrap_or(ty::Visibility::Public); + .unwrap_or(Visibility::Public); if ctor_vis.is_at_least(field_vis, self.r.tcx) { ctor_vis = field_vis; } @@ -856,7 +843,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(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()); @@ -896,7 +883,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { item: &Item, ident: Ident, local_def_id: LocalDefId, - vis: ty::Visibility, + vis: Visibility, parent: Module<'ra>, ) { let sp = item.span; @@ -920,8 +907,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } .map(|module| { let used = self.process_macro_use_imports(item, module); - let vis = ty::Visibility::<LocalDefId>::Public; - let binding = (module, vis, sp, expansion).to_name_binding(self.r.arenas); + let binding = self.r.arenas.new_pub_res_binding(module.res().unwrap(), sp, expansion); (used, Some(ModuleOrUniformRoot::Module(module)), binding) }) .unwrap_or((true, None, self.r.dummy_binding)); @@ -978,7 +964,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ); } } - self.r.define(parent, ident, TypeNS, imported_binding); + self.r.define_binding(parent, ident, TypeNS, imported_binding); } /// Constructs the reduced graph for one foreign item. @@ -995,7 +981,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(parent, ident, ns, self.res(def_id), vis, item.span, expansion); self.r.feed_visibility(feed, vis); } @@ -1003,7 +989,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; if self.block_needs_anonymous_module(block) { - let module = self.r.new_module( + let module = self.r.new_local_module( Some(parent), ModuleKind::Block, expansion.to_expn_id(), @@ -1081,7 +1067,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: ty::Visibility::Restricted(CRATE_DEF_ID), + vis: Visibility::Restricted(CRATE_DEF_ID), }) }; @@ -1135,7 +1121,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } /// Returns `true` if this attribute list contains `macro_use`. - fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { + fn contains_macro_use(&self, attrs: &[ast::Attribute]) -> bool { for attr in attrs { if attr.has_name(sym::macro_escape) { let inner_attribute = matches!(attr.style, ast::AttrStyle::Inner); @@ -1231,11 +1217,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.macro_names.insert(ident); let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { - ty::Visibility::Public + Visibility::Public } else { - ty::Visibility::Restricted(CRATE_DEF_ID) + Visibility::Restricted(CRATE_DEF_ID) }; - let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas); + let binding = self.r.arenas.new_res_binding(res, vis.to_def_id(), span, expansion); self.r.set_binding_parent_module(binding, parent_scope.module); self.r.all_macro_rules.insert(ident.name); if is_macro_export { @@ -1254,7 +1240,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(self.r.graph_root, ident, MacroNS, import_binding); + self.r.define_binding(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); @@ -1275,14 +1261,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // Visibilities must not be resolved non-speculatively twice // and we already resolved this one as a `fn` item visibility. ItemKind::Fn(..) => { - self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public) + self.try_resolve_visibility(&item.vis, false).unwrap_or(Visibility::Public) } _ => self.resolve_visibility(&item.vis), }; 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(module, ident, MacroNS, res, vis, span, expansion); self.r.feed_visibility(feed, vis); self.parent_scope.macro_rules } @@ -1418,7 +1404,7 @@ 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)); + 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) { let impl_def_id = self.r.tcx.local_parent(local_def_id); let key = BindingKey::new(ident.normalize_to_macros_2_0(), ns); @@ -1503,13 +1489,13 @@ 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(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. let ctor_vis = if vis.is_public() && ast::attr::contains_name(&variant.attrs, sym::non_exhaustive) { - ty::Visibility::Restricted(CRATE_DEF_ID) + Visibility::Restricted(CRATE_DEF_ID) } else { vis }; @@ -1519,7 +1505,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(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/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 781e2ce9704..7d51fef28d3 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -2,7 +2,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; -use rustc_attr_parsing::{AttributeParser, Early, OmitDoc}; +use rustc_attr_parsing::{AttributeParser, Early, OmitDoc, ShouldEmit}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -132,7 +132,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { &self.resolver.tcx.sess, self.resolver.tcx.features(), Vec::new(), - Early { emit_errors: false }, + Early { emit_errors: ShouldEmit::Nothing }, ); let attrs = parser.parse_attribute_list( &i.attrs, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 93d848787ef..f6f45adabe9 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,6 +1,8 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path}; +use rustc_ast::{ + self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path, join_path_idents, +}; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{ self as attr, AttributeKind, CfgEntry, Stability, StrippedCfgItem, find_attr, @@ -232,12 +234,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - let old_kind = match (ns, old_binding.module()) { + let old_kind = match (ns, old_binding.res()) { (ValueNS, _) => "value", (MacroNS, _) => "macro", (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", - (TypeNS, Some(module)) if module.is_normal() => "module", - (TypeNS, Some(module)) if module.is_trait() => "trait", + (TypeNS, Res::Def(DefKind::Mod, _)) => "module", + (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", (TypeNS, _) => "type", }; @@ -1319,7 +1321,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // collect submodules to explore - if let Some(module) = name_binding.module() { + if let Some(def_id) = name_binding.res().module_like_def_id() { // form the path let mut path_segments = path_segments.clone(); path_segments.push(ast::PathSegment::from_ident(ident)); @@ -1339,14 +1341,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if !is_extern_crate_that_also_appears_in_prelude || alias_import { // add the module to the lookup - if seen_modules.insert(module.def_id()) { + if seen_modules.insert(def_id) { if via_import { &mut worklist_via_import } else { &mut worklist }.push( ( - module, + this.expect_module(def_id), path_segments, child_accessible, child_doc_visible, - is_stable && this.is_stable(module.def_id(), name_binding.span), + is_stable && this.is_stable(def_id, name_binding.span), ), ); } @@ -2018,7 +2020,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - let mut sugg_paths = vec![]; + let mut sugg_paths: Vec<(Vec<Ident>, bool)> = vec![]; if let Some(mut def_id) = res.opt_def_id() { // We can't use `def_path_str` in resolve. let mut path = vec![def_id]; @@ -2031,16 +2033,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } // We will only suggest importing directly if it is accessible through that path. - let path_names: Option<Vec<String>> = path + let path_names: Option<Vec<Ident>> = path .iter() .rev() .map(|def_id| { - self.tcx.opt_item_name(*def_id).map(|n| { - if def_id.is_top_level_module() { - "crate".to_string() + self.tcx.opt_item_name(*def_id).map(|name| { + Ident::with_dummy_span(if def_id.is_top_level_module() { + kw::Crate } else { - n.to_string() - } + name + }) }) }) .collect(); @@ -2084,17 +2086,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match binding.kind { NameBindingKind::Import { import, .. } => { for segment in import.module_path.iter().skip(1) { - path.push(segment.ident.to_string()); + path.push(segment.ident); } sugg_paths.push(( - path.iter() - .cloned() - .chain(vec![ident.to_string()].into_iter()) - .collect::<Vec<_>>(), + path.iter().cloned().chain(std::iter::once(ident)).collect::<Vec<_>>(), true, // re-export )); } - NameBindingKind::Res(_) | NameBindingKind::Module(_) => {} + NameBindingKind::Res(_) => {} } let first = binding == first_binding; let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); @@ -2126,7 +2125,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err.subdiagnostic(note); } // We prioritize shorter paths, non-core imports and direct imports over the alternatives. - sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport)); + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); for (sugg, reexport) in sugg_paths { if not_publicly_reexported { break; @@ -2136,7 +2135,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // `tests/ui/imports/issue-55884-2.rs` continue; } - let path = sugg.join("::"); + let path = join_path_idents(sugg); let sugg = if reexport { errors::ImportIdent::ThroughReExport { span: dedup_span, ident, path } } else { @@ -2150,7 +2149,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } pub(crate) fn find_similarly_named_module_or_crate( - &mut self, + &self, ident: Symbol, current_module: Module<'ra>, ) -> Option<Symbol> { @@ -2159,7 +2158,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .keys() .map(|ident| ident.name) .chain( - self.module_map + self.local_module_map + .iter() + .filter(|(_, module)| { + current_module.is_ancestor_of(**module) && current_module != **module + }) + .flat_map(|(_, module)| module.kind.name()), + ) + .chain( + self.extern_module_map + .borrow() .iter() .filter(|(_, module)| { current_module.is_ancestor_of(**module) && current_module != **module @@ -2306,25 +2314,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .ok() }; if let Some(binding) = binding { - let mut found = |what| { - msg = format!( - "expected {}, found {} `{}` in {}", - ns.descr(), - what, - ident, - parent - ) - }; - if binding.module().is_some() { - found("module") - } else { - match binding.res() { - // Avoid using TyCtxt::def_kind_descr in the resolver, because it - // indirectly *calls* the resolver, and would cause a query cycle. - Res::Def(kind, id) => found(kind.descr(id)), - _ => found(ns_to_try.descr()), - } - } + msg = format!( + "expected {}, found {} `{ident}` in {parent}", + ns.descr(), + binding.res().descr(), + ); }; } (msg, None) @@ -2449,7 +2443,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn undeclared_module_suggest_declare( - &mut self, + &self, ident: Ident, path: std::path::PathBuf, ) -> Option<(Vec<(Span, String)>, String, Applicability)> { @@ -2464,7 +2458,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { )) } - fn undeclared_module_exists(&mut self, ident: Ident) -> Option<std::path::PathBuf> { + fn undeclared_module_exists(&self, ident: Ident) -> Option<std::path::PathBuf> { let map = self.tcx.sess.source_map(); let src = map.span_to_filename(ident.span).into_local_path()?; @@ -2823,24 +2817,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return cached; } visited.insert(parent_module, false); - let res = r.module_map.get(&parent_module).is_some_and(|m| { - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + let m = r.expect_module(parent_module); + let mut res = false; + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) - { - return true; - } + res = true; + break; } } - false - }); + } visited.insert(parent_module, res); res } diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 5de80de3f8d..34d1e9552fd 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -38,11 +38,11 @@ pub(crate) struct EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { } impl Resolver<'_, '_> { - fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId { + fn nearest_normal_mod(&self, def_id: LocalDefId) -> LocalDefId { self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local() } - fn private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility { + fn private_vis_import(&self, binding: NameBinding<'_>) -> Visibility { let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() }; Visibility::Restricted( import @@ -52,7 +52,7 @@ impl Resolver<'_, '_> { ) } - fn private_vis_def(&mut self, def_id: LocalDefId) -> Visibility { + fn private_vis_def(&self, def_id: LocalDefId) -> Visibility { // For mod items `nearest_normal_mod` returns its argument, but we actually need its parent. let normal_mod_id = self.nearest_normal_mod(def_id); if normal_mod_id == def_id { @@ -113,8 +113,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { /// Update effective visibilities of bindings in the given module, /// including their whole reexport chains. fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { - assert!(self.r.module_map.contains_key(&module_id.to_def_id())); - let module = self.r.get_module(module_id.to_def_id()).unwrap(); + let module = self.r.expect_module(module_id.to_def_id()); let resolutions = self.r.resolutions(module); for (_, name_resolution) in resolutions.borrow().iter() { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 99297de40d5..34941398a2b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -3,11 +3,10 @@ use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS}; -use rustc_middle::{bug, ty}; +use rustc_middle::bug; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; -use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; use rustc_span::{Ident, Span, kw, sym}; use tracing::{debug, instrument}; @@ -20,11 +19,9 @@ use crate::{ AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, Determinacy, Finalize, ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, - ScopeSet, Segment, ToNameBinding, Used, Weak, errors, + ScopeSet, Segment, Used, Weak, errors, }; -type Visibility = ty::Visibility<LocalDefId>; - #[derive(Copy, Clone)] pub enum UsePrelude { No, @@ -222,7 +219,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn hygienic_lexical_parent( - &mut self, + &self, module: Module<'ra>, ctxt: &mut SyntaxContext, derive_fallback_lint_id: Option<NodeId>, @@ -464,13 +461,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { - let binding = ( + let binding = this.arenas.new_pub_res_binding( Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat), - Visibility::Public, derive.span, LocalExpnId::ROOT, - ) - .to_name_binding(this.arenas); + ); result = Ok((binding, Flags::empty())); break; } @@ -846,7 +841,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns == TypeNS { if ident.name == kw::Crate || ident.name == kw::DollarCrate { let module = self.resolve_crate_root(ident); - return Ok(self.module_self_bindings[&module]); + return Ok(module.self_binding.unwrap()); } else if ident.name == kw::Super || ident.name == kw::SelfLower { // FIXME: Implement these with renaming requirements so that e.g. // `use super;` doesn't work, but `use super as name;` does. @@ -890,7 +885,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } - let check_usable = |this: &mut Self, binding: NameBinding<'ra>| { + let check_usable = |this: &Self, binding: NameBinding<'ra>| { let usable = this.is_accessible_from(binding.vis, parent_scope.module); if usable { Ok(binding) } else { Err((Determined, Weak::No)) } }; @@ -1637,11 +1632,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res); - if let Some(next_module) = binding.module() { - if self.mods_with_parse_errors.contains(&next_module.def_id()) { + if let Some(def_id) = binding.res().module_like_def_id() { + if self.mods_with_parse_errors.contains(&def_id) { module_had_parse_errors = true; } - module = Some(ModuleOrUniformRoot::Module(next_module)); + module = Some(ModuleOrUniformRoot::Module(self.expect_module(def_id))); record_segment_res(self, res); } else if res == Res::ToolMod && !is_last && opt_ns.is_some() { if binding.is_import() { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 40c63a3edf3..0a4c25b0eb0 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -11,11 +11,12 @@ use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir::def::{self, DefKind, PartialRes}; use rustc_hir::def_id::DefId; use rustc_middle::metadata::{ModChild, Reexport}; -use rustc_middle::{span_bug, ty}; +use rustc_middle::span_bug; +use rustc_middle::ty::Visibility; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, - REDUNDANT_IMPORTS, UNUSED_IMPORTS, + AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, + PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -88,7 +89,7 @@ pub(crate) enum ImportKind<'ra> { is_prelude: bool, // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. - max_vis: Cell<Option<ty::Visibility>>, + max_vis: Cell<Option<Visibility>>, id: NodeId, }, ExternCrate { @@ -185,7 +186,7 @@ pub(crate) struct ImportData<'ra> { /// |`use ::foo` | `ModuleOrUniformRoot::CrateRootAndExternPrelude` | a special case in 2015 edition | /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>, - pub vis: ty::Visibility, + pub vis: Visibility, } /// All imports are unique and allocated on a same arena, @@ -455,7 +456,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { f: F, ) -> T where - F: FnOnce(&mut Resolver<'ra, 'tcx>, &mut NameResolution<'ra>) -> T, + F: FnOnce(&Resolver<'ra, 'tcx>, &mut NameResolution<'ra>) -> T, { // 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. @@ -634,10 +635,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn check_hidden_glob_reexports( - &mut self, - exported_ambiguities: FxHashSet<NameBinding<'ra>>, - ) { + pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet<NameBinding<'ra>>) { for module in self.arenas.local_modules().iter() { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); @@ -679,9 +677,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { NameBindingKind::Res(res) => { Some(self.def_id_to_node_id(res.def_id().expect_local())) } - NameBindingKind::Module(module) => { - Some(self.def_id_to_node_id(module.def_id().expect_local())) - } NameBindingKind::Import { import, .. } => import.id(), }; if let Some(binding_id) = binding_id { @@ -699,6 +694,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } + + if let NameBindingKind::Import { import, .. } = binding.kind + && let Some(binding_id) = import.id() + && let import_def_id = self.local_def_id(binding_id) + && self.effective_visibilities.is_exported(import_def_id) + && let Res::Def(reexported_kind, reexported_def_id) = binding.res() + && !matches!(reexported_kind, DefKind::Ctor(..)) + && !reexported_def_id.is_local() + && self.tcx.is_private_dep(reexported_def_id.krate) + { + self.lint_buffer.buffer_lint( + EXPORTED_PRIVATE_DEPENDENCIES, + binding_id, + binding.span, + BuiltinLintDiag::ReexportPrivateDependency { + kind: binding.res().descr().to_string(), + name: key.ident.name.to_string(), + krate: self.tcx.crate_name(reexported_def_id.krate), + }, + ); + } } } } @@ -875,7 +891,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // We need the `target`, `source` can be extracted. let imported_binding = this.import(binding, import); - this.define(parent, target, ns, imported_binding); + this.define_binding(parent, target, ns, imported_binding); PendingBinding::Ready(Some(imported_binding)) } Err(Determinacy::Determined) => { @@ -1283,7 +1299,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if !binding.vis.is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); - if let ty::Visibility::Restricted(binding_def_id) = binding.vis + if let Visibility::Restricted(binding_def_id) = binding.vis && binding_def_id.is_top_level_module() { crate_private_reexport = true; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 44a567f7810..93cec8daa5a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -28,7 +28,7 @@ use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, Par use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; -use rustc_middle::ty::DelegationFnSig; +use rustc_middle::ty::{DelegationFnSig, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint::{self, BuiltinLintDiag}; @@ -638,8 +638,8 @@ impl PathSource<'_, '_, '_> { enum MaybeExported<'a> { Ok(NodeId), Impl(Option<DefId>), - ImplItem(Result<DefId, &'a Visibility>), - NestedUse(&'a Visibility), + ImplItem(Result<DefId, &'a ast::Visibility>), + NestedUse(&'a ast::Visibility), } impl MaybeExported<'_> { @@ -2491,7 +2491,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved /// label and reports an error if the label is not found or is unreachable. - fn resolve_label(&mut self, mut label: Ident) -> Result<(NodeId, Span), ResolutionError<'ra>> { + fn resolve_label(&self, mut label: Ident) -> Result<(NodeId, Span), ResolutionError<'ra>> { let mut suggestion = None; for i in (0..self.label_ribs.len()).rev() { @@ -2899,9 +2899,21 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } if param.ident.name == kw::UnderscoreLifetime { + // To avoid emitting two similar errors, + // we need to check if the span is a raw underscore lifetime, see issue #143152 + let is_raw_underscore_lifetime = self + .r + .tcx + .sess + .psess + .raw_identifier_spans + .iter() + .any(|span| span == param.span()); + self.r .dcx() - .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); + .create_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }) + .emit_unless(is_raw_underscore_lifetime); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); continue; @@ -3463,7 +3475,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { span, "error should be emitted when an unexpected trait item is used", ); - rustc_middle::ty::Visibility::Public + Visibility::Public }; this.r.feed_visibility(this.r.feed(id), vis); }; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b669796bb02..ee462d90764 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -926,7 +926,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { continue; }; if let Res::Def(DefKind::Mod, module) = res.expect_full_res() - && let Some(module) = self.r.get_module(module) + && let module = self.r.expect_module(module) && let item = path[idx + 1].ident && let Some(did) = find_doc_alias_name(self.r, module, item.name) { @@ -939,7 +939,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn suggest_trait_and_bounds( - &mut self, + &self, err: &mut Diag<'_>, source: PathSource<'_, '_, '_>, res: Option<Res>, @@ -1140,7 +1140,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Emit special messages for unresolved `Self` and `self`. fn suggest_self_ty( - &mut self, + &self, err: &mut Diag<'_>, source: PathSource<'_, '_, '_>, path: &[Segment], @@ -1256,7 +1256,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn detect_missing_binding_available_from_pattern( - &mut self, + &self, err: &mut Diag<'_>, path: &[Segment], following_seg: Option<&Segment>, @@ -1302,11 +1302,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - fn suggest_at_operator_in_slice_pat_with_range( - &mut self, - err: &mut Diag<'_>, - path: &[Segment], - ) { + fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) { let Some(pat) = self.diag_metadata.current_pat else { return }; let (bound, side, range) = match &pat.kind { ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range), @@ -1367,7 +1363,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn explain_functions_in_pattern( - &mut self, + &self, err: &mut Diag<'_>, res: Option<Res>, source: PathSource<'_, '_, '_>, @@ -1379,7 +1375,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn suggest_changing_type_to_const_param( - &mut self, + &self, err: &mut Diag<'_>, res: Option<Res>, source: PathSource<'_, '_, '_>, @@ -1429,7 +1425,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn suggest_pattern_match_with_let( - &mut self, + &self, err: &mut Diag<'_>, source: PathSource<'_, '_, '_>, span: Span, @@ -1485,7 +1481,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`. - fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diag<'_>) -> bool { + fn restrict_assoc_type_in_where_clause(&self, span: Span, err: &mut Diag<'_>) -> bool { // Detect that we are actually in a `where` predicate. let (bounded_ty, bounds, where_span) = if let Some(ast::WherePredicate { kind: @@ -1633,7 +1629,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); - let path_sep = |this: &mut Self, err: &mut Diag<'_>, expr: &Expr, kind: DefKind| { + let path_sep = |this: &Self, err: &mut Diag<'_>, expr: &Expr, kind: DefKind| { const MESSAGE: &str = "use the path separator to refer to an item"; let (lhs_span, rhs_span) = match &expr.kind { @@ -2531,16 +2527,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // FIXME: this is not totally accurate, but mostly works suggestion.candidate != following_seg.ident.name } - Res::Def(DefKind::Mod, def_id) => self.r.get_module(def_id).map_or_else( - || false, - |module| { - self.r - .resolutions(module) - .borrow() - .iter() - .any(|(key, _)| key.ident.name == following_seg.ident.name) - }, - ), + Res::Def(DefKind::Mod, def_id) => { + let module = self.r.expect_module(def_id); + self.r + .resolutions(module) + .borrow() + .iter() + .any(|(key, _)| key.ident.name == following_seg.ident.name) + } _ => true, }); } @@ -2589,7 +2583,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // try to give a suggestion for this pattern: `name = blah`, which is common in other languages // suggest `let name = blah` to introduce a new binding - fn let_binding_suggestion(&mut self, err: &mut Diag<'_>, ident_span: Span) -> bool { + fn let_binding_suggestion(&self, err: &mut Diag<'_>, ident_span: Span) -> bool { if ident_span.from_expansion() { return false; } @@ -2656,18 +2650,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if result.is_some() || !name_binding.vis.is_visible_locally() { return; } - if let Some(module) = name_binding.module() { + if let Some(module_def_id) = name_binding.res().module_like_def_id() { // form the path let mut path_segments = path_segments.clone(); path_segments.push(ast::PathSegment::from_ident(ident)); - let module_def_id = module.def_id(); let doc_visible = doc_visible && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id)); if module_def_id == def_id { let path = Path { span: name_binding.span, segments: path_segments, tokens: None }; result = Some(( - module, + r.expect_module(module_def_id), ImportSuggestion { did: Some(def_id), descr: "module", @@ -2682,6 +2675,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else { // add the module to the lookup if seen_modules.insert(module_def_id) { + let module = r.expect_module(module_def_id); worklist.push((module, path_segments, doc_visible)); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index c2b3451d4a1..dae30b77ec1 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -65,7 +65,7 @@ use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::{ self, DelegationFnSig, Feed, MainDefinition, RegisteredTools, ResolverGlobalCtxt, - ResolverOutputs, TyCtxt, TyCtxtFeed, + ResolverOutputs, TyCtxt, TyCtxtFeed, Visibility, }; use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; @@ -579,12 +579,16 @@ struct ModuleData<'ra> { globs: RefCell<Vec<Import<'ra>>>, /// Used to memoize the traits in this module for faster searches through all traits in scope. - traits: RefCell<Option<Box<[(Ident, NameBinding<'ra>)]>>>, + traits: RefCell<Option<Box<[(Ident, NameBinding<'ra>, Option<Module<'ra>>)]>>>, /// Span of the module itself. Used for error reporting. span: Span, expansion: ExpnId, + + /// Binding for implicitly declared names that come with a module, + /// like `self` (not yet used), or `crate`/`$crate` (for root modules). + self_binding: Option<NameBinding<'ra>>, } /// All modules are unique and allocated on a same arena, @@ -613,6 +617,7 @@ impl<'ra> ModuleData<'ra> { expansion: ExpnId, span: Span, no_implicit_prelude: bool, + self_binding: Option<NameBinding<'ra>>, ) -> Self { let is_foreign = match kind { ModuleKind::Def(_, def_id, _) => !def_id.is_local(), @@ -630,6 +635,7 @@ impl<'ra> ModuleData<'ra> { traits: RefCell::new(None), span, expansion, + self_binding, } } } @@ -655,12 +661,12 @@ impl<'ra> Module<'ra> { let mut traits = self.traits.borrow_mut(); if traits.is_none() { let mut collected_traits = Vec::new(); - self.for_each_child(resolver, |_, name, ns, binding| { + self.for_each_child(resolver, |r, name, ns, binding| { if ns != TypeNS { return; } - if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() { - collected_traits.push((name, binding)) + if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() { + collected_traits.push((name, binding, r.as_mut().get_module(def_id))) } }); *traits = Some(collected_traits.into_boxed_slice()); @@ -674,7 +680,6 @@ impl<'ra> Module<'ra> { } } - // Public for rustdoc. fn def_id(self) -> DefId { self.opt_def_id().expect("`ModuleData::def_id` is called on a block module") } @@ -749,7 +754,7 @@ struct NameBindingData<'ra> { warn_ambiguity: bool, expansion: LocalExpnId, span: Span, - vis: ty::Visibility<DefId>, + vis: Visibility<DefId>, } /// All name bindings are unique and allocated on a same arena, @@ -769,20 +774,9 @@ impl std::hash::Hash for NameBindingData<'_> { } } -trait ToNameBinding<'ra> { - fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra>; -} - -impl<'ra> ToNameBinding<'ra> for NameBinding<'ra> { - fn to_name_binding(self, _: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> { - self - } -} - #[derive(Clone, Copy, Debug)] enum NameBindingKind<'ra> { Res(Res), - Module(Module<'ra>), Import { binding: NameBinding<'ra>, import: Import<'ra> }, } @@ -875,18 +869,9 @@ struct AmbiguityError<'ra> { } impl<'ra> NameBindingData<'ra> { - fn module(&self) -> Option<Module<'ra>> { - match self.kind { - NameBindingKind::Module(module) => Some(module), - NameBindingKind::Import { binding, .. } => binding.module(), - _ => None, - } - } - fn res(&self) -> Res { match self.kind { NameBindingKind::Res(res) => res, - NameBindingKind::Module(module) => module.res().unwrap(), NameBindingKind::Import { binding, .. } => binding.res(), } } @@ -921,7 +906,7 @@ impl<'ra> NameBindingData<'ra> { DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _, )) => true, - NameBindingKind::Res(..) | NameBindingKind::Module(..) => false, + NameBindingKind::Res(..) => false, } } @@ -930,11 +915,7 @@ impl<'ra> NameBindingData<'ra> { NameBindingKind::Import { import, .. } => { matches!(import.kind, ImportKind::ExternCrate { .. }) } - NameBindingKind::Module(module) - if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind => - { - def_id.is_crate_root() - } + NameBindingKind::Res(Res::Def(_, def_id)) => def_id.is_crate_root(), _ => false, } } @@ -1100,7 +1081,10 @@ pub struct Resolver<'ra, 'tcx> { /// some AST passes can generate identifiers that only resolve to local or /// lang items. empty_module: Module<'ra>, - module_map: FxIndexMap<DefId, Module<'ra>>, + /// Eagerly populated map of all local non-block modules. + local_module_map: FxIndexMap<LocalDefId, Module<'ra>>, + /// Lazily populated cache of modules loaded from external crates. + extern_module_map: RefCell<FxIndexMap<DefId, Module<'ra>>>, binding_parent_modules: FxHashMap<NameBinding<'ra>, Module<'ra>>, underscore_disambiguator: u32, @@ -1108,7 +1092,7 @@ pub struct Resolver<'ra, 'tcx> { /// Maps glob imports to the names of items actually imported. glob_map: FxIndexMap<LocalDefId, FxIndexSet<Symbol>>, glob_error: Option<ErrorGuaranteed>, - visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>, + visibilities_for_hashing: Vec<(LocalDefId, Visibility)>, used_imports: FxHashSet<NodeId>, maybe_unused_trait_imports: FxIndexSet<LocalDefId>, @@ -1126,17 +1110,14 @@ pub struct Resolver<'ra, 'tcx> { builtin_types_bindings: FxHashMap<Symbol, NameBinding<'ra>>, builtin_attrs_bindings: FxHashMap<Symbol, NameBinding<'ra>>, registered_tool_bindings: FxHashMap<Ident, NameBinding<'ra>>, - /// Binding for implicitly declared names that come with a module, - /// like `self` (not yet used), or `crate`/`$crate` (for root modules). - module_self_bindings: FxHashMap<Module<'ra>, NameBinding<'ra>>, - used_extern_options: FxHashSet<Symbol>, macro_names: FxHashSet<Ident>, builtin_macros: FxHashMap<Symbol, SyntaxExtensionKind>, registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxIndexMap<Symbol, NameBinding<'ra>>, + /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap<LocalDefId, &'ra MacroData>, - /// Lazily populated cache of macros loaded from external crates. + /// Lazily populated cache of macro definitions loaded from external crates. extern_macro_map: RefCell<FxHashMap<DefId, &'ra MacroData>>, dummy_ext_bang: Arc<SyntaxExtension>, dummy_ext_derive: Arc<SyntaxExtension>, @@ -1181,7 +1162,7 @@ pub struct Resolver<'ra, 'tcx> { /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. /// Also includes of list of each fields visibility - struct_constructors: LocalDefIdMap<(Res, ty::Visibility<DefId>, Vec<ty::Visibility<DefId>>)>, + struct_constructors: LocalDefIdMap<(Res, Visibility<DefId>, Vec<Visibility<DefId>>)>, lint_buffer: LintBuffer, @@ -1255,6 +1236,32 @@ pub struct ResolverArenas<'ra> { } impl<'ra> ResolverArenas<'ra> { + fn new_res_binding( + &'ra self, + res: Res, + vis: Visibility<DefId>, + span: Span, + expansion: LocalExpnId, + ) -> NameBinding<'ra> { + self.alloc_name_binding(NameBindingData { + kind: NameBindingKind::Res(res), + ambiguity: None, + warn_ambiguity: false, + vis, + span, + expansion, + }) + } + + fn new_pub_res_binding( + &'ra self, + res: Res, + span: Span, + expn_id: LocalExpnId, + ) -> NameBinding<'ra> { + self.new_res_binding(res, Visibility::Public, span, expn_id) + } + fn new_module( &'ra self, parent: Option<Module<'ra>>, @@ -1262,26 +1269,25 @@ impl<'ra> ResolverArenas<'ra> { expn_id: ExpnId, span: Span, no_implicit_prelude: bool, - module_map: &mut FxIndexMap<DefId, Module<'ra>>, - module_self_bindings: &mut FxHashMap<Module<'ra>, NameBinding<'ra>>, ) -> Module<'ra> { + let (def_id, self_binding) = match kind { + ModuleKind::Def(def_kind, def_id, _) => ( + Some(def_id), + Some(self.new_pub_res_binding(Res::Def(def_kind, def_id), span, LocalExpnId::ROOT)), + ), + ModuleKind::Block => (None, None), + }; let module = Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( parent, kind, expn_id, span, no_implicit_prelude, + self_binding, )))); - let def_id = module.opt_def_id(); if def_id.is_none_or(|def_id| def_id.is_local()) { self.local_modules.borrow_mut().push(module); } - if let Some(def_id) = def_id { - module_map.insert(def_id, module); - let vis = ty::Visibility::<DefId>::Public; - let binding = (module, vis, module.span, LocalExpnId::ROOT).to_name_binding(self); - module_self_bindings.insert(module, binding); - } module } fn local_modules(&'ra self) -> std::cell::Ref<'ra, Vec<Module<'ra>>> { @@ -1422,25 +1428,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { arenas: &'ra ResolverArenas<'ra>, ) -> Resolver<'ra, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); - let mut module_map = FxIndexMap::default(); - let mut module_self_bindings = FxHashMap::default(); + let mut local_module_map = FxIndexMap::default(); let graph_root = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), ExpnId::root(), crate_span, attr::contains_name(attrs, sym::no_implicit_prelude), - &mut module_map, - &mut module_self_bindings, ); + local_module_map.insert(CRATE_DEF_ID, graph_root); let empty_module = arenas.new_module( None, ModuleKind::Def(DefKind::Mod, root_def_id, None), ExpnId::root(), DUMMY_SP, true, - &mut Default::default(), - &mut Default::default(), ); let mut node_id_to_def_id = NodeMap::default(); @@ -1470,8 +1472,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let registered_tools = tcx.registered_tools(()); - - let pub_vis = ty::Visibility::<DefId>::Public; let edition = tcx.sess.edition(); let mut resolver = Resolver { @@ -1503,7 +1503,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { trait_map: NodeMap::default(), underscore_disambiguator: 0, empty_module, - module_map, + local_module_map, + extern_module_map: Default::default(), block_map: Default::default(), binding_parent_modules: FxHashMap::default(), ast_transform_scopes: FxHashMap::default(), @@ -1520,12 +1521,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { macro_expanded_macro_export_errors: BTreeSet::new(), arenas, - dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas), + dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT), builtin_types_bindings: PrimTy::ALL .iter() .map(|prim_ty| { - let binding = (Res::PrimTy(*prim_ty), pub_vis, DUMMY_SP, LocalExpnId::ROOT) - .to_name_binding(arenas); + let res = Res::PrimTy(*prim_ty); + let binding = arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT); (prim_ty.name(), binding) }) .collect(), @@ -1533,21 +1534,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .iter() .map(|builtin_attr| { let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(builtin_attr.name)); - let binding = - (res, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas); + let binding = arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT); (builtin_attr.name, binding) }) .collect(), registered_tool_bindings: registered_tools .iter() .map(|ident| { - let binding = (Res::ToolMod, pub_vis, ident.span, LocalExpnId::ROOT) - .to_name_binding(arenas); + let res = Res::ToolMod; + let binding = arenas.new_pub_res_binding(res, ident.span, LocalExpnId::ROOT); (*ident, binding) }) .collect(), - module_self_bindings, - used_extern_options: Default::default(), macro_names: FxHashSet::default(), builtin_macros: Default::default(), @@ -1605,12 +1603,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let root_parent_scope = ParentScope::module(graph_root, &resolver); resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope); - resolver.feed_visibility(crate_feed, ty::Visibility::Public); + resolver.feed_visibility(crate_feed, Visibility::Public); resolver } - fn new_module( + fn new_local_module( &mut self, parent: Option<Module<'ra>>, kind: ModuleKind, @@ -1618,17 +1616,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> Module<'ra> { - let module_map = &mut self.module_map; - let module_self_bindings = &mut self.module_self_bindings; - self.arenas.new_module( - parent, - kind, - expn_id, - span, - no_implicit_prelude, - module_map, - module_self_bindings, - ) + let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude); + if let Some(def_id) = module.opt_def_id() { + self.local_module_map.insert(def_id.expect_local(), module); + } + module + } + + fn new_extern_module( + &self, + parent: Option<Module<'ra>>, + kind: ModuleKind, + expn_id: ExpnId, + span: Span, + no_implicit_prelude: bool, + ) -> Module<'ra> { + let module = self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude); + self.extern_module_map.borrow_mut().insert(module.def_id(), module); + module } fn new_local_macro(&mut self, def_id: LocalDefId, macro_data: MacroData) -> &'ra MacroData { @@ -1659,7 +1664,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Default::default() } - fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: ty::Visibility) { + fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: Visibility) { let feed = feed.upgrade(self.tcx); feed.visibility(vis.to_def_id()); self.visibilities_for_hashing.push((feed.def_id(), vis)); @@ -1775,9 +1780,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let exported_ambiguities = self.tcx.sess.time("compute_effective_visibilities", || { EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate) }); - self.tcx.sess.time("check_hidden_glob_reexports", || { - self.check_hidden_glob_reexports(exported_ambiguities) - }); + self.tcx.sess.time("lint_reexports", || self.lint_reexports(exported_ambiguities)); self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); @@ -1837,10 +1840,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { module.ensure_traits(self); let traits = module.traits.borrow(); - for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() { - if self.trait_may_have_item(trait_binding.module(), assoc_item) { + for &(trait_name, trait_binding, trait_module) in traits.as_ref().unwrap().iter() { + if self.trait_may_have_item(trait_module, assoc_item) { let def_id = trait_binding.res().def_id(); - let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name); + let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name); found_traits.push(TraitCandidate { def_id, import_ids }); } } @@ -2016,7 +2019,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - fn resolve_crate_root(&mut self, ident: Ident) -> Module<'ra> { + fn resolve_crate_root(&self, ident: Ident) -> Module<'ra> { debug!("resolve_crate_root({:?})", ident); let mut ctxt = ident.span.ctxt(); let mark = if ident.name == kw::DollarCrate { @@ -2089,7 +2092,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module } - fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'ra>) -> Module<'ra> { + fn resolve_self(&self, ctxt: &mut SyntaxContext, module: Module<'ra>) -> Module<'ra> { let mut module = self.expect_module(module.nearest_parent_mod()); while module.span.ctxt().normalize_to_macros_2_0() != *ctxt { let parent = module.parent.unwrap_or_else(|| self.expn_def_scope(ctxt.remove_mark())); @@ -2110,11 +2113,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.pat_span_map.insert(node, span); } - fn is_accessible_from( - &self, - vis: ty::Visibility<impl Into<DefId>>, - module: Module<'ra>, - ) -> bool { + fn is_accessible_from(&self, vis: Visibility<impl Into<DefId>>, module: Module<'ra>) -> bool { vis.is_accessible_from(module.nearest_parent_mod(), self.tcx) } @@ -2174,9 +2173,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { self.crate_loader(|c| c.maybe_process_path_extern(ident.name))? }; - let crate_root = self.expect_module(crate_id.as_def_id()); - let vis = ty::Visibility::<DefId>::Public; - (crate_root, vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(self.arenas) + let res = Res::Def(DefKind::Mod, crate_id.as_def_id()); + self.arenas.new_pub_res_binding(res, DUMMY_SP, LocalExpnId::ROOT) }) }); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index cbb24289f51..77ef7f56c09 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -20,7 +20,7 @@ use rustc_expand::expand::{ use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_middle::middle::stability; -use rustc_middle::ty::{RegisteredTools, TyCtxt, Visibility}; +use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, @@ -41,7 +41,7 @@ use crate::imports::Import; use crate::{ BindingKey, DeriveData, Determinacy, Finalize, InvocationParent, MacroData, ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, ResolutionError, - Resolver, ScopeSet, Segment, ToNameBinding, Used, + Resolver, ScopeSet, Segment, Used, }; type Res = def::Res<NodeId>; @@ -170,7 +170,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.invocation_parents[&id].parent_def } - fn resolve_dollar_crates(&mut self) { + fn resolve_dollar_crates(&self) { hygiene::update_dollar_crate_names(|ctxt| { let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt)); match self.resolve_crate_root(ident).kind { @@ -436,8 +436,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { .iter() .map(|(_, ident)| { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - let binding = (res, Visibility::<DefId>::Public, ident.span, expn_id) - .to_name_binding(self.arenas); + let binding = self.arenas.new_pub_res_binding(res, ident.span, expn_id); (*ident, binding) }) .collect(); @@ -836,7 +835,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { - let check_consistency = |this: &mut Self, + let check_consistency = |this: &Self, path: &[Segment], span, kind: MacroKind, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index f61cd1f0adf..24e15ded94f 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -7,6 +7,7 @@ use pulldown_cmark::{ }; use rustc_ast as ast; use rustc_ast::attr::AttributeExt; +use rustc_ast::join_path_syms; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordSet; @@ -259,7 +260,7 @@ pub fn main_body_opts() -> Options { | Options::ENABLE_SMART_PUNCTUATION } -fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> { +fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<Symbol, MalformedGenerics> { let mut stripped_segment = String::new(); let mut param_depth = 0; @@ -284,7 +285,7 @@ fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, Malfor } if param_depth == 0 { - Ok(stripped_segment) + Ok(Symbol::intern(&stripped_segment)) } else { // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>` Err(MalformedGenerics::UnbalancedAngleBrackets) @@ -346,9 +347,8 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen debug!("path_str: {path_str:?}\nstripped segments: {stripped_segments:?}"); - let stripped_path = stripped_segments.join("::"); - - if !stripped_path.is_empty() { + if !stripped_segments.is_empty() { + let stripped_path = join_path_syms(stripped_segments); Ok(stripped_path.into()) } else { Err(MalformedGenerics::MissingType) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a91e2140fd4..d6215e1de04 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1708,6 +1708,11 @@ impl RustcOptGroup { OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), }; } + + /// This is for diagnostics-only. + pub fn long_name(&self) -> &str { + self.long_name + } } pub fn make_opt( diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 626262c8442..2bdde2f887a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2256,6 +2256,8 @@ options! { environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(target_thread_local)` directive"), + higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED], + "allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"), hint_mostly_unused: bool = (false, parse_bool, [TRACKED], "hint that most of this crate will go unused, to minimize work for uncalled functions"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 0118cdb1fc2..9097b27b86c 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -205,6 +205,46 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>( } } +/// This is only used by unstable_feature_bound as it does not have issue number information for now. +/// This is basically the same as `feature_err_issue` +/// but without the feature issue note. If we can do a lookup for issue number from feature name, +/// then we should directly use `feature_err_issue` for ambiguity error of +/// `#[unstable_feature_bound]`. +#[track_caller] +pub fn feature_err_unstable_feature_bound( + sess: &Session, + feature: Symbol, + span: impl Into<MultiSpan>, + explain: impl Into<DiagMessage>, +) -> Diag<'_> { + 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() + } + } + + let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); + + // #23973: do not suggest `#![feature(...)]` if we are in beta/stable + if sess.psess.unstable_features.is_nightly_build() { + err.subdiagnostic(FeatureDiagnosticHelp { feature }); + + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { + err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); + } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { + err.subdiagnostic(suggestion); + } + } + err +} + /// Info about a parsing session. pub struct ParseSess { dcx: DiagCtxt, diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 641bac88ad0..77f01548bca 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -110,6 +110,7 @@ impl DefPathHash { /// Builds a new [DefPathHash] with the given [StableCrateId] and /// `local_hash`, where `local_hash` must be unique within its crate. + #[inline] pub fn new(stable_crate_id: StableCrateId, local_hash: Hash64) -> DefPathHash { DefPathHash(Fingerprint::new(stable_crate_id.0, local_hash)) } @@ -404,21 +405,21 @@ rustc_data_structures::define_id_collections!( impl<CTX: HashStableContext> HashStable<CTX> for DefId { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); + hcx.def_path_hash(*self).hash_stable(hcx, hasher); } } impl<CTX: HashStableContext> HashStable<CTX> for LocalDefId { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); + hcx.def_path_hash(self.to_def_id()).local_hash().hash_stable(hcx, hasher); } } impl<CTX: HashStableContext> HashStable<CTX> for CrateNum { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); + self.as_def_id().to_stable_hash_key(hcx).stable_crate_id().hash_stable(hcx, hasher); } } @@ -464,30 +465,36 @@ macro_rules! typed_def_id { pub struct $Name(DefId); impl $Name { + #[inline] pub const fn new_unchecked(def_id: DefId) -> Self { Self(def_id) } + #[inline] pub fn to_def_id(self) -> DefId { self.into() } + #[inline] pub fn is_local(self) -> bool { self.0.is_local() } + #[inline] pub fn as_local(self) -> Option<$LocalName> { self.0.as_local().map($LocalName::new_unchecked) } } impl From<$LocalName> for $Name { + #[inline] fn from(local: $LocalName) -> Self { Self(local.0.to_def_id()) } } impl From<$Name> for DefId { + #[inline] fn from(typed: $Name) -> Self { typed.0 } @@ -500,26 +507,31 @@ macro_rules! typed_def_id { impl !PartialOrd for $LocalName {} impl $LocalName { + #[inline] pub const fn new_unchecked(def_id: LocalDefId) -> Self { Self(def_id) } + #[inline] pub fn to_def_id(self) -> DefId { self.0.into() } + #[inline] pub fn to_local_def_id(self) -> LocalDefId { self.0 } } impl From<$LocalName> for LocalDefId { + #[inline] fn from(typed: $LocalName) -> Self { typed.0 } } impl From<$LocalName> for DefId { + #[inline] fn from(typed: $LocalName) -> Self { typed.0.into() } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b12edf426c..d28a73bc139 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -715,6 +715,7 @@ symbols! { const_indexing, const_let, const_loop, + const_make_global, const_mut_refs, const_panic, const_panic_fmt, @@ -839,6 +840,7 @@ symbols! { derive, derive_coerce_pointee, derive_const, + derive_const_issue: "118304", derive_default_enum, derive_smart_pointer, destruct, @@ -2284,6 +2286,7 @@ symbols! { unsized_locals, unsized_tuple_coercion, unstable, + unstable_feature_bound, unstable_location_reason_default: "this crate is being loaded from the sysroot, an \ unstable location; did you mean to load this crate \ from crates.io via `Cargo.toml` instead?", diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index a9bf5eae445..6bcb7f6e093 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>( // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - tcx.codegen_fn_attrs(def_id) + &tcx.codegen_instance_attrs(instance.def) } else { CodegenFnAttrs::EMPTY }; diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index fd509503053..f95ce756354 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -5,7 +5,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); base.cpu = "mips64r2".into(); - base.features = "+mips64r2".into(); + base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); Target { // LLVM doesn't recognize "muslabi64" yet. diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index aa087b1a35a..d42e097b0fd 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -3,7 +3,7 @@ use crate::spec::{Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); base.cpu = "mips64r2".into(); - base.features = "+mips64r2".into(); + base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bc464b099e2..b9acadc406e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -51,6 +51,7 @@ use std::path::PathBuf; use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; +use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, @@ -73,7 +74,7 @@ use rustc_middle::ty::{ TypeVisitableExt, }; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::error_reporting::TypeErrCtxt; @@ -225,7 +226,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { struct AbsolutePathPrinter<'tcx> { tcx: TyCtxt<'tcx>, - segments: Vec<String>, + segments: Vec<Symbol>, } impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { @@ -253,7 +254,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { - self.segments = vec![self.tcx.crate_name(cnum).to_string()]; + self.segments = vec![self.tcx.crate_name(cnum)]; Ok(()) } fn path_qualified( @@ -279,7 +280,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError> { print_prefix(self)?; - self.segments.push(disambiguated_data.to_string()); + self.segments.push(disambiguated_data.as_sym(true)); Ok(()) } fn path_generic_args( @@ -314,7 +315,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // known" by the same name, we use the "absolute path" which uses the original // crate name instead. let (expected, found) = if expected_str == found_str { - (expected_abs.join("::"), found_abs.join("::")) + (join_path_syms(&expected_abs), join_path_syms(&found_abs)) } else { (expected_str.clone(), found_str.clone()) }; 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 712e88300ff..98f67257fd1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -12,6 +12,7 @@ use rustc_infer::traits::{ Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, }; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; +use rustc_session::parse::feature_err_unstable_feature_bound; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use tracing::{debug, instrument}; @@ -611,6 +612,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) .with_span_label(span, format!("cannot normalize `{alias}`")) } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(sym)) => { + if let Some(e) = self.tainted_by_errors() { + return e; + } + + let mut err; + + if self.tcx.features().staged_api() { + err = self.dcx().struct_span_err( + span, + format!("unstable feature `{sym}` is used without being enabled."), + ); + + err.help(format!("The feature can be enabled by marking the current item with `#[unstable_feature_bound({sym})]`")); + } else { + err = feature_err_unstable_feature_bound( + &self.tcx.sess, + sym, + span, + format!("use of unstable library feature `{sym}`"), + ); + } + err + } _ => { if let Some(e) = self.tainted_by_errors() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index cd3e6c4bc54..1ac309da101 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ty::PredicateKind::ConstEquate { .. } // Ambiguous predicates should never error | ty::PredicateKind::Ambiguous + // We never return Err when proving UnstableFeature goal. + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature{ .. }) | ty::PredicateKind::NormalizesTo { .. } | ty::PredicateKind::AliasRelate { .. } | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType { .. }) => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index d4cc1ceb280..1d8b934cef3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -453,7 +453,7 @@ pub fn report_dyn_incompatibility<'tcx>( let trait_str = tcx.def_path_str(trait_def_id); let trait_span = tcx.hir_get_if_local(trait_def_id).and_then(|node| match node { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(_, _, ident, ..) | hir::ItemKind::TraitAlias(ident, _, _) => { + hir::ItemKind::Trait(_, _, _, ident, ..) | hir::ItemKind::TraitAlias(ident, _, _) => { Some(ident.span) } _ => unreachable!(), 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 bd1d29826e6..bf7d4257b62 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -22,6 +22,7 @@ use rustc_hir::{ expr_needs_parens, is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_middle::middle::privacy::Level; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::{ @@ -267,7 +268,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let node = self.tcx.hir_node_by_def_id(body_id); match node { hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, ident, generics, bounds, _), + kind: hir::ItemKind::Trait(_, _, _, ident, generics, bounds, _), .. }) if self_ty == self.tcx.types.self_param => { assert!(param_ty); @@ -330,7 +331,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } hir::Node::Item(hir::Item { kind: - hir::ItemKind::Trait(_, _, _, generics, ..) + hir::ItemKind::Trait(_, _, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }), .. }) if projection.is_some() => { @@ -354,7 +355,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::ItemKind::Struct(_, generics, _) | hir::ItemKind::Enum(_, generics, _) | hir::ItemKind::Union(_, generics, _) - | hir::ItemKind::Trait(_, _, _, generics, ..) + | hir::ItemKind::Trait(_, _, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } | hir::ItemKind::TyAlias(_, generics, _) @@ -414,7 +415,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { hir::ItemKind::Struct(_, generics, _) | hir::ItemKind::Enum(_, generics, _) | hir::ItemKind::Union(_, generics, _) - | hir::ItemKind::Trait(_, _, _, generics, ..) + | hir::ItemKind::Trait(_, _, _, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn { generics, .. } | hir::ItemKind::TyAlias(_, generics, _) @@ -2870,11 +2871,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::ClauseKind::Trait(trait_pred) => { let def_id = trait_pred.def_id(); let visible_item = if let Some(local) = def_id.as_local() { - // Check for local traits being reachable. - let vis = &tcx.resolutions(()).effective_visibilities; - // Account for non-`pub` traits in the root of the local crate. - let is_locally_reachable = tcx.parent(def_id).is_crate_root(); - vis.is_reachable(local) || is_locally_reachable + let ty = trait_pred.self_ty(); + // when `TraitA: TraitB` and `S` only impl TraitA, + // we check if `TraitB` can be reachable from `S` + // to determine whether to note `TraitA` is sealed trait. + if let ty::Adt(adt, _) = ty.kind() { + let visibilities = tcx.effective_visibilities(()); + visibilities.effective_vis(local).is_none_or(|v| { + v.at_level(Level::Reexported) + .is_accessible_from(adt.did(), tcx) + }) + } else { + // FIXME(xizheyin): if the type is not ADT, we should not suggest it + true + } } else { // Check for foreign traits being reachable. tcx.visible_parent_map(()).get(&def_id).is_some() @@ -3436,7 +3446,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut is_auto_trait = false; match tcx.hir_get_if_local(data.impl_or_alias_def_id) { Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(is_auto, _, ident, ..), + kind: hir::ItemKind::Trait(_, is_auto, _, ident, ..), .. })) => { // FIXME: we should do something else so that it works even on crate foreign diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 90cdf75265d..7901d52dffb 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -534,7 +534,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.scope)) { hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, _, generics, ..), + kind: hir::ItemKind::Trait(_, _, _, _, generics, ..), .. }) | hir::Node::Item(hir::Item { diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 0118321befb..7c6b7b14ecb 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -33,8 +33,8 @@ impl<'tcx> InferCtxt<'tcx> { let ty = self.resolve_vars_if_possible(ty); // FIXME(#132279): This should be removed as it causes us to incorrectly - // handle opaques in their defining scope. - if !self.next_trait_solver() && !(param_env, ty).has_infer() { + // handle opaques in their defining scope, and stalled coroutines. + if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() { return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 068e90b00b8..2b33b8ac9f8 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -4,7 +4,7 @@ use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, elaborate}; use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; @@ -46,6 +46,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> { } } + // FIXME(-Znext-trait-solver): Normalize these. + let higher_ranked_assumptions = infcx.take_registered_region_assumptions(); + let higher_ranked_assumptions = + elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions); + // FIXME: This needs to be modified so that we normalize the known type // outlives obligations then elaborate them into their region/type components. // Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even @@ -59,6 +64,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { assumed_wf_tys, disable_implied_bounds_hack, ), + higher_ranked_assumptions, ) } } diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index 5a5d16167d2..f58961683a9 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -7,7 +7,7 @@ mod normalize; mod select; pub(crate) use delegate::SolverDelegate; -pub use fulfill::{FulfillmentCtxt, NextSolverError}; +pub use fulfill::{FulfillmentCtxt, NextSolverError, StalledOnCoroutines}; pub(crate) use normalize::deeply_normalize_for_diagnostics; pub use normalize::{ deeply_normalize, deeply_normalize_with_skipped_universes, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 1a24254d57f..17429e15cce 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -206,14 +206,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect()) } - fn make_deduplicated_outlives_constraints( - &self, - ) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> { + fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> { // Cannot use `take_registered_region_obligations` as we may compute the response // inside of a `probe` whenever we have multiple choices inside of the solver. let region_obligations = self.0.inner.borrow().region_obligations().to_owned(); + let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned(); let region_constraints = self.0.with_region_constraints(|region_constraints| { - make_query_region_constraints(self.tcx, region_obligations, region_constraints) + make_query_region_constraints( + self.tcx, + region_obligations, + region_constraints, + region_assumptions, + ) }); let mut seen = FxHashSet::default(); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index d56042a5ca2..3ce0f025512 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -10,7 +10,8 @@ use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; use rustc_middle::ty::{ - self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, + self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + TypingMode, }; use rustc_next_trait_solver::delegate::SolverDelegate as _; use rustc_next_trait_solver::solve::{ @@ -254,7 +255,7 @@ where &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx> { - let stalled_generators = match infcx.typing_mode() { + let stalled_coroutines = match infcx.typing_mode() { TypingMode::Analysis { defining_opaque_types_and_generators } => { defining_opaque_types_and_generators } @@ -264,7 +265,7 @@ where | TypingMode::PostAnalysis => return Default::default(), }; - if stalled_generators.is_empty() { + if stalled_coroutines.is_empty() { return Default::default(); } @@ -275,7 +276,7 @@ where .visit_proof_tree( obl.as_goal(), &mut StalledOnCoroutines { - stalled_generators, + stalled_coroutines, span: obl.cause.span, cache: Default::default(), }, @@ -297,10 +298,10 @@ where /// /// This function can be also return false positives, which will lead to poor diagnostics /// so we want to keep this visitor *precise* too. -struct StalledOnCoroutines<'tcx> { - stalled_generators: &'tcx ty::List<LocalDefId>, - span: Span, - cache: DelayedSet<Ty<'tcx>>, +pub struct StalledOnCoroutines<'tcx> { + pub stalled_coroutines: &'tcx ty::List<LocalDefId>, + pub span: Span, + pub cache: DelayedSet<Ty<'tcx>>, } impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> { @@ -330,12 +331,14 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> { } if let ty::CoroutineWitness(def_id, _) = *ty.kind() - && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id)) + && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id)) { - return ControlFlow::Break(()); + ControlFlow::Break(()) + } else if ty.has_coroutines() { + ty.super_visit_with(self) + } else { + ControlFlow::Continue(()) } - - ty.super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 3ae908ec16b..759db1d18c0 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -800,6 +800,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {} ty::PredicateKind::Ambiguous => return false, }; diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index ce5a4edeaaa..f50f01a285b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -461,6 +461,7 @@ fn impl_intersection_has_negative_obligation( // requirements, when proving the negated where clauses below. drop(equate_obligations); drop(infcx.take_registered_region_obligations()); + drop(infcx.take_registered_region_assumptions()); drop(infcx.take_and_reset_region_constraints()); plug_infer_with_placeholders( diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 9a4f3887bbb..ea1eed95723 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -238,6 +238,7 @@ fn predicate_references_self<'tcx>( // FIXME(generic_const_exprs): this can mention `Self` | ty::ClauseKind::ConstEvaluatable(..) | ty::ClauseKind::HostEffect(..) + | ty::ClauseKind::UnstableFeature(_) => None, } } @@ -278,6 +279,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => false, }) } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 64a51e0550b..e35f89358e9 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use rustc_data_structures::obligation_forest::{ Error, ForestObligation, ObligationForest, ObligationProcessor, Outcome, ProcessResult, }; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::{ FromSolverError, PolyTraitObligation, PredicateObligations, ProjectionCacheKey, SelectionError, @@ -11,7 +12,11 @@ use rustc_infer::traits::{ use rustc_middle::bug; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{ + self, Binder, Const, GenericArgsRef, TypeVisitable, TypeVisitableExt, TypingMode, + may_use_unstable_feature, +}; +use rustc_span::DUMMY_SP; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, debug_span, instrument}; @@ -24,6 +29,7 @@ use super::{ }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::solve::StalledOnCoroutines; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -166,8 +172,25 @@ where &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx> { - let mut processor = - DrainProcessor { removed_predicates: PredicateObligations::new(), infcx }; + let stalled_coroutines = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + + if stalled_coroutines.is_empty() { + return Default::default(); + } + + let mut processor = DrainProcessor { + infcx, + removed_predicates: PredicateObligations::new(), + stalled_coroutines, + }; let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor); assert!(outcome.errors.is_empty()); return processor.removed_predicates; @@ -175,6 +198,7 @@ where struct DrainProcessor<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, removed_predicates: PredicateObligations<'tcx>, + stalled_coroutines: &'tcx ty::List<LocalDefId>, } impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> { @@ -183,10 +207,14 @@ where type OUT = Outcome<Self::Obligation, Self::Error>; fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { - pending_obligation - .stalled_on - .iter() - .any(|&var| self.infcx.ty_or_const_infer_var_changed(var)) + self.infcx + .resolve_vars_if_possible(pending_obligation.obligation.predicate) + .visit_with(&mut StalledOnCoroutines { + stalled_coroutines: self.stalled_coroutines, + span: DUMMY_SP, + cache: Default::default(), + }) + .is_break() } fn process_obligation( @@ -404,6 +432,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::AliasRelate(..) => { bug!("AliasRelate is only used by the new solver") } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => { + unreachable!("unexpected higher ranked `UnstableFeature` goal") + } }, Some(pred) => match pred { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { @@ -767,6 +798,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + if may_use_unstable_feature(self.selcx.infcx, obligation.param_env, symbol) { + ProcessResult::Changed(Default::default()) + } else { + ProcessResult::Unchanged + } + } }, } } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 59d3ac21387..53518038f8d 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -78,7 +78,10 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds.retain(|bound| !bound.has_placeholders()); if !constraints.is_empty() { - let QueryRegionConstraints { outlives } = constraints; + // FIXME(higher_ranked_auto): Should we register assumptions here? + // We otherwise would get spurious errors if normalizing an implied + // outlives bound required proving some higher-ranked coroutine obl. + let QueryRegionConstraints { outlives, assumptions: _ } = constraints; let cause = ObligationCause::misc(span, body_id); for &(predicate, _) in &outlives { infcx.register_outlives_constraint(predicate, &cause); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 3b549244431..f027ba1c5cb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -80,6 +80,11 @@ where pre_obligations.is_empty(), "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}", ); + let pre_assumptions = infcx.take_registered_region_assumptions(); + assert!( + pre_assumptions.is_empty(), + "scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}", + ); let value = infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(infcx); @@ -100,11 +105,13 @@ where let value = infcx.resolve_vars_if_possible(value); let region_obligations = infcx.take_registered_region_obligations(); + let region_assumptions = infcx.take_registered_region_assumptions(); let region_constraint_data = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( infcx.tcx, region_obligations, ®ion_constraint_data, + region_assumptions, ); if region_constraints.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e294f7839aa..7540cbe3fd1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -110,6 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 4bdf04311a0..018e9748cf0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -180,8 +180,9 @@ where span, )?; output.error_info = error_info; - if let Some(QueryRegionConstraints { outlives }) = output.constraints { + if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints { region_constraints.outlives.extend(outlives.iter().cloned()); + region_constraints.assumptions.extend(assumptions.iter().cloned()); } output.constraints = if region_constraints.is_empty() { None diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cc188a280aa..2c7089507a8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -842,6 +842,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::CoroutineWitness(def_id, _) => { + if self.should_stall_coroutine_witness(def_id) { + candidates.ambiguous = true; + } else { + candidates.vec.push(AutoImplCandidate); + } + } + ty::Bool | ty::Char | ty::Int(_) @@ -861,7 +869,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Coroutine(..) | ty::Never | ty::Tuple(_) - | ty::CoroutineWitness(..) | ty::UnsafeBinder(_) => { // Only consider auto impls of unsafe traits when there are // no unsafe fields. @@ -1119,12 +1126,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.kind() { // These impls are built-in because we cannot express sufficiently // generic impls in libcore. - ty::FnDef(..) - | ty::FnPtr(..) - | ty::Error(_) - | ty::Tuple(..) - | ty::CoroutineWitness(..) - | ty::Pat(..) => { + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) | ty::Tuple(..) | ty::Pat(..) => { candidates.vec.push(BuiltinCandidate); } @@ -1192,6 +1194,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::CoroutineWitness(coroutine_def_id, _) => { + if self.should_stall_coroutine_witness(coroutine_def_id) { + candidates.ambiguous = true; + } else { + candidates.vec.push(SizedCandidate); + } + } + // Fallback to whatever user-defined impls or param-env clauses exist in this case. ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} @@ -1229,7 +1239,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Char | ty::Ref(..) | ty::Coroutine(..) - | ty::CoroutineWitness(..) | ty::Array(..) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1238,6 +1247,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.vec.push(SizedCandidate); } + ty::CoroutineWitness(coroutine_def_id, _) => { + if self.should_stall_coroutine_witness(coroutine_def_id) { + candidates.ambiguous = true; + } else { + candidates.vec.push(SizedCandidate); + } + } + // Conditionally `Sized`. ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => { candidates.vec.push(SizedCandidate); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ee8cef20279..488094b15ac 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -411,18 +411,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty); - let types = self.constituent_types_for_ty(self_ty)?; - let types = self.infcx.enter_forall_and_leak_universe(types); + let constituents = self.constituent_types_for_auto_trait(self_ty)?; + let constituents = self.infcx.enter_forall_and_leak_universe(constituents); let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - let obligations = self.collect_predicates_for_types( + let mut obligations = self.collect_predicates_for_types( obligation.param_env, - cause, + cause.clone(), obligation.recursion_depth + 1, obligation.predicate.def_id(), - types, + constituents.types, ); + // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types` + // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really + // matter yet. + for assumption in constituents.assumptions { + let assumption = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + assumption, + &mut obligations, + ); + self.infcx.register_region_assumption(assumption); + } + Ok(obligations) }) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2e65750db25..2b563c5b8d5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -20,6 +20,7 @@ use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::relate::TypeRelation; use rustc_infer::traits::{PredicateObligations, TraitObligation}; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; pub use rustc_middle::traits::select::*; @@ -28,7 +29,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{ self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt, - TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, + TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate, may_use_unstable_feature, }; use rustc_span::{Symbol, sym}; use tracing::{debug, instrument, trace}; @@ -832,6 +833,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(symbol)) => { + if may_use_unstable_feature(self.infcx, obligation.param_env, symbol) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToAmbig) + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, @@ -1504,7 +1513,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { defining_opaque_types_and_generators: defining_opaque_types, } | TypingMode::Borrowck { defining_opaque_types } => { - defining_opaque_types.is_empty() || !pred.has_opaque_types() + defining_opaque_types.is_empty() + || (!pred.has_opaque_types() && !pred.has_coroutines()) } // The hidden types of `defined_opaque_types` is not local to the current // inference context, so we can freely move this to the global cache. @@ -2247,10 +2257,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> { /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` #[instrument(level = "debug", skip(self), ret)] - fn constituent_types_for_ty( + fn constituent_types_for_auto_trait( &self, t: Ty<'tcx>, - ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> { + ) -> Result<ty::Binder<'tcx, AutoImplConstituents<'tcx>>, SelectionError<'tcx>> { Ok(match *t.kind() { ty::Uint(_) | ty::Int(_) @@ -2261,17 +2271,26 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never - | ty::Char => ty::Binder::dummy(Vec::new()), + | ty::Char => { + ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] }) + } // This branch is only for `experimental_default_bounds`. // Other foreign types were rejected earlier in // `assemble_candidates_from_auto_impls`. - ty::Foreign(..) => ty::Binder::dummy(Vec::new()), + ty::Foreign(..) => { + ty::Binder::dummy(AutoImplConstituents { types: vec![], assumptions: vec![] }) + } - ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]), + ty::UnsafeBinder(ty) => { + ty.map_bound(|ty| AutoImplConstituents { types: vec![ty], assumptions: vec![] }) + } // Treat this like `struct str([u8]);` - ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]), + ty::Str => ty::Binder::dummy(AutoImplConstituents { + types: vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)], + assumptions: vec![], + }), ty::Placeholder(..) | ty::Dynamic(..) @@ -2283,30 +2302,41 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { - ty::Binder::dummy(vec![element_ty]) + ty::Binder::dummy(AutoImplConstituents { + types: vec![element_ty], + assumptions: vec![], + }) } - ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]), + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => { + ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] }) + } ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - ty::Binder::dummy(tys.iter().collect()) + ty::Binder::dummy(AutoImplConstituents { + types: tys.iter().collect(), + assumptions: vec![], + }) } ty::Closure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); - ty::Binder::dummy(vec![ty]) + ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] }) } ty::CoroutineClosure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); - ty::Binder::dummy(vec![ty]) + ty::Binder::dummy(AutoImplConstituents { types: vec![ty], assumptions: vec![] }) } ty::Coroutine(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let witness = args.as_coroutine().witness(); - ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect()) + ty::Binder::dummy(AutoImplConstituents { + types: [ty].into_iter().chain(iter::once(witness)).collect(), + assumptions: vec![], + }) } ty::CoroutineWitness(def_id, args) => self @@ -2314,16 +2344,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> { .tcx .coroutine_hidden_types(def_id) .instantiate(self.infcx.tcx, args) - .map_bound(|witness| witness.types.to_vec()), + .map_bound(|witness| AutoImplConstituents { + types: witness.types.to_vec(), + assumptions: witness.assumptions.to_vec(), + }), // For `PhantomData<T>`, we pass `T`. ty::Adt(def, args) if def.is_phantom_data() => { - ty::Binder::dummy(args.types().collect()) + ty::Binder::dummy(AutoImplConstituents { + types: args.types().collect(), + assumptions: vec![], + }) } - ty::Adt(def, args) => { - ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) - } + ty::Adt(def, args) => ty::Binder::dummy(AutoImplConstituents { + types: def.all_fields().map(|f| f.ty(self.tcx(), args)).collect(), + assumptions: vec![], + }), ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { if self.infcx.can_define_opaque_ty(def_id) { @@ -2333,7 +2370,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // which enforces a DAG between the functions requiring // the auto trait bounds in question. match self.tcx().type_of_opaque(def_id) { - Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]), + Ok(ty) => ty::Binder::dummy(AutoImplConstituents { + types: vec![ty.instantiate(self.tcx(), args)], + assumptions: vec![], + }), Err(_) => { return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); } @@ -2803,6 +2843,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligations } + + fn should_stall_coroutine_witness(&self, def_id: DefId) -> bool { + match self.infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => { + def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id)) + } + TypingMode::Coherence + | TypingMode::PostAnalysis + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false, + } + } } impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { @@ -3113,3 +3165,9 @@ pub(crate) enum ProjectionMatchesProjection { Ambiguous, No, } + +#[derive(Clone, Debug, TypeFoldable, TypeVisitable)] +pub(crate) struct AutoImplConstituents<'tcx> { + pub types: Vec<Ty<'tcx>>, + pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>, +} diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 141454bfe37..0c14b124e25 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -80,6 +80,7 @@ pub fn expand_trait_aliases<'tcx>( | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::HostEffect(..) => {} } } @@ -378,6 +379,13 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc _ => return false, }; + // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature + // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` + // is pending a proper fix + if !tcx.features().sized_hierarchy() && matches!(sizedness, SizedTraitKind::MetaSized) { + return true; + } + if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) { debug!("fast path -- trivial sizedness"); return true; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index fed9f254cdf..adce9850b59 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -197,6 +197,7 @@ pub fn clause_obligations<'tcx>( ty::ClauseKind::ConstEvaluatable(ct) => { wf.add_wf_preds_for_term(ct.into()); } + ty::ClauseKind::UnstableFeature(_) => {} } wf.normalize(infcx) @@ -1095,6 +1096,7 @@ pub fn object_region_bounds<'tcx>( | ty::ClauseKind::Projection(_) | ty::ClauseKind::ConstArgHasType(_, _) | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::UnstableFeature(_) | ty::ClauseKind::ConstEvaluatable(_) => None, } }) diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 447e13126cc..87d17f3e131 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -1,5 +1,10 @@ use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, TyCtxt, fold_regions}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::canonical::query_response::make_query_region_constraints; +use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::traits::{Obligation, ObligationCause}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions}; +use rustc_trait_selection::traits::{ObligationCtxt, with_replaced_escaping_bound_vars}; /// Return the set of types that should be taken into account when checking /// trait bounds on a coroutine's internal state. This properly replaces @@ -30,8 +35,57 @@ pub(crate) fn coroutine_hidden_types<'tcx>( }), ); + let assumptions = compute_assumptions(tcx, def_id, bound_tys); + ty::EarlyBinder::bind(ty::Binder::bind_with_vars( - ty::CoroutineWitnessTypes { types: bound_tys }, + ty::CoroutineWitnessTypes { types: bound_tys, assumptions }, tcx.mk_bound_variable_kinds(&vars), )) } + +fn compute_assumptions<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + bound_tys: &'tcx ty::List<Ty<'tcx>>, +) -> &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> { + let infcx = tcx.infer_ctxt().build(ty::TypingMode::Analysis { + defining_opaque_types_and_generators: ty::List::empty(), + }); + with_replaced_escaping_bound_vars(&infcx, &mut vec![None], bound_tys, |bound_tys| { + let param_env = tcx.param_env(def_id); + let ocx = ObligationCtxt::new(&infcx); + + ocx.register_obligations(bound_tys.iter().map(|ty| { + Obligation::new( + tcx, + ObligationCause::dummy(), + param_env, + ty::ClauseKind::WellFormed(ty.into()), + ) + })); + let _errors = ocx.select_all_or_error(); + + let region_obligations = infcx.take_registered_region_obligations(); + let region_assumptions = infcx.take_registered_region_assumptions(); + let region_constraints = infcx.take_and_reset_region_constraints(); + + let outlives = make_query_region_constraints( + tcx, + region_obligations, + ®ion_constraints, + region_assumptions, + ) + .outlives + .fold_with(&mut OpportunisticRegionResolver::new(&infcx)); + + tcx.mk_outlives_from_iter( + outlives + .into_iter() + .map(|(o, _)| o) + // FIXME(higher_ranked_auto): We probably should deeply resolve these before + // filtering out infers which only correspond to unconstrained infer regions + // which we can sometimes get. + .filter(|o| !o.has_infer()), + ) + }) +} diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 68ff66bbce7..c1b848a2e79 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -57,6 +57,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 3b313edea6f..4a7263d0ccd 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -223,7 +223,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { } // Skips type aliases, as they are meant to be transparent. // FIXME(type_alias_impl_trait): can we require mentioning nested type aliases explicitly? - ty::Alias(ty::Free, alias_ty) if alias_ty.def_id.is_local() => { + ty::Alias(ty::Free, alias_ty) if let Some(def_id) = alias_ty.def_id.as_local() => { + if !self.seen.insert(def_id) { + return; + } self.tcx .type_of(alias_ty.def_id) .instantiate(self.tcx, alias_ty.args) @@ -256,16 +259,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { return; } - let impl_args = alias_ty.args.rebase_onto( + let alias_args = alias_ty.args.rebase_onto( self.tcx, impl_trait_ref.def_id, ty::GenericArgs::identity_for_item(self.tcx, parent), ); - if self.tcx.check_args_compatible(assoc.def_id, impl_args) { + if self.tcx.check_args_compatible(assoc.def_id, alias_args) { self.tcx .type_of(assoc.def_id) - .instantiate(self.tcx, impl_args) + .instantiate(self.tcx, alias_args) .visit_with(self); return; } else { diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 7ffcf7b5d96..dc15cc3eb55 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -234,6 +234,9 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { ty::ClauseKind::ConstArgHasType(..) => { // Nothing to elaborate } + ty::ClauseKind::UnstableFeature(_) => { + // Nothing to elaborate + } } } } @@ -368,3 +371,54 @@ impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I, (0, upper) } } + +pub fn elaborate_outlives_assumptions<I: Interner>( + cx: I, + assumptions: impl IntoIterator<Item = ty::OutlivesPredicate<I, I::GenericArg>>, +) -> HashSet<ty::OutlivesPredicate<I, I::GenericArg>> { + let mut collected = HashSet::default(); + + for ty::OutlivesPredicate(arg1, r2) in assumptions { + collected.insert(ty::OutlivesPredicate(arg1, r2)); + match arg1.kind() { + // Elaborate the components of an type, since we may have substituted a + // generic coroutine with a more specific type. + ty::GenericArgKind::Type(ty1) => { + let mut components = smallvec![]; + push_outlives_components(cx, ty1, &mut components); + for c in components { + match c { + Component::Region(r1) => { + if !r1.is_bound() { + collected.insert(ty::OutlivesPredicate(r1.into(), r2)); + } + } + + Component::Param(p) => { + let ty = Ty::new_param(cx, p); + collected.insert(ty::OutlivesPredicate(ty.into(), r2)); + } + + Component::Placeholder(p) => { + let ty = Ty::new_placeholder(cx, p); + collected.insert(ty::OutlivesPredicate(ty.into(), r2)); + } + + Component::Alias(alias_ty) => { + collected.insert(ty::OutlivesPredicate(alias_ty.to_ty(cx).into(), r2)); + } + + Component::UnresolvedInferenceVariable(_) | Component::EscapingAlias(_) => { + } + } + } + } + // Nothing to elaborate for a region. + ty::GenericArgKind::Lifetime(_) => {} + // Consts don't really participate in outlives. + ty::GenericArgKind::Const(_) => {} + } + } + + collected +} diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 37cc2baa402..d7b9e0ca340 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -130,6 +130,12 @@ bitflags::bitflags! { /// Does this have any binders with bound vars (e.g. that need to be anonymized)? const HAS_BINDER_VARS = 1 << 23; + + /// Does this type have any coroutine witnesses in it? + // FIXME: This should probably be changed to track whether the type has any + // *coroutines* in it, though this will happen if we remove coroutine witnesses + // altogether. + const HAS_TY_CORO = 1 << 24; } } @@ -240,10 +246,12 @@ impl<I: Interner> FlagComputation<I> { self.add_flags(TypeFlags::HAS_TY_PARAM); } - ty::Closure(_, args) - | ty::Coroutine(_, args) - | ty::CoroutineClosure(_, args) - | ty::CoroutineWitness(_, args) => { + ty::Closure(_, args) | ty::Coroutine(_, args) | ty::CoroutineClosure(_, args) => { + self.add_args(args.as_slice()); + } + + ty::CoroutineWitness(_, args) => { + self.add_flags(TypeFlags::HAS_TY_CORO); self.add_args(args.as_slice()); } @@ -401,7 +409,6 @@ impl<I: Interner> FlagComputation<I> { self.add_const(expected); self.add_const(found); } - ty::PredicateKind::Ambiguous => {} ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => { self.add_alias_term(alias); self.add_term(term); @@ -410,6 +417,8 @@ impl<I: Interner> FlagComputation<I> { self.add_term(t1); self.add_term(t2); } + ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_sym)) => {} + ty::PredicateKind::Ambiguous => {} } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 6c77a90250a..e86a2305e23 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -285,3 +285,40 @@ pub trait InferCtxtLike: Sized { fn reset_opaque_types(&self); } + +pub fn may_use_unstable_feature<'a, I: Interner, Infcx>( + infcx: &'a Infcx, + param_env: I::ParamEnv, + symbol: I::Symbol, +) -> bool +where + Infcx: InferCtxtLike<Interner = I>, +{ + // Iterate through all goals in param_env to find the one that has the same symbol. + for pred in param_env.caller_bounds().iter() { + if let ty::ClauseKind::UnstableFeature(sym) = pred.kind().skip_binder() { + if sym == symbol { + return true; + } + } + } + + // During codegen we must assume that all feature bounds hold as we may be + // monomorphizing a body from an upstream crate which had an unstable feature + // enabled that we do not. + // + // Coherence should already report overlap errors involving unstable impls + // as the affected code would otherwise break when stabilizing this feature. + // It is also easily possible to accidentally cause unsoundness this way as + // we have to always enable unstable impls during codegen. + // + // Return ambiguity can also prevent people from writing code which depends on inference guidance + // that might no longer work after the impl is stabilised, + // tests/ui/unstable-feature-bound/unstable_impl_method_selection.rs is one of the example. + // + // Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled + // if we are in std/core even if there is a corresponding `feature` attribute on the crate. + + (infcx.typing_mode() == TypingMode::PostAnalysis) + || infcx.cx().features().feature_bound_holds_in_crate(symbol) +} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 2754d40fd36..0e307e15d5b 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -630,6 +630,8 @@ pub trait Features<I: Interner>: Copy { fn coroutine_clone(self) -> bool; fn associated_const_equality(self) -> bool; + + fn feature_bound_holds_in_crate(self, symbol: I::Symbol) -> bool; } pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index dd3cf1fc181..e3231244577 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -106,6 +106,7 @@ pub trait Interner: type ParamTy: ParamLike; type BoundTy: BoundVarLike<Self>; type PlaceholderTy: PlaceholderLike<Self, Bound = Self::BoundTy>; + type Symbol: Copy + Hash + PartialEq + Eq + Debug; // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; @@ -145,6 +146,13 @@ pub trait Interner: type BoundRegion: BoundVarLike<Self>; type PlaceholderRegion: PlaceholderLike<Self, Bound = Self::BoundRegion>; + type RegionAssumptions: Copy + + Debug + + Hash + + Eq + + SliceLike<Item = ty::OutlivesPredicate<Self, Self::GenericArg>> + + TypeFoldable<Self>; + // Predicates type ParamEnv: ParamEnv<Self>; type Predicate: Predicate<Self>; diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 4e41fd16ffd..8bc15ec4ff5 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -46,6 +46,13 @@ pub enum ClauseKind<I: Interner> { /// corresponding trait clause; this just enforces the *constness* of that /// implementation. HostEffect(ty::HostEffectPredicate<I>), + + /// Support marking impl as unstable. + UnstableFeature( + #[type_foldable(identity)] + #[type_visitable(ignore)] + I::Symbol, + ), } #[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)] @@ -134,6 +141,9 @@ impl<I: Interner> fmt::Debug for ClauseKind<I> { ClauseKind::ConstEvaluatable(ct) => { write!(f, "ConstEvaluatable({ct:?})") } + ClauseKind::UnstableFeature(feature_name) => { + write!(f, "UnstableFeature({feature_name:?})") + } } } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index db6fbefcf05..7c665424750 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1150,4 +1150,5 @@ pub struct FnHeader<I: Interner> { #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineWitnessTypes<I: Interner> { pub types: I::Tys, + pub assumptions: I::RegionAssumptions, } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a96ac97f785..5104484e9c4 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -269,6 +269,10 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } + fn has_coroutines(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_CORO) + } + fn references_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_ERROR) } |
