diff --git a/Cargo.lock b/Cargo.lock index ff21d5f8c0861..e1b0de6c7a4ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4557,9 +4557,7 @@ dependencies = [ "rustc-demangle", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", - "rustc_macros", "rustc_middle", "rustc_session", "rustc_span", @@ -4694,6 +4692,7 @@ dependencies = [ "arrayvec", "askama", "expect-test", + "indexmap 2.0.0", "itertools", "minifier", "once_cell", diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 54274f153564b..a53fd4ae95a5d 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -18,7 +18,6 @@ #![feature(new_uninit)] #![feature(maybe_uninit_slice)] #![feature(decl_macro)] -#![feature(pointer_byte_offsets)] #![feature(rustc_attrs)] #![cfg_attr(test, feature(test))] #![feature(strict_provenance)] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e94e26a63bc6f..e09ba03d881a6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -734,6 +734,8 @@ pub enum RangeSyntax { } /// All the different flavors of pattern that Rust recognizes. +// +// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum PatKind { /// Represents a wildcard pattern (`_`). @@ -967,6 +969,7 @@ impl Stmt { } } +// Adding a new variant? Please update `test_stmt` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum StmtKind { /// A local (let) binding. @@ -1345,6 +1348,7 @@ pub struct StructExpr { pub rest: StructRest, } +// Adding a new variant? Please update `test_expr` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// An array (`[a, b, c, d]`) @@ -2015,6 +2019,8 @@ pub struct BareFnTy { } /// The various kinds of type recognized by the compiler. +// +// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum TyKind { /// A variable-length slice (`[T]`). @@ -2880,6 +2886,7 @@ pub struct ConstItem { pub expr: Option>, } +// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { /// An `extern crate` item, with the optional *original* crate name if the crate was renamed. diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4f1b13870fd46..b82f878ea87e0 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -188,7 +188,7 @@ impl<'hir> LoweringContext<'_, 'hir> { e.id, None, e.span, - hir::AsyncCoroutineKind::Block, + hir::CoroutineSource::Block, |this| this.with_new_scopes(|this| this.lower_block_expr(block)), ), ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr), @@ -598,7 +598,7 @@ impl<'hir> LoweringContext<'_, 'hir> { closure_node_id: NodeId, ret_ty: Option>, span: Span, - async_gen_kind: hir::AsyncCoroutineKind, + async_gen_kind: hir::CoroutineSource, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::ExprKind<'hir> { let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span))); @@ -1005,7 +1005,7 @@ impl<'hir> LoweringContext<'_, 'hir> { inner_closure_id, async_ret_ty, body.span, - hir::AsyncCoroutineKind::Closure, + hir::CoroutineSource::Closure, |this| this.with_new_scopes(|this| this.lower_expr_mut(body)), ); let hir_id = this.lower_node_id(inner_closure_id); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 3c165f87dbf3d..ca851bd4d35f0 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1206,7 +1206,7 @@ impl<'hir> LoweringContext<'_, 'hir> { closure_id, None, body.span, - hir::AsyncCoroutineKind::Fn, + hir::CoroutineSource::Fn, |this| { // Create a block from the user's function body: let user_body = this.lower_block_expr(body); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1e51449e70cc6..04a8e2b134a60 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1217,7 +1217,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id: this.lower_node_id(node_id), body: this.lower_const_body(path_expr.span, Some(&path_expr)), }); - return GenericArg::Const(ConstArg { value: ct, span }); + return GenericArg::Const(ConstArg { + value: ct, + span, + is_desugared_from_effects: false, + }); } } } @@ -1228,6 +1232,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg { value: self.lower_anon_const(&ct), span: self.lower_span(ct.value.span), + is_desugared_from_effects: false, }), } } @@ -2525,6 +2530,7 @@ impl<'hir> GenericArgsCtor<'hir> { self.args.push(hir::GenericArg::Const(hir::ConstArg { value: hir::AnonConst { def_id, hir_id, body }, span, + is_desugared_from_effects: true, })) } diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 44ba495721d20..363ba0d4a815b 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -13,6 +13,7 @@ use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Session; use rustc_span::hygiene::Transparency; use rustc_span::{symbol::sym, symbol::Symbol, Span}; +use std::fmt::{self, Display}; use std::num::NonZeroU32; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -23,10 +24,7 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; /// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; -pub fn rust_version_symbol() -> Symbol { - let version = option_env!("CFG_RELEASE").unwrap_or(""); - Symbol::intern(&version) -} +pub const CURRENT_RUSTC_VERSION: &str = env!("CFG_RELEASE"); pub fn is_builtin_attr(attr: &Attribute) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) @@ -144,13 +142,24 @@ pub enum StabilityLevel { /// `#[stable]` Stable { /// Rust release which stabilized this feature. - since: Symbol, + since: Since, /// Is this item allowed to be referred to on stable, despite being contained in unstable /// modules? allowed_through_unstable_modules: bool, }, } +/// Rust release in which a feature is stabilized. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum Since { + Version(Version), + /// Stabilized in the upcoming version, whatever number that is. + Current, + /// Failed to parse a stabilization version. + Err, +} + impl StabilityLevel { pub fn is_unstable(&self) -> bool { matches!(self, StabilityLevel::Unstable { .. }) @@ -372,22 +381,24 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit let since = if let Some(since) = since { if since.as_str() == VERSION_PLACEHOLDER { - Ok(rust_version_symbol()) - } else if parse_version(since.as_str(), false).is_some() { - Ok(since) + Since::Current + } else if let Some(version) = parse_version(since.as_str(), false) { + Since::Version(version) } else { - Err(sess.emit_err(session_diagnostics::InvalidSince { span: attr.span })) + sess.emit_err(session_diagnostics::InvalidSince { span: attr.span }); + Since::Err } } else { - Err(sess.emit_err(session_diagnostics::MissingSince { span: attr.span })) + sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); + Since::Err }; - match (feature, since) { - (Ok(feature), Ok(since)) => { + match feature { + Ok(feature) => { let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; Some((feature, level)) } - (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, + Err(ErrorGuaranteed { .. }) => None, } } @@ -556,11 +567,12 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -struct Version { - major: u16, - minor: u16, - patch: u16, +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable_Generic)] +pub struct Version { + pub major: u16, + pub minor: u16, + pub patch: u16, } fn parse_version(s: &str, allow_appendix: bool) -> Option { @@ -576,6 +588,12 @@ fn parse_version(s: &str, allow_appendix: bool) -> Option { Some(Version { major, minor, patch }) } +impl Display for Version { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) + } +} + /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( @@ -609,7 +627,7 @@ pub fn eval_condition( sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span }); return false; }; - let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap(); + let rustc_version = parse_version(CURRENT_RUSTC_VERSION, true).unwrap(); // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details if sess.assume_incomplete_release { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b5ad02dc688ff..928c25d106179 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -8,7 +8,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{AsyncCoroutineKind, CoroutineKind, LangItem}; +use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; @@ -2506,8 +2506,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let kind = match use_span.coroutine_kind() { Some(coroutine_kind) => match coroutine_kind { CoroutineKind::Async(async_kind) => match async_kind { - AsyncCoroutineKind::Block => "async block", - AsyncCoroutineKind::Closure => "async closure", + CoroutineSource::Block => "async block", + CoroutineSource::Closure => "async closure", _ => bug!("async block/closure expected, but async function found."), }, CoroutineKind::Coroutine => "coroutine", diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index de9ece3faba4d..e630ac7c4faee 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -682,9 +682,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }; let mir_description = match hir.body(body).coroutine_kind { Some(hir::CoroutineKind::Async(gen)) => match gen { - hir::AsyncCoroutineKind::Block => " of async block", - hir::AsyncCoroutineKind::Closure => " of async closure", - hir::AsyncCoroutineKind::Fn => { + hir::CoroutineSource::Block => " of async block", + hir::CoroutineSource::Closure => " of async closure", + hir::CoroutineSource::Fn => { let parent_item = hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); let output = &parent_item diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index f22851d76b31a..c84256f8ff321 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -318,7 +318,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .param_env .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) .fully_perform(self.infcx, DUMMY_SP) - .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty)); + .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) + .ok()?; debug!(?bounds, ?constraints); self.add_outlives_bounds(bounds); constraints diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 7252658d460d0..aa1ce1b929d0f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -37,8 +37,9 @@ //! following snippet //! //! ```rust -//! # #![allow(dead_code)] -//! struct A { x : i32 } +//! struct A { +//! x: i32, +//! } //! //! struct B(i32); //! @@ -74,6 +75,7 @@ //! trait PartialEq { //! fn eq(&self, other: &Self) -> bool; //! } +//! //! impl PartialEq for i32 { //! fn eq(&self, other: &i32) -> bool { //! *self == *other @@ -90,22 +92,22 @@ //! //! ```text //! Struct(vec![FieldInfo { -//! span: -//! name: Some(), -//! self_: , -//! other: vec![, +//! name: Some(), +//! self_: , +//! other: vec![], +//! }]) //! ``` //! //! For the `B` impl, called with `B(a)` and `B(b)`, //! //! ```text //! Struct(vec![FieldInfo { -//! span: , -//! name: None, -//! self_: -//! other: vec![] -//! }]) +//! span: , +//! name: None, +//! self_: , +//! other: vec![], +//! }]) //! ``` //! //! ## Enums @@ -114,33 +116,42 @@ //! == C0(b)`, the SubstructureFields is //! //! ```text -//! EnumMatching(0, , -//! vec![FieldInfo { -//! span: -//! name: None, -//! self_: , -//! other: vec![] -//! }]) +//! EnumMatching( +//! 0, +//! , +//! vec![FieldInfo { +//! span: , +//! name: None, +//! self_: , +//! other: vec![], +//! }], +//! ) //! ``` //! //! For `C1 {x}` and `C1 {x}`, //! //! ```text -//! EnumMatching(1, , -//! vec![FieldInfo { -//! span: -//! name: Some(), -//! self_: , -//! other: vec![] -//! }]) +//! EnumMatching( +//! 1, +//! , +//! vec![FieldInfo { +//! span: , +//! name: Some(), +//! self_: , +//! other: vec![], +//! }], +//! ) //! ``` //! //! For the tags, //! //! ```text //! EnumTag( -//! &[, ], ) +//! &[, ], +//! , +//! ) //! ``` +//! //! Note that this setup doesn't allow for the brute-force "match every variant //! against every other variant" approach, which is bad because it produces a //! quadratic amount of code (see #15375). @@ -154,9 +165,13 @@ //! //! StaticStruct(, Unnamed(vec![])) //! -//! StaticEnum(, -//! vec![(, , Unnamed(vec![])), -//! (, , Named(vec![(, )]))]) +//! StaticEnum( +//! , +//! vec![ +//! (, , Unnamed(vec![])), +//! (, , Named(vec![(, )])), +//! ], +//! ) //! ``` pub use StaticFields::*; @@ -522,7 +537,10 @@ impl<'a> TraitDef<'a> { /// Given that we are deriving a trait `DerivedTrait` for a type like: /// /// ```ignore (only-for-syntax-highlight) - /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> + /// where + /// C: WhereTrait, + /// { /// a: A, /// b: B::Item, /// b1: ::Item, @@ -535,12 +553,13 @@ impl<'a> TraitDef<'a> { /// create an impl like: /// /// ```ignore (only-for-syntax-highlight) - /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where - /// C: WhereTrait, + /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> + /// where + /// C: WhereTrait, /// A: DerivedTrait + B1 + ... + BN, /// B: DerivedTrait + B1 + ... + BN, /// C: DerivedTrait + B1 + ... + BN, - /// B::Item: DerivedTrait + B1 + ... + BN, + /// B::Item: DerivedTrait + B1 + ... + BN, /// ::Item: DerivedTrait + B1 + ... + BN, /// ... /// { @@ -676,65 +695,59 @@ impl<'a> TraitDef<'a> { } })); - { - // Extra scope required here so ty_params goes out of scope before params is moved - - let mut ty_params = params - .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) - .peekable(); - - if ty_params.peek().is_some() { - let ty_param_names: Vec = - ty_params.map(|ty_param| ty_param.ident.name).collect(); - - for field_ty in field_tys { - let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); - - for field_ty_param in field_ty_params { - // if we have already handled this type, skip it - if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind - && let [sole_segment] = &*p.segments - && ty_param_names.contains(&sole_segment.ident.name) - { - continue; - } - let mut bounds: Vec<_> = self - .additional_bounds - .iter() - .map(|p| { - cx.trait_bound( - p.to_path(cx, self.span, type_ident, generics), - self.is_const, - ) - }) - .collect(); - - // Require the current trait. - if !self.skip_path_as_bound { - bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); - } + let ty_param_names: Vec = params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + .map(|ty_param| ty_param.ident.name) + .collect(); - // Add a `Copy` bound if required. - if is_packed && self.needs_copy_as_bound_if_packed { - let p = deriving::path_std!(marker::Copy); - bounds.push(cx.trait_bound( + if !ty_param_names.is_empty() { + for field_ty in field_tys { + let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); + + for field_ty_param in field_ty_params { + // if we have already handled this type, skip it + if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind + && let [sole_segment] = &*p.segments + && ty_param_names.contains(&sole_segment.ident.name) + { + continue; + } + let mut bounds: Vec<_> = self + .additional_bounds + .iter() + .map(|p| { + cx.trait_bound( p.to_path(cx, self.span, type_ident, generics), self.is_const, - )); - } + ) + }) + .collect(); - if !bounds.is_empty() { - let predicate = ast::WhereBoundPredicate { - span: self.span, - bound_generic_params: field_ty_param.bound_generic_params, - bounded_ty: field_ty_param.ty, - bounds, - }; + // Require the current trait. + if !self.skip_path_as_bound { + bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); + } - let predicate = ast::WherePredicate::BoundPredicate(predicate); - where_clause.predicates.push(predicate); - } + // Add a `Copy` bound if required. + if is_packed && self.needs_copy_as_bound_if_packed { + let p = deriving::path_std!(marker::Copy); + bounds.push(cx.trait_bound( + p.to_path(cx, self.span, type_ident, generics), + self.is_const, + )); + } + + if !bounds.is_empty() { + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_generic_params: field_ty_param.bound_generic_params, + bounded_ty: field_ty_param.ty, + bounds, + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); } } } @@ -1026,6 +1039,7 @@ impl<'a> MethodDef<'a> { } /// The normal case uses field access. + /// /// ``` /// #[derive(PartialEq)] /// # struct Dummy; @@ -1038,10 +1052,12 @@ impl<'a> MethodDef<'a> { /// } /// } /// ``` + /// /// But if the struct is `repr(packed)`, we can't use something like /// `&self.x` because that might cause an unaligned ref. So for any trait /// method that takes a reference, we use a local block to force a copy. /// This requires that the field impl `Copy`. + /// /// ```rust,ignore (example) /// # struct A { x: u8, y: u8 } /// impl PartialEq for A { @@ -1053,7 +1069,7 @@ impl<'a> MethodDef<'a> { /// impl Hash for A { /// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { /// ::core::hash::Hash::hash(&{ self.x }, state); - /// ::core::hash::Hash::hash(&{ self.y }, state) + /// ::core::hash::Hash::hash(&{ self.y }, state); /// } /// } /// ``` @@ -1107,7 +1123,9 @@ impl<'a> MethodDef<'a> { /// A2(i32) /// } /// ``` + /// /// is equivalent to: + /// /// ``` /// #![feature(core_intrinsics)] /// enum A { @@ -1119,15 +1137,15 @@ impl<'a> MethodDef<'a> { /// fn eq(&self, other: &A) -> bool { /// let __self_tag = ::core::intrinsics::discriminant_value(self); /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); - /// __self_tag == __arg1_tag && - /// match (self, other) { - /// (A::A2(__self_0), A::A2(__arg1_0)) => - /// *__self_0 == *__arg1_0, + /// __self_tag == __arg1_tag + /// && match (self, other) { + /// (A::A2(__self_0), A::A2(__arg1_0)) => *__self_0 == *__arg1_0, /// _ => true, /// } /// } /// } /// ``` + /// /// Creates a tag check combined with a match for a tuple of all /// `selflike_args`, with an arm for each variant with fields, possibly an /// arm for each fieldless variant (if `unify_fieldless_variants` is not @@ -1349,7 +1367,7 @@ impl<'a> MethodDef<'a> { // (Variant1, Variant1, ...) => Body1 // (Variant2, Variant2, ...) => Body2, // ... - // _ => ::core::intrinsics::unreachable() + // _ => ::core::intrinsics::unreachable(), // } let get_match_expr = |mut selflike_args: ThinVec>| { let match_arg = if selflike_args.len() == 1 { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 9e834b83df43c..6e3a4cae2f62b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -362,9 +362,14 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { // currently use this mode so we have to allow it -- but we absolutely // shouldn't let any more targets do that. // (Also see .) + // + // The unstable abi `PtxKernel` also uses Direct for now. + // It needs to switch to something else before stabilization can happen. + // (See issue: https://github.com/rust-lang/rust/issues/117271) assert!( - matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64"), - "`PassMode::Direct` for aggregates only allowed on wasm targets\nProblematic type: {:#?}", + matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") + || self.conv == Conv::PtxKernel, + "`PassMode::Direct` for aggregates only allowed on wasm and `extern \"ptx-kernel\"` fns\nProblematic type: {:#?}", arg.layout, ); } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f16fe372a92c9..d8c89f5947fb5 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -22,7 +22,9 @@ use rustc_session::utils::NativeLibKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use rustc_target::spec::crt_objects::CrtObjects; +use rustc_target::spec::LinkSelfContainedComponents; +use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -728,6 +730,7 @@ fn link_natively<'a>( ) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); + let self_contained_components = self_contained_components(sess, crate_type); let mut cmd = linker_with_args( &linker_path, flavor, @@ -737,6 +740,7 @@ fn link_natively<'a>( tmpdir, out_filename, codegen_results, + self_contained_components, )?; linker::disable_localization(&mut cmd); @@ -812,14 +816,14 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let opts = &sess.target; - let pre_objects = if self_contained { + let pre_objects = if self_contained_crt_objects { &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; - let post_objects = if self_contained { + let post_objects = if self_contained_crt_objects { &opts.post_link_objects_self_contained } else { &opts.post_link_objects @@ -830,7 +834,9 @@ fn link_natively<'a>( .iter() .copied() .flatten() - .map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string()) + .map(|obj| { + get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string() + }) .collect::>() }; let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); @@ -1710,26 +1716,43 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// Various toolchain components used during linking are used from rustc distribution /// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn self_contained(sess: &Session, crate_type: CrateType) -> bool { - if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { - if sess.target.link_self_contained == LinkSelfContainedDefault::False { - sess.emit_err(errors::UnsupportedLinkSelfContained); - } - return self_contained; - } +fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfContainedComponents { + // Turn the backwards compatible bool values for `self_contained` into fully inferred + // `LinkSelfContainedComponents`. + let self_contained = + if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { + // Emit an error if the user requested self-contained mode on the CLI but the target + // explicitly refuses it. + if sess.target.link_self_contained.is_disabled() { + sess.emit_err(errors::UnsupportedLinkSelfContained); + } + self_contained + } else { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, + + LinkSelfContainedDefault::WithComponents(components) => { + // For target specs with explicitly enabled components, we can return them + // directly. + return components; + } - match sess.target.link_self_contained { - LinkSelfContainedDefault::False => false, - LinkSelfContainedDefault::True => true, - // FIXME: Find a better heuristic for "native musl toolchain is available", - // based on host and linker path, for example. - // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)), - LinkSelfContainedDefault::Mingw => { - sess.host == sess.target - && sess.target.vendor != "uwp" - && detect_self_contained_mingw(&sess) - } + // FIXME: Find a better heuristic for "native musl toolchain is available", + // based on host and linker path, for example. + // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). + LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::InferredForMingw => { + sess.host == sess.target + && sess.target.vendor != "uwp" + && detect_self_contained_mingw(&sess) + } + } + }; + if self_contained { + LinkSelfContainedComponents::all() + } else { + LinkSelfContainedComponents::empty() } } @@ -1889,37 +1912,14 @@ fn add_linked_symbol_object( return; }; - // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections, - // so add an empty section. if file.format() == object::BinaryFormat::Coff { + // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections, + // so add an empty section. file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text); // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the // default mangler in `object` crate. file.set_mangling(object::write::Mangling::None); - - // Add feature flags to the object file. On MSVC this is optional but LLD will complain if - // not present. - let mut feature = 0; - - if file.architecture() == object::Architecture::I386 { - // Indicate that all SEH handlers are registered in .sxdata section. - // We don't have generate any code, so we don't need .sxdata section but LLD still - // expects us to set this bit (see #96498). - // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format - feature |= 1; - } - - file.add_symbol(object::write::Symbol { - name: "@feat.00".into(), - value: feature, - size: 0, - kind: object::SymbolKind::Data, - scope: object::SymbolScope::Compilation, - weak: false, - section: object::write::SymbolSection::Absolute, - flags: object::SymbolFlags::None, - }); } for (sym, kind) in symbols.iter() { @@ -2053,13 +2053,14 @@ fn linker_with_args<'a>( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, + self_contained_components: LinkSelfContainedComponents, ) -> Result { - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - self_contained, + self_contained_components.are_any_components_enabled(), &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -2086,7 +2087,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); add_linked_symbol_object( cmd, @@ -2229,7 +2230,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - self_contained, + self_contained_components, flavor, crate_type, codegen_results, @@ -2245,7 +2246,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, self_contained); + add_post_link_objects(cmd, sess, link_output_kind, self_contained_crt_objects); // ------------ Late order-dependent options ------------ @@ -2262,7 +2263,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - self_contained: bool, + self_contained_components: LinkSelfContainedComponents, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2270,7 +2271,7 @@ fn add_order_independent_options( tmpdir: &Path, ) { // Take care of the flavors and CLI options requesting the `lld` linker. - add_lld_args(cmd, sess, flavor); + add_lld_args(cmd, sess, flavor, self_contained_components); add_apple_sdk(cmd, sess, flavor); @@ -2295,7 +2296,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if self_contained { + if self_contained_components.is_crt_objects_enabled() { cmd.no_crt_objects(); } @@ -2326,7 +2327,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, self_contained); + add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled()); cmd.output_filename(out_filename); @@ -2976,8 +2977,16 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result Option { let arch = match sess.target.options.cpu.as_ref() { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index e401f6bbcbfb2..5900c764073df 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; -use rustc_hir::{AsyncCoroutineKind, CoroutineKind, Mutability}; +use rustc_hir::{CoroutineKind, CoroutineSource, Mutability}; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; @@ -560,9 +560,9 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: & fn coroutine_kind_label(coroutine_kind: Option) -> &'static str { match coroutine_kind { - Some(CoroutineKind::Async(AsyncCoroutineKind::Block)) => "async_block", - Some(CoroutineKind::Async(AsyncCoroutineKind::Closure)) => "async_closure", - Some(CoroutineKind::Async(AsyncCoroutineKind::Fn)) => "async_fn", + Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block", + Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure", + Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn", Some(CoroutineKind::Coroutine) => "coroutine", None => "closure", } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 616fab8fe7d65..40183baccf8fe 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -360,7 +360,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { pub struct Coroutine(pub hir::CoroutineKind); impl<'tcx> NonConstOp<'tcx> for Coroutine { fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { - if let hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Block) = self.0 { + if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 { Status::Unstable(sym::const_async_blocks) } else { Status::Forbidden @@ -372,8 +372,8 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind()); - if let hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Block) = self.0 { + let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind()); + if let hir::CoroutineKind::Async(hir::CoroutineSource::Block) = self.0 { ccx.tcx.sess.create_feature_err( errors::UnallowedOpInConstContext { span, msg }, sym::const_async_blocks, diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index d3bd3244a52aa..39462112dc287 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -1,5 +1,6 @@ driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} +driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden driver_impl_ice_flags = compiler flags: {$flags} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 7e4a7d93210e5..c6c8fd22f4efc 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -60,7 +60,7 @@ use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use std::time::{Instant, SystemTime}; use time::OffsetDateTime; @@ -136,7 +136,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, - rustc_symbol_mangling::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end @@ -223,11 +222,18 @@ pub struct RunCompiler<'a, 'b> { file_loader: Option>, make_codegen_backend: Option Box + Send>>, + using_internal_features: Arc, } impl<'a, 'b> RunCompiler<'a, 'b> { pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self { - Self { at_args, callbacks, file_loader: None, make_codegen_backend: None } + Self { + at_args, + callbacks, + file_loader: None, + make_codegen_backend: None, + using_internal_features: Arc::default(), + } } /// Set a custom codegen backend. @@ -259,9 +265,23 @@ impl<'a, 'b> RunCompiler<'a, 'b> { self } + /// Set the session-global flag that checks whether internal features have been used, + /// suppressing the message about submitting an issue in ICEs when enabled. + #[must_use] + pub fn set_using_internal_features(mut self, using_internal_features: Arc) -> Self { + self.using_internal_features = using_internal_features; + self + } + /// Parse args and run the compiler. pub fn run(self) -> interface::Result<()> { - run_compiler(self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend) + run_compiler( + self.at_args, + self.callbacks, + self.file_loader, + self.make_codegen_backend, + self.using_internal_features, + ) } } @@ -272,6 +292,7 @@ fn run_compiler( make_codegen_backend: Option< Box Box + Send>, >, + using_internal_features: Arc, ) -> interface::Result<()> { let mut early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default()); @@ -316,6 +337,7 @@ fn run_compiler( override_queries: None, make_codegen_backend, registry: diagnostics_registry(), + using_internal_features, expanded_args: args, }; @@ -1333,8 +1355,12 @@ fn ice_path() -> &'static Option { /// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to /// extra_info. /// +/// Returns a flag that can be set to disable the note for submitting a bug. This can be passed to +/// [`RunCompiler::set_using_internal_features`] to let macro expansion set it when encountering +/// internal features. +/// /// A custom rustc driver can skip calling this to set up a custom ICE hook. -pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) { +pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) -> Arc { // If the user has not explicitly overridden "RUST_BACKTRACE", then produce // full backtraces. When a compiler ICE happens, we want to gather // as much information as possible to present in the issue opened @@ -1345,6 +1371,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) std::env::set_var("RUST_BACKTRACE", "full"); } + let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default()); + let using_internal_features_hook = using_internal_features.clone(); panic::update_hook(Box::new( move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), info: &PanicInfo<'_>| { @@ -1394,9 +1422,11 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) } // Print the ICE message - report_ice(info, bug_report_url, extra_info); + report_ice(info, bug_report_url, extra_info, &using_internal_features_hook); }, )); + + using_internal_features } /// Prints the ICE message, including query stack, but without backtrace. @@ -1405,7 +1435,12 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) /// /// When `install_ice_hook` is called, this function will be called as the panic /// hook. -fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) { +fn report_ice( + info: &panic::PanicInfo<'_>, + bug_report_url: &str, + extra_info: fn(&Handler), + using_internal_features: &AtomicBool, +) { let fallback_bundle = rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( @@ -1422,7 +1457,11 @@ fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn( handler.emit_err(session_diagnostics::Ice); } - handler.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) { + handler.emit_note(session_diagnostics::IceBugReportInternalFeature); + } else { + handler.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + } let version = util::version_str!().unwrap_or("unknown_version"); let triple = config::host_triple(); @@ -1506,7 +1545,7 @@ pub fn main() -> ! { init_rustc_env_logger(&handler); signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); - install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); let exit_code = catch_with_exit_code(|| { let args = env::args_os() .enumerate() @@ -1516,7 +1555,9 @@ pub fn main() -> ! { }) }) .collect::>(); - RunCompiler::new(&args, &mut callbacks).run() + RunCompiler::new(&args, &mut callbacks) + .set_using_internal_features(using_internal_features) + .run() }); if let Some(format) = callbacks.time_passes { diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 442989f8de83a..2b31fdd77cca3 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -42,6 +42,10 @@ pub(crate) struct IceBugReport<'a> { pub bug_report_url: &'a str, } +#[derive(Diagnostic)] +#[diag(driver_impl_ice_bug_report_internal_feature)] +pub(crate) struct IceBugReportInternalFeature; + #[derive(Diagnostic)] #[diag(driver_impl_ice_version)] pub(crate) struct IceVersion<'a> { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index da74ee6391ae2..5e3fdf170bc0c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -508,6 +508,8 @@ pub enum StashKey { TraitMissingMethod, OpaqueHiddenTypeMismatch, MaybeForgetReturn, + /// Query cycle detected, stashing in favor of a better error. + Cycle, } fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index b73c7593381c3..bef48765937f5 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -35,7 +35,7 @@ pub struct StripUnconfigured<'a> { pub lint_node_id: NodeId, } -pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features { +pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -> Features { fn feature_list(attr: &Attribute) -> ThinVec { if attr.has_name(sym::feature) && let Some(list) = attr.meta_item_list() @@ -167,6 +167,15 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features { // If the declared feature is unstable, record it. if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) { (f.set_enabled)(&mut features); + // When the ICE comes from core, alloc or std (approximation of the standard library), there's a chance + // that the person hitting the ICE may be using -Zbuild-std or similar with an untested target. + // The bug is probably in the standard library and not the compiler in that case, but that doesn't + // really matter - we want a bug report. + if features.internal(name) + && ![sym::core, sym::alloc, sym::std].contains(&crate_name) + { + sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); + } features.set_declared_lang_feature(name, mi.span(), None); continue; } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f8d55192a3747..17c6352ce24c2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -246,6 +246,8 @@ impl<'hir> PathSegment<'hir> { pub struct ConstArg { pub value: AnonConst, pub span: Span, + /// Indicates whether this comes from a `~const` desugaring. + pub is_desugared_from_effects: bool, } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -400,7 +402,14 @@ impl<'hir> GenericArgs<'hir> { /// This function returns the number of type and const generic params. /// It should only be used for diagnostics. pub fn num_generic_params(&self) -> usize { - self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count() + self.args + .iter() + .filter(|arg| match arg { + GenericArg::Lifetime(_) + | GenericArg::Const(ConstArg { is_desugared_from_effects: true, .. }) => false, + _ => true, + }) + .count() } /// The span encompassing the text inside the surrounding brackets. @@ -1511,7 +1520,7 @@ impl<'hir> Body<'hir> { #[derive(HashStable_Generic, Encodable, Decodable)] pub enum CoroutineKind { /// An explicit `async` block or the body of an async function. - Async(AsyncCoroutineKind), + Async(CoroutineSource), /// A coroutine literal created via a `yield` inside a closure. Coroutine, @@ -1520,56 +1529,45 @@ pub enum CoroutineKind { impl fmt::Display for CoroutineKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - CoroutineKind::Async(k) => fmt::Display::fmt(k, f), + CoroutineKind::Async(k) => { + if f.alternate() { + f.write_str("`async` ")?; + } else { + f.write_str("async ")? + } + k.fmt(f) + } CoroutineKind::Coroutine => f.write_str("coroutine"), } } } -impl CoroutineKind { - pub fn descr(&self) -> &'static str { - match self { - CoroutineKind::Async(ask) => ask.descr(), - CoroutineKind::Coroutine => "coroutine", - } - } -} - -/// In the case of a coroutine created as part of an async construct, -/// which kind of async construct caused it to be created? +/// In the case of a coroutine created as part of an async/gen construct, +/// which kind of async/gen construct caused it to be created? /// /// This helps error messages but is also used to drive coercions in /// type-checking (see #60424). #[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)] #[derive(HashStable_Generic, Encodable, Decodable)] -pub enum AsyncCoroutineKind { - /// An explicit `async` block written by the user. +pub enum CoroutineSource { + /// An explicit `async`/`gen` block written by the user. Block, - /// An explicit `async` closure written by the user. + /// An explicit `async`/`gen` closure written by the user. Closure, - /// The `async` block generated as the body of an async function. + /// The `async`/`gen` block generated as the body of an async/gen function. Fn, } -impl fmt::Display for AsyncCoroutineKind { +impl fmt::Display for CoroutineSource { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - AsyncCoroutineKind::Block => "async block", - AsyncCoroutineKind::Closure => "async closure body", - AsyncCoroutineKind::Fn => "async fn body", - }) - } -} - -impl AsyncCoroutineKind { - pub fn descr(&self) -> &'static str { match self { - AsyncCoroutineKind::Block => "`async` block", - AsyncCoroutineKind::Closure => "`async` closure body", - AsyncCoroutineKind::Fn => "`async fn` body", + CoroutineSource::Block => "block", + CoroutineSource::Closure => "closure body", + CoroutineSource::Fn => "fn body", } + .fmt(f) } } @@ -3781,6 +3779,7 @@ impl<'hir> Node<'hir> { ItemKind::TyAlias(ty, _) | ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => Some(ty), + ItemKind::Impl(impl_item) => Some(&impl_item.self_ty), _ => None, }, Node::TraitItem(it) => match it.kind { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index cdfc67d5740f6..c8ac95e29949b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -210,6 +210,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; + Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, gen_state, Target::Enum, GenericRequirement::None; Coroutine, sym::coroutine, gen_trait, Target::Trait, GenericRequirement::Minimum(1); diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 7f0c0b961e4e8..d29a27eced0e7 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -429,6 +429,14 @@ pub(crate) fn check_generic_arg_count( .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) .count(); let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; + let synth_const_param_count = gen_params + .params + .iter() + .filter(|param| { + matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. }) + }) + .count(); + let named_const_param_count = param_counts.consts - synth_const_param_count; let infer_lifetimes = (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); @@ -573,11 +581,13 @@ pub(crate) fn check_generic_arg_count( debug!(?expected_min); debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + let provided = gen_args.num_generic_params(); + check_types_and_consts( expected_min, - param_counts.consts + named_type_param_count, - param_counts.consts + named_type_param_count + synth_type_param_count, - gen_args.num_generic_params(), + named_const_param_count + named_type_param_count, + named_const_param_count + named_type_param_count + synth_type_param_count, + provided, param_counts.lifetimes + has_self as usize, gen_args.num_lifetime_params(), ) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 360d31b863c1d..e61ca232de643 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -128,7 +128,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b let param_env = tcx.param_env(item_def_id); for field in &def.non_enum_variant().fields { - let field_ty = tcx.normalize_erasing_regions(param_env, field.ty(tcx, args)); + let Ok(field_ty) = tcx.try_normalize_erasing_regions(param_env, field.ty(tcx, args)) + else { + tcx.sess.delay_span_bug(span, "could not normalize field type"); + continue; + }; if !allowed_union_field(field_ty, tcx, param_env) { let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0622aa2ee80fb..2b8219c01c769 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -181,14 +181,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { })?; } - tcx.sess.track_errors(|| { - tcx.sess.time("impl_wf_inference", || { - tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)) - }); - })?; - tcx.sess.track_errors(|| { tcx.sess.time("coherence_checking", || { + // Check impls constrain their parameters + tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)); + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { tcx.ensure().coherent_trait(trait_def_id); } @@ -205,15 +202,19 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { })?; } - tcx.sess.time("wf_checking", || { + let errs = tcx.sess.time("wf_checking", || { tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) - })?; + }); // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync. tcx.sess.time("item_types_checking", || { tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module)) }); + // HACK: `check_mod_type_wf` may spuriously emit errors due to `delay_span_bug`, even if those errors + // only actually get emitted in `check_mod_item_types`. + errs?; + if tcx.features().rustc_attrs { tcx.sess.track_errors(|| collect::test_opaque_hidden_types(tcx))?; } diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 2b5f6fd214c1f..76bd370a641b4 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -129,6 +129,44 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { if self.missing_lifetimes() { "lifetime" } else { "generic" } } + /// Returns true if the generic type is a trait + /// and is being referred to from one of its trait impls + fn is_in_trait_impl(&self) -> bool { + if self.tcx.is_trait(self.def_id) { + // Here we check if the reference to the generic type + // is from the 'of_trait' field of the enclosing impl + + let parent = self.tcx.hir().get_parent(self.path_segment.hir_id); + let parent_item = self + .tcx + .hir() + .get_by_def_id(self.tcx.hir().get_parent_item(self.path_segment.hir_id).def_id); + + // Get the HIR id of the trait ref + let hir::Node::TraitRef(hir::TraitRef { hir_ref_id: trait_ref_id, .. }) = parent else { + return false; + }; + + // Get the HIR id of the 'of_trait' field of the impl + let hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }), + .. + }), + .. + }) = parent_item + else { + return false; + }; + + // Check that trait is referred to from the of_trait field of impl + trait_ref_id == id_in_of_trait + } else { + false + } + } + fn num_provided_args(&self) -> usize { if self.missing_lifetimes() { self.num_provided_lifetime_args() @@ -955,20 +993,26 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // If there is a single unbound associated type and a single excess generic param // suggest replacing the generic param with the associated type bound if provided_args_matches_unbound_traits && !unbound_types.is_empty() { - let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..]; - let suggestions = iter::zip(unused_generics, &unbound_types) - .map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = "))) - .collect::>(); + // Don't suggest if we're in a trait impl as + // that would result in invalid syntax (fixes #116464) + if !self.is_in_trait_impl() { + let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..]; + let suggestions = iter::zip(unused_generics, &unbound_types) + .map(|(potential, name)| { + (potential.span().shrink_to_lo(), format!("{name} = ")) + }) + .collect::>(); - if !suggestions.is_empty() { - err.multipart_suggestion_verbose( - format!( - "replace the generic bound{s} with the associated type{s}", - s = pluralize!(unbound_types.len()) - ), - suggestions, - Applicability::MaybeIncorrect, - ); + if !suggestions.is_empty() { + err.multipart_suggestion_verbose( + format!( + "replace the generic bound{s} with the associated type{s}", + s = pluralize!(unbound_types.len()) + ), + suggestions, + Applicability::MaybeIncorrect, + ); + } } } else if remove_entire_generics { let span = self diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 5eb68cf6b28c3..6b6d1574b2bf6 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) = (parent_node, callee_node) { let fn_decl_span = if hir.body(body).coroutine_kind - == Some(hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Closure)) + == Some(hir::CoroutineKind::Async(hir::CoroutineSource::Closure)) { // Actually need to unwrap one more layer of HIR to get to // the _real_ closure... diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index a834ea150477c..ee23f47c2711c 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -660,9 +660,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { match self.try_coercion_cast(fcx) { Ok(()) => { - self.trivial_cast_lint(fcx); - debug!(" -> CoercionCast"); - fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); + if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() { + // When casting a raw pointer to another raw pointer, we cannot convert the cast into + // a coercion because the pointee types might only differ in regions, which HIR typeck + // cannot distinguish. This would cause us to erroneously discard a cast which will + // lead to a borrowck error like #113257. + // We still did a coercion above to unify inference variables for `ptr as _` casts. + // This does cause us to miss some trivial casts in the trival cast lint. + debug!(" -> PointerCast"); + } else { + self.trivial_cast_lint(fcx); + debug!(" -> CoercionCast"); + fcx.typeck_results + .borrow_mut() + .set_coercion_cast(self.expr.hir_id.local_id); + } } Err(_) => { match self.do_check(fcx) { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index cf8a4cafbb611..47bde21ceb2eb 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -636,7 +636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // In the case of the async block that we create for a function body, // we expect the return type of the block to match that of the enclosing // function. - Some(hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Fn)) => { + Some(hir::CoroutineKind::Async(hir::CoroutineSource::Fn)) => { debug!("closure is async fn body"); let def_id = self.tcx.hir().body_owner_def_id(body.id()); self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else( diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index f379e8477e389..abb8322803010 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -962,38 +962,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let ty::Adt(e, args_e) = expected.kind() else { - return false; - }; - let ty::Adt(f, args_f) = found.kind() else { - return false; - }; - if e.did() != f.did() { - return false; - } - if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { - return false; - } let map = self.tcx.hir(); - if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) - && let hir::ExprKind::Ret(_) = expr.kind - { - // `return foo;` - } else if map.get_return_block(expr.hir_id).is_some() { - // Function's tail expression. - } else { - return false; - } - let e = args_e.type_at(1); - let f = args_f.type_at(1); - if self - .infcx - .type_implements_trait( - self.tcx.get_diagnostic_item(sym::Into).unwrap(), - [f, e], - self.param_env, - ) - .must_apply_modulo_regions() + let returned = matches!( + map.find_parent(expr.hir_id), + Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })) + ) || map.get_return_block(expr.hir_id).is_some(); + if returned + && let ty::Adt(e, args_e) = expected.kind() + && let ty::Adt(f, args_f) = found.kind() + && e.did() == f.did() + && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result) + && let e_ok = args_e.type_at(0) + && let f_ok = args_f.type_at(0) + && self.infcx.can_eq(self.param_env, f_ok, e_ok) + && let e_err = args_e.type_at(1) + && let f_err = args_f.type_at(1) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [f_err, e_err], + self.param_env, + ) + .must_apply_modulo_regions() { err.multipart_suggestion( "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index afa5a3b9379ea..b5a07f0d3e9ec 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -26,7 +26,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType, + self, AdtKind, CanonicalUserType, GenericParamDefKind, IsIdentity, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, GenericArgsRef, UserArgs, UserSelfTy}; use rustc_session::lint; @@ -207,6 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { debug!("fcx {}", self.tag()); + // FIXME: is_identity being on `UserType` and not `Canonical` is awkward if !canonical_user_type_annotation.is_identity() { self.typeck_results .borrow_mut() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 13aa6454bc363..ca7dc298de64f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,8 +10,8 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - AsyncCoroutineKind, CoroutineKind, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, - Stmt, StmtKind, TyKind, WherePredicate, + CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt, + StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::traits::{self, StatementAsExpression}; @@ -536,7 +536,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Coroutine(def_id, ..) if matches!( self.tcx.coroutine_kind(def_id), - Some(CoroutineKind::Async(AsyncCoroutineKind::Closure)) + Some(CoroutineKind::Async(CoroutineSource::Closure)) ) => { errors::SuggestBoxing::AsyncBody @@ -1747,19 +1747,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let ty::Adt(adt, args) = found.kind() else { return false }; + let ty::Adt(adt, args) = found.kind() else { + return false; + }; let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && self.tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false - } + let Some(sig) = self.body_fn_sig() else { + return false; + }; + let ty::Adt(kind, _) = sig.output().kind() else { + return false; + }; + self.tcx.is_diagnostic_item(diagnostic_item, kind.did()) }; // don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`, diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index bdae07a3946f5..dde138973b931 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -5,7 +5,6 @@ #![cfg_attr(not(bootstrap), doc(rust_logo))] #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![cfg_attr(not(bootstrap), allow(internal_features))] -#![feature(never_type)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -19,15 +18,11 @@ mod assert_dep_graph; mod errors; mod persist; -use assert_dep_graph::assert_dep_graph; pub use persist::copy_cgu_workproduct_to_incr_comp_cache_dir; -pub use persist::delete_workproduct_files; pub use persist::finalize_session_directory; -pub use persist::garbage_collect_session_directories; pub use persist::in_incr_comp_dir; pub use persist::in_incr_comp_dir_sess; pub use persist::load_query_result_cache; -pub use persist::prepare_session_directory; pub use persist::save_dep_graph; pub use persist::save_work_product_index; pub use persist::setup_dep_graph; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index db8ea2bfe48aa..f56fb0d0534be 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -53,7 +53,7 @@ //! ## Synchronization //! //! There is some synchronization needed in order for the compiler to be able to -//! determine whether a given private session directory is not in used any more. +//! determine whether a given private session directory is not in use any more. //! This is done by creating a lock file for each session directory and //! locking it while the directory is still being used. Since file locks have //! operating system support, we can rely on the lock being released if the @@ -136,26 +136,29 @@ const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE; /// Returns the path to a session's dependency graph. -pub fn dep_graph_path(sess: &Session) -> PathBuf { +pub(crate) fn dep_graph_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, DEP_GRAPH_FILENAME) } + /// Returns the path to a session's staging dependency graph. /// /// On the difference between dep-graph and staging dep-graph, /// see `build_dep_graph`. -pub fn staging_dep_graph_path(sess: &Session) -> PathBuf { +pub(crate) fn staging_dep_graph_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME) } -pub fn work_products_path(sess: &Session) -> PathBuf { + +pub(crate) fn work_products_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME) } + /// Returns the path to a session's query cache. pub fn query_cache_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, QUERY_CACHE_FILENAME) } /// Locks a given session directory. -pub fn lock_file_path(session_dir: &Path) -> PathBuf { +fn lock_file_path(session_dir: &Path) -> PathBuf { let crate_dir = session_dir.parent().unwrap(); let directory_name = session_dir.file_name().unwrap().to_string_lossy(); @@ -202,7 +205,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub fn prepare_session_directory( +pub(crate) fn prepare_session_directory( sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId, @@ -373,7 +376,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { let _ = garbage_collect_session_directories(sess); } -pub fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> { +pub(crate) fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> { let sess_dir_iterator = sess.incr_comp_session_dir().read_dir()?; for entry in sess_dir_iterator { let entry = entry?; @@ -621,7 +624,7 @@ fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool { } /// Runs garbage collection for the current session. -pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { +pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { debug!("garbage_collect_session_directories() - begin"); let session_directory = sess.incr_comp_session_dir(); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 2310d0b12ef0c..cbd55fe420563 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -1,4 +1,4 @@ -//! Code to save/load the dep-graph from files. +//! Code to load the dep-graph from files. use crate::errors; use rustc_data_structures::memmap::Mmap; diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index fdecaca5a2dc1..94c05f4a2c81e 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -11,14 +11,11 @@ mod save; mod work_product; pub use fs::finalize_session_directory; -pub use fs::garbage_collect_session_directories; pub use fs::in_incr_comp_dir; pub use fs::in_incr_comp_dir_sess; -pub use fs::prepare_session_directory; pub use load::load_query_result_cache; pub use load::setup_dep_graph; pub use load::LoadResult; pub use save::save_dep_graph; pub use save::save_work_product_index; pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir; -pub use work_product::delete_workproduct_files; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 210da751d95e4..fa21320be263e 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,3 +1,4 @@ +use crate::assert_dep_graph::assert_dep_graph; use crate::errors; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::join; @@ -39,7 +40,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { let dep_graph_path = dep_graph_path(sess); let staging_dep_graph_path = staging_dep_graph_path(sess); - sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx)); + sess.time("assert_dep_graph", || assert_dep_graph(tcx)); sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx)); if sess.opts.unstable_opts.incremental_info { diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index bce5ca1e16bd1..fb96bed5a7180 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -11,7 +11,8 @@ use rustc_session::Session; use std::fs as std_fs; use std::path::Path; -/// Copies a CGU work product to the incremental compilation directory, so next compilation can find and reuse it. +/// Copies a CGU work product to the incremental compilation directory, so next compilation can +/// find and reuse it. pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, @@ -45,7 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( } /// Removes files for a given work product. -pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { +pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ba118f8110f31..e4be435fded30 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1584,14 +1584,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { target: &str, types: &FxIndexMap>, ) { - for (key, values) in types.iter() { + for (kind, values) in types.iter() { let count = values.len(); - let kind = key.descr(); for &sp in values { err.span_label( sp, format!( - "{}{} {}{}", + "{}{} {:#}{}", if count == 1 { "the " } else { "one of the " }, target, kind, @@ -2952,17 +2951,19 @@ pub enum TyCategory { Foreign, } -impl TyCategory { - fn descr(&self) -> &'static str { +impl fmt::Display for TyCategory { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Closure => "closure", - Self::Opaque => "opaque type", - Self::OpaqueFuture => "future", - Self::Coroutine(gk) => gk.descr(), - Self::Foreign => "foreign type", + Self::Closure => "closure".fmt(f), + Self::Opaque => "opaque type".fmt(f), + Self::OpaqueFuture => "future".fmt(f), + Self::Coroutine(gk) => gk.fmt(f), + Self::Foreign => "foreign type".fmt(f), } } +} +impl TyCategory { pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> { match *ty.kind() { ty::Closure(def_id, _) => Some((Self::Closure, def_id)), diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 00289f9bfb411..a8ae43d52978d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -54,13 +54,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::Param(expected), ty::Param(found)) => { let generics = tcx.generics_of(body_owner_def_id); - let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); - if !sp.contains(e_span) { - diag.span_label(e_span, "expected type parameter"); + if let Some(param) = generics.opt_type_param(expected, tcx) { + let e_span = tcx.def_span(param.def_id); + if !sp.contains(e_span) { + diag.span_label(e_span, "expected type parameter"); + } } - let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); - if !sp.contains(f_span) { - diag.span_label(f_span, "found type parameter"); + if let Some(param) = generics.opt_type_param(found, tcx) { + let f_span = tcx.def_span(param.def_id); + if !sp.contains(f_span) { + diag.span_label(f_span, "found type parameter"); + } } diag.note( "a type parameter was expected, but a different one was found; \ @@ -83,23 +87,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { - let p_def_id = tcx.generics_of(body_owner_def_id).type_param(p, tcx).def_id; - let p_span = tcx.def_span(p_def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } - let hir = tcx.hir(); + let parent = tcx.generics_of(body_owner_def_id) + .opt_type_param(p, tcx) + .and_then(|param| { + let p_def_id = param.def_id; + let p_span = tcx.def_span(p_def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label( + p_span, + format!("{expected}this type parameter"), + ); + } + p_def_id.as_local().and_then(|id| { + let local_id = tcx.hir().local_def_id_to_hir_id(id); + let generics = tcx.hir().find_parent(local_id)?.generics()?; + Some((id, generics)) + }) + }); let mut note = true; - let parent = p_def_id.as_local().and_then(|id| { - let local_id = hir.local_def_id_to_hir_id(id); - let generics = tcx.hir().find_parent(local_id)?.generics()?; - Some((id, generics)) - }); if let Some((local_id, generics)) = parent { // Synthesize the associated type restriction `Add`. // FIXME: extract this logic for use in other diagnostics. @@ -172,14 +182,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); + } } diag.help("type parameters must be constrained to match other types"); if tcx.sess.teach(&diag.get_code().unwrap()) { @@ -217,9 +229,11 @@ impl Trait for X { } (ty::Param(p), ty::Closure(..) | ty::Coroutine(..)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "expected this type parameter"); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "expected this type parameter"); + } } diag.help(format!( "every closure has a distinct type and so could not always match the \ @@ -228,14 +242,16 @@ impl Trait for X { } (ty::Param(p), _) | (_, ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); + if let Some(param) = generics.opt_type_param(p, tcx) { + let p_span = tcx.def_span(param.def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); + } } } (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) @@ -364,13 +380,14 @@ impl Trait for X { }; // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. // This will also work for `impl Trait`. - let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { - let generics = tcx.generics_of(body_owner_def_id); - generics.type_param(param_ty, tcx).def_id - } else { + let ty::Param(param_ty) = proj_ty.self_ty().kind() else { return false; }; - let Some(def_id) = def_id.as_local() else { + let generics = tcx.generics_of(body_owner_def_id); + let Some(param) = generics.opt_type_param(param_ty, tcx) else { + return false; + }; + let Some(def_id) = param.def_id.as_local() else { return false; }; @@ -390,6 +407,10 @@ impl Trait for X { return true; } } + if (param_ty.index as usize) >= generics.parent_count { + // The param comes from the current item, do not look at the parent. (#117209) + return false; + } // If associated item, look to constrain the params of the trait/impl. let hir_id = match item { hir::Node::ImplItem(item) => item.hir_id(), diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index d2ce77ad53511..87badcbdf15ba 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -24,6 +24,7 @@ use rustc_span::source_map::{FileLoader, FileName}; use rustc_span::symbol::sym; use std::path::PathBuf; use std::result; +use std::sync::Arc; pub type Result = result::Result; @@ -410,6 +411,12 @@ pub struct Config { /// Registry of diagnostics codes. pub registry: Registry, + /// The inner atomic value is set to true when a feature marked as `internal` is + /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with + /// internal features are wontfix, and they are usually the cause of the ICEs. + /// None signifies that this is not tracked. + pub using_internal_features: Arc, + /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows /// This is mainly useful for other tools that reads that debuginfo to figure out @@ -453,6 +460,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.make_codegen_backend, registry.clone(), config.ice_file, + config.using_internal_features, config.expanded_args, ); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 998e2686005fd..884afae23d803 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -856,6 +856,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // This check has to be run after all lints are done processing. We don't // define a lint filter, as all lint checks should have finished at this point. sess.time("check_lint_expectations", || tcx.ensure().check_expectations(None)); + + // This query is only invoked normally if a diagnostic is emitted that needs any + // diagnostic item. If the crate compiles without checking any diagnostic items, + // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. + let _ = tcx.all_diagnostic_items(()); }); if sess.opts.unstable_opts.print_vtable_sizes { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index fe253febfc787..4fb295da64012 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -181,9 +181,11 @@ impl<'tcx> Queries<'tcx> { feed.crate_name(crate_name); let feed = tcx.feed_unit_query(); - feed.features_query( - tcx.arena.alloc(rustc_expand::config::features(sess, &pre_configured_attrs)), - ); + feed.features_query(tcx.arena.alloc(rustc_expand::config::features( + sess, + &pre_configured_attrs, + crate_name, + ))); feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); }); Ok(qcx) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ec4fd78994e95..3d191669b1adb 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -35,6 +35,7 @@ use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtecto use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; +use std::sync::Arc; type CfgSpecs = FxHashSet<(String, Option)>; @@ -69,6 +70,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se None, "", None, + Arc::default(), Default::default(), ); (sess, cfg) @@ -612,7 +614,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_frame_pointers, Some(false)); tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); - tracked!(instrument_coverage, Some(InstrumentCoverage::All)); + tracked!(instrument_coverage, InstrumentCoverage::All); tracked!(link_dead_code, Some(true)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); @@ -789,7 +791,6 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); - tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_directives, false); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0634e44c5620b..d2455a036ccd5 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -26,7 +26,7 @@ use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::mem; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use std::thread; /// Function pointer type that constructs a new CodegenBackend. @@ -71,6 +71,7 @@ pub fn create_session( >, descriptions: Registry, ice_file: Option, + using_internal_features: Arc, expanded_args: Vec, ) -> (Session, Box) { let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend { @@ -114,6 +115,7 @@ pub fn create_session( target_override, rustc_version_str().unwrap_or("unknown"), ice_file, + using_internal_features, expanded_args, ); diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 85829906f4ef5..776ba8f9ca11a 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -4,6 +4,7 @@ #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] +#![feature(proc_macro_tracked_env)] #![allow(rustc::default_hash_types)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index d8a695b131bc1..ad1980136f3b9 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -97,6 +97,9 @@ struct QueryModifiers { /// A cycle error results in a delay_bug call cycle_delay_bug: Option, + /// A cycle error results in a stashed cycle error that can be unstashed and canceled later + cycle_stash: Option, + /// Don't hash the result, instead just mark a query red if it runs no_hash: Option, @@ -127,6 +130,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut desc = None; let mut fatal_cycle = None; let mut cycle_delay_bug = None; + let mut cycle_stash = None; let mut no_hash = None; let mut anon = None; let mut eval_always = None; @@ -181,6 +185,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(fatal_cycle = modifier); } else if modifier == "cycle_delay_bug" { try_insert!(cycle_delay_bug = modifier); + } else if modifier == "cycle_stash" { + try_insert!(cycle_stash = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); } else if modifier == "anon" { @@ -208,6 +214,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { desc, fatal_cycle, cycle_delay_bug, + cycle_stash, no_hash, anon, eval_always, @@ -329,6 +336,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { fatal_cycle, arena_cache, cycle_delay_bug, + cycle_stash, no_hash, anon, eval_always, diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 04facbf657d22..4129712a6b218 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -26,7 +26,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use std::collections::HashMap; use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, punctuated::Punctuated, Ident, LitStr, Token}; +use syn::{braced, punctuated::Punctuated, Expr, Ident, Lit, LitStr, Macro, Token}; #[cfg(test)] mod tests; @@ -53,21 +53,46 @@ impl Parse for Keyword { struct Symbol { name: Ident, - value: Option, + value: Value, +} + +enum Value { + SameAsName, + String(LitStr), + Env(LitStr, Macro), + Unsupported(Expr), } impl Parse for Symbol { fn parse(input: ParseStream<'_>) -> Result { let name = input.parse()?; - let value = match input.parse::() { - Ok(_) => Some(input.parse()?), - Err(_) => None, - }; + let colon_token: Option = input.parse()?; + let value = if colon_token.is_some() { input.parse()? } else { Value::SameAsName }; Ok(Symbol { name, value }) } } +impl Parse for Value { + fn parse(input: ParseStream<'_>) -> Result { + let expr: Expr = input.parse()?; + match &expr { + Expr::Lit(expr) => { + if let Lit::Str(lit) = &expr.lit { + return Ok(Value::String(lit.clone())); + } + } + Expr::Macro(expr) => { + if expr.mac.path.is_ident("env") && let Ok(lit) = expr.mac.parse_body() { + return Ok(Value::Env(lit, expr.mac.clone())); + } + } + _ => {} + } + Ok(Value::Unsupported(expr)) + } +} + struct Input { keywords: Punctuated, symbols: Punctuated, @@ -111,6 +136,37 @@ pub fn symbols(input: TokenStream) -> TokenStream { output } +struct Preinterned { + idx: u32, + span_of_name: Span, +} + +struct Entries { + map: HashMap, +} + +impl Entries { + fn with_capacity(capacity: usize) -> Self { + Entries { map: HashMap::with_capacity(capacity) } + } + + fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 { + if let Some(prev) = self.map.get(str) { + errors.error(span, format!("Symbol `{str}` is duplicated")); + errors.error(prev.span_of_name, "location of previous definition".to_string()); + prev.idx + } else { + let idx = self.len(); + self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + idx + } + } + + fn len(&self) -> u32 { + u32::try_from(self.map.len()).expect("way too many symbols") + } +} + fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut errors = Errors::default(); @@ -127,20 +183,9 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut keyword_stream = quote! {}; let mut symbols_stream = quote! {}; let mut prefill_stream = quote! {}; - let mut counter = 0u32; - let mut keys = - HashMap::::with_capacity(input.keywords.len() + input.symbols.len() + 10); + let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); let mut prev_key: Option<(Span, String)> = None; - let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { - if let Some(prev_span) = keys.get(str) { - errors.error(span, format!("Symbol `{str}` is duplicated")); - errors.error(*prev_span, "location of previous definition".to_string()); - } else { - keys.insert(str.to_string(), span); - } - }; - let mut check_order = |span: Span, str: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { if str < prev_str { @@ -156,49 +201,98 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let name = &keyword.name; let value = &keyword.value; let value_string = value.value(); - check_dup(keyword.name.span(), &value_string, &mut errors); + let idx = entries.insert(keyword.name.span(), &value_string, &mut errors); prefill_stream.extend(quote! { #value, }); keyword_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate the listed symbols. for symbol in input.symbols.iter() { let name = &symbol.name; + check_order(symbol.name.span(), &name.to_string(), &mut errors); + let value = match &symbol.value { - Some(value) => value.value(), - None => name.to_string(), + Value::SameAsName => name.to_string(), + Value::String(lit) => lit.value(), + Value::Env(..) => continue, // in another loop below + Value::Unsupported(expr) => { + errors.list.push(syn::Error::new_spanned( + expr, + concat!( + "unsupported expression for symbol value; implement support for this in ", + file!(), + ), + )); + continue; + } }; - check_dup(symbol.name.span(), &value, &mut errors); - check_order(symbol.name.span(), &name.to_string(), &mut errors); + let idx = entries.insert(symbol.name.span(), &value, &mut errors); prefill_stream.extend(quote! { #value, }); symbols_stream.extend(quote! { - pub const #name: Symbol = Symbol::new(#counter); + pub const #name: Symbol = Symbol::new(#idx); }); - counter += 1; } // Generate symbols for the strings "0", "1", ..., "9". - let digits_base = counter; - counter += 10; for n in 0..10 { let n = n.to_string(); - check_dup(Span::call_site(), &n, &mut errors); + entries.insert(Span::call_site(), &n, &mut errors); prefill_stream.extend(quote! { #n, }); } + // Symbols whose value comes from an environment variable. It's allowed for + // these to have the same value as another symbol. + for symbol in &input.symbols { + let (env_var, expr) = match &symbol.value { + Value::Env(lit, expr) => (lit, expr), + Value::SameAsName | Value::String(_) | Value::Unsupported(_) => continue, + }; + + if !proc_macro::is_available() { + errors.error( + Span::call_site(), + "proc_macro::tracked_env is not available in unit test".to_owned(), + ); + break; + } + + let value = match proc_macro::tracked_env::var(env_var.value()) { + Ok(value) => value, + Err(err) => { + errors.list.push(syn::Error::new_spanned(expr, err)); + continue; + } + }; + + let idx = if let Some(prev) = entries.map.get(&value) { + prev.idx + } else { + prefill_stream.extend(quote! { + #value, + }); + entries.insert(symbol.name.span(), &value, &mut errors) + }; + + let name = &symbol.name; + symbols_stream.extend(quote! { + pub const #name: Symbol = Symbol::new(#idx); + }); + } + + let symbol_digits_base = entries.map["0"].idx; + let preinterned_symbols_count = entries.len(); let output = quote! { - const SYMBOL_DIGITS_BASE: u32 = #digits_base; - const PREINTERNED_SYMBOLS_COUNT: u32 = #counter; + const SYMBOL_DIGITS_BASE: u32 = #symbol_digits_base; + const PREINTERNED_SYMBOLS_COUNT: u32 = #preinterned_symbols_count; #[doc(hidden)] #[allow(non_upper_case_globals)] diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs index bd0c08a53c4f2..9c53453df5b54 100644 --- a/compiler/rustc_macros/src/symbols/tests.rs +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -27,7 +27,7 @@ fn test_symbols() { let body_tokens = m.mac.tokens.clone(); - test_symbols_macro(body_tokens, &[]); + test_symbols_macro(body_tokens, &["proc_macro::tracked_env is not available in unit test"]); } fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index c8f3c2a20a694..0b5426c3bb1a1 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -21,35 +21,17 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html -use crate::infer::MemberConstraint; -use crate::mir::ConstraintCategory; -use crate::ty::GenericArg; -use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt}; use rustc_macros::HashStable; +use rustc_type_ir::Canonical as IrCanonical; use smallvec::SmallVec; -use std::fmt::Display; use std::ops::Index; -/// A "canonicalized" type `V` is one where all free inference -/// variables have been rewritten to "canonical vars". These are -/// numbered starting from 0 in order of first appearance. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable)] -pub struct Canonical<'tcx, V> { - pub value: V, - pub max_universe: ty::UniverseIndex, - pub variables: CanonicalVarInfos<'tcx>, -} +use crate::infer::MemberConstraint; +use crate::mir::ConstraintCategory; +use crate::ty::GenericArg; +use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt}; -impl<'tcx, V: Display> std::fmt::Display for Canonical<'tcx, V> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}", - self.value, self.max_universe, self.variables - ) - } -} +pub type Canonical<'tcx, V> = IrCanonical, V>; pub type CanonicalVarInfos<'tcx> = &'tcx List>; @@ -379,56 +361,6 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } } -impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> { - pub fn is_proven(&self) -> bool { - self.value.is_proven() - } - - pub fn is_ambiguous(&self) -> bool { - !self.is_proven() - } -} - -impl<'tcx, V> Canonical<'tcx, V> { - /// Allows you to map the `value` of a canonical while keeping the - /// same set of bound variables. - /// - /// **WARNING:** This function is very easy to mis-use, hence the - /// name! In particular, the new value `W` must use all **the - /// same type/region variables** in **precisely the same order** - /// as the original! (The ordering is defined by the - /// `TypeFoldable` implementation of the type in question.) - /// - /// An example of a **correct** use of this: - /// - /// ```rust,ignore (not real code) - /// let a: Canonical<'_, T> = ...; - /// let b: Canonical<'_, (T,)> = a.unchecked_map(|v| (v, )); - /// ``` - /// - /// An example of an **incorrect** use of this: - /// - /// ```rust,ignore (not real code) - /// let a: Canonical<'tcx, T> = ...; - /// let ty: Ty<'tcx> = ...; - /// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty)); - /// ``` - pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical<'tcx, W> { - let Canonical { max_universe, variables, value } = self; - Canonical { max_universe, variables, value: map_op(value) } - } - - /// Allows you to map the `value` of a canonical while keeping the same set of - /// bound variables. - /// - /// **WARNING:** This function is very easy to mis-use, hence the name! See - /// the comment of [Canonical::unchecked_map] for more details. - pub fn unchecked_rebind(self, value: W) -> Canonical<'tcx, W> { - let Canonical { max_universe, variables, value: _ } = self; - Canonical { max_universe, variables, value } - } -} - pub type QueryOutlivesConstraint<'tcx> = (ty::OutlivesPredicate, Region<'tcx>>, ConstraintCategory<'tcx>); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 23d77a1ebe49e..062b03e71fdc1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -251,6 +251,7 @@ rustc_queries! { "computing type of opaque `{path}`", path = tcx.def_path_str(key), } + cycle_stash } query type_alias_is_lazy(key: DefId) -> bool { @@ -341,7 +342,7 @@ rustc_queries! { query opaque_types_defined_by( key: LocalDefId - ) -> &'tcx [LocalDefId] { + ) -> &'tcx ty::List { desc { |tcx| "computing the opaque types defined by `{}`", tcx.def_path_str(key.to_def_id()) diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index e4069e11df274..d52a717b6b09b 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -230,9 +230,9 @@ impl<'tcx, D: TyDecoder>> Decodable assert!(pos >= SHORTHAND_OFFSET); let shorthand = pos - SHORTHAND_OFFSET; - decoder.with_position(shorthand, ty::PredicateKind::decode) + decoder.with_position(shorthand, as Decodable>::decode) } else { - ty::PredicateKind::decode(decoder) + as Decodable>::decode(decoder) }, bound_vars, ) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 3c8bac2a9d3db..a669ff8d96115 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -6,7 +6,7 @@ pub mod tls; use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKindStruct}; -use crate::infer::canonical::CanonicalVarInfo; +use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::struct_lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -84,10 +84,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AdtDef = ty::AdtDef<'tcx>; type GenericArgs = ty::GenericArgsRef<'tcx>; type GenericArg = ty::GenericArg<'tcx>; + type Term = ty::Term<'tcx>; + type Binder = Binder<'tcx, T>; - type Predicate = Predicate<'tcx>; - type PredicateKind = ty::PredicateKind<'tcx>; type TypeAndMut = TypeAndMut<'tcx>; + type CanonicalVars = CanonicalVarInfos<'tcx>; + type Ty = Ty<'tcx>; type Tys = &'tcx List>; type AliasTy = ty::AliasTy<'tcx>; @@ -95,10 +97,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundTy = ty::BoundTy; type PlaceholderTy = ty::PlaceholderType; type InferTy = InferTy; + type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; type PolyFnSig = PolyFnSig<'tcx>; type AllocId = crate::mir::interpret::AllocId; + type Const = ty::Const<'tcx>; type InferConst = ty::InferConst; type AliasConst = ty::UnevaluatedConst<'tcx>; @@ -107,6 +111,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundConst = ty::BoundVar; type ValueConst = ty::ValTree<'tcx>; type ExprConst = ty::Expr<'tcx>; + type Region = Region<'tcx>; type EarlyBoundRegion = ty::EarlyBoundRegion; type BoundRegion = ty::BoundRegion; @@ -114,6 +119,15 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type InferRegion = ty::RegionVid; type PlaceholderRegion = ty::PlaceholderRegion; + type Predicate = Predicate<'tcx>; + type TraitPredicate = ty::TraitPredicate<'tcx>; + type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>; + type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>; + type ProjectionPredicate = ty::ProjectionPredicate<'tcx>; + type SubtypePredicate = ty::SubtypePredicate<'tcx>; + type CoercePredicate = ty::CoercePredicate<'tcx>; + type ClosureKind = ty::ClosureKind; + fn ty_and_mut_to_parts( TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>, ) -> (Self::Ty, ty::Mutability) { @@ -148,6 +162,7 @@ pub struct CtxtInterners<'tcx> { external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>, predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, fields: InternedSet<'tcx, List>, + local_def_ids: InternedSet<'tcx, List>, } impl<'tcx> CtxtInterners<'tcx> { @@ -173,6 +188,7 @@ impl<'tcx> CtxtInterners<'tcx> { external_constraints: Default::default(), predefined_opaques_in_body: Default::default(), fields: Default::default(), + local_def_ids: Default::default(), } } @@ -1559,6 +1575,7 @@ slice_interners!( place_elems: pub mk_place_elems(PlaceElem<'tcx>), bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind), fields: pub mk_fields(FieldIdx), + local_def_ids: intern_local_def_ids(LocalDefId), ); impl<'tcx> TyCtxt<'tcx> { @@ -1788,6 +1805,13 @@ impl<'tcx> TyCtxt<'tcx> { self.intern_clauses(clauses) } + pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List { + // FIXME consider asking the input slice to be sorted to avoid + // re-interning permutations, in which case that would be asserted + // here. + self.intern_local_def_ids(clauses) + } + pub fn mk_const_list_from_iter(self, iter: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 184a70ed4cb8c..738bb5e8b1959 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -241,7 +241,9 @@ impl<'tcx> Ty<'tcx> { } ty::Dynamic(..) => "trait object".into(), ty::Closure(..) => "closure".into(), - ty::Coroutine(def_id, ..) => tcx.coroutine_kind(def_id).unwrap().descr().into(), + ty::Coroutine(def_id, ..) => { + format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into() + } ty::CoroutineWitness(..) => "coroutine witness".into(), ty::Infer(ty::TyVar(_)) => "inferred type".into(), ty::Infer(ty::IntVar(_)) => "integer".into(), @@ -299,7 +301,9 @@ impl<'tcx> Ty<'tcx> { ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(..) => "trait object".into(), ty::Closure(..) => "closure".into(), - ty::Coroutine(def_id, ..) => tcx.coroutine_kind(def_id).unwrap().descr().into(), + ty::Coroutine(def_id, ..) => { + format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into() + } ty::CoroutineWitness(..) => "coroutine witness".into(), ty::Tuple(..) => "tuple".into(), ty::Placeholder(..) => "higher-ranked type".into(), diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index d75315b7ac955..a348e9f608aab 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -134,7 +134,7 @@ impl FlagComputation { if should_remove_further_specializable { self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE; } - self.add_flags(TypeFlags::HAS_TY_GENERATOR); + self.add_flags(TypeFlags::HAS_TY_COROUTINE); } &ty::Closure(_, args) => { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 44e88d3e2302f..888ee1d237ae7 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -237,6 +237,20 @@ impl<'tcx> Generics { } } + /// Returns the `GenericParamDef` with the given index if available. + pub fn opt_param_at( + &'tcx self, + param_index: usize, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx GenericParamDef> { + if let Some(index) = param_index.checked_sub(self.parent_count) { + self.params.get(index) + } else { + tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) + .opt_param_at(param_index, tcx) + } + } + pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] { if let Some(index) = param_index.checked_sub(self.parent_count) { &self.params[..index] @@ -268,6 +282,20 @@ impl<'tcx> Generics { } } + /// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this + /// `Generics`. + pub fn opt_type_param( + &'tcx self, + param: &ParamTy, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx GenericParamDef> { + let param = self.opt_param_at(param.index as usize, tcx)?; + match param.kind { + GenericParamDefKind::Type { .. } => Some(param), + _ => None, + } + } + /// Returns the `GenericParamDef` associated with this `ParamConst`. pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 769b374c793a5..739d4fa886ec3 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -97,17 +97,17 @@ pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::BoundRegionKind::*; pub use self::sty::{ AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, - BoundVariableKind, CanonicalPolyFnSig, ClosureArgs, ClosureArgsParts, ConstKind, ConstVid, - CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate, + BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind, + ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection, - PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, Region, RegionKind, RegionVid, - TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, + PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, PredicateKind, Region, + RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, TypeckResults, - UserType, UserTypeAnnotationIndex, + CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, + TypeckResults, UserType, UserTypeAnnotationIndex, }; pub mod _match; @@ -233,6 +233,7 @@ impl MainDefinition { #[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub struct ImplHeader<'tcx> { pub impl_def_id: DefId, + pub impl_args: ty::GenericArgsRef<'tcx>, pub self_ty: Ty<'tcx>, pub trait_ref: Option>, pub predicates: Vec>, @@ -626,98 +627,6 @@ impl<'tcx> Clause<'tcx> { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -/// A clause is something that can appear in where bounds or be inferred -/// by implied bounds. -pub enum ClauseKind<'tcx> { - /// Corresponds to `where Foo: Bar`. `Foo` here would be - /// the `Self` type of the trait reference and `A`, `B`, and `C` - /// would be the type parameters. - Trait(TraitPredicate<'tcx>), - - /// `where 'a: 'b` - RegionOutlives(RegionOutlivesPredicate<'tcx>), - - /// `where T: 'a` - TypeOutlives(TypeOutlivesPredicate<'tcx>), - - /// `where ::Name == X`, approximately. - /// See the `ProjectionPredicate` struct for details. - Projection(ProjectionPredicate<'tcx>), - - /// Ensures that a const generic argument to a parameter `const N: u8` - /// is of type `u8`. - ConstArgHasType(Const<'tcx>, Ty<'tcx>), - - /// No syntax: `T` well-formed. - WellFormed(GenericArg<'tcx>), - - /// Constant initializer must evaluate successfully. - ConstEvaluatable(ty::Const<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -pub enum PredicateKind<'tcx> { - /// Prove a clause - Clause(ClauseKind<'tcx>), - - /// Trait must be object-safe. - ObjectSafe(DefId), - - /// No direct syntax. May be thought of as `where T: FnFoo<...>` - /// for some generic args `...` and `T` being a closure type. - /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, GenericArgsRef<'tcx>, ClosureKind), - - /// `T1 <: T2` - /// - /// This obligation is created most often when we have two - /// unresolved type variables and hence don't have enough - /// information to process the subtyping obligation yet. - Subtype(SubtypePredicate<'tcx>), - - /// `T1` coerced to `T2` - /// - /// Like a subtyping obligation, this is created most often - /// when we have two unresolved type variables and hence - /// don't have enough information to process the coercion - /// obligation yet. At the moment, we actually process coercions - /// very much like subtyping and don't handle the full coercion - /// logic. - Coerce(CoercePredicate<'tcx>), - - /// Constants must be equal. The first component is the const that is expected. - ConstEquate(Const<'tcx>, Const<'tcx>), - - /// A marker predicate that is always ambiguous. - /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. - Ambiguous, - - /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. - /// This predicate requires two terms to be equal to eachother. - /// - /// Only used for new solver - AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, Debug)] -pub enum AliasRelationDirection { - Equate, - Subtype, -} - -impl std::fmt::Display for AliasRelationDirection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AliasRelationDirection::Equate => write!(f, "=="), - AliasRelationDirection::Subtype => write!(f, "<:"), - } - } -} - /// The crate outlives map is computed during typeck and contains the /// outlives of every item in the local crate. You should not use it /// directly, because to do so will make your pass dependent on the diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 22648d853f4e6..433ac33f1b820 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2649,6 +2649,13 @@ macro_rules! forward_display_to_print { } macro_rules! define_print_and_forward_display { + (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { + define_print!(($self, $cx): $($ty $print)*); + forward_display_to_print!($($ty),+); + }; +} + +macro_rules! define_print { (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { fn print(&$self, $cx: &mut P) -> Result<(), PrintError> { @@ -2660,8 +2667,6 @@ macro_rules! define_print_and_forward_display { Ok(()) } })+ - - forward_display_to_print!($($ty),+); }; } @@ -2759,6 +2764,51 @@ forward_display_to_print! { ty::OutlivesPredicate, ty::Region<'tcx>> } +define_print! { + (self, cx): + + ty::ClauseKind<'tcx> { + match *self { + ty::ClauseKind::Trait(ref data) => { + p!(print(data)) + } + ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), + ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), + ty::ClauseKind::Projection(predicate) => p!(print(predicate)), + ty::ClauseKind::ConstArgHasType(ct, ty) => { + p!("the constant `", print(ct), "` has type `", print(ty), "`") + }, + ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::ClauseKind::ConstEvaluatable(ct) => { + p!("the constant `", print(ct), "` can be evaluated") + } + } + } + + ty::PredicateKind<'tcx> { + match *self { + ty::PredicateKind::Clause(data) => { + p!(print(data)) + } + ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), + ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), + ty::PredicateKind::ObjectSafe(trait_def_id) => { + p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") + } + ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!( + "the closure `", + print_value_path(closure_def_id, &[]), + write("` implements the trait `{}`", kind) + ), + ty::PredicateKind::ConstEquate(c1, c2) => { + p!("the constant `", print(c1), "` equals `", print(c2), "`") + } + ty::PredicateKind::Ambiguous => p!("ambiguous"), + ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), + } + } +} + define_print_and_forward_display! { (self, cx): @@ -2887,55 +2937,13 @@ define_print_and_forward_display! { } ty::Predicate<'tcx> { - let binder = self.kind(); - p!(print(binder)) + p!(print(self.kind())) } ty::Clause<'tcx> { p!(print(self.kind())) } - ty::ClauseKind<'tcx> { - match *self { - ty::ClauseKind::Trait(ref data) => { - p!(print(data)) - } - ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::Projection(predicate) => p!(print(predicate)), - ty::ClauseKind::ConstArgHasType(ct, ty) => { - p!("the constant `", print(ct), "` has type `", print(ty), "`") - }, - ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), - ty::ClauseKind::ConstEvaluatable(ct) => { - p!("the constant `", print(ct), "` can be evaluated") - } - } - } - - ty::PredicateKind<'tcx> { - match *self { - ty::PredicateKind::Clause(data) => { - p!(print(data)) - } - ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), - ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") - } - ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!( - "the closure `", - print_value_path(closure_def_id, &[]), - write("` implements the trait `{}`", kind) - ), - ty::PredicateKind::ConstEquate(c1, c2) => { - p!("the constant `", print(c1), "` equals `", print(c2), "`") - } - ty::PredicateKind::Ambiguous => p!("ambiguous"), - ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), - } - } - GenericArg<'tcx> { match self.unpack() { GenericArgKind::Lifetime(lt) => p!(print(lt)), diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 967f62cdbca56..6af68bc5dbabb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -185,43 +185,6 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> { } } -impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"), - ty::ClauseKind::Trait(ref a) => a.fmt(f), - ty::ClauseKind::RegionOutlives(ref pair) => pair.fmt(f), - ty::ClauseKind::TypeOutlives(ref pair) => pair.fmt(f), - ty::ClauseKind::Projection(ref pair) => pair.fmt(f), - ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({data:?})"), - ty::ClauseKind::ConstEvaluatable(ct) => { - write!(f, "ConstEvaluatable({ct:?})") - } - } - } -} - -impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::PredicateKind::Clause(ref a) => a.fmt(f), - ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), - ty::PredicateKind::Coerce(ref pair) => pair.fmt(f), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - write!(f, "ObjectSafe({trait_def_id:?})") - } - ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { - write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})") - } - ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), - ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), - ty::PredicateKind::AliasRelate(t1, t2, dir) => { - write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") - } - } - } -} - impl<'tcx> fmt::Debug for AliasTy<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { WithInfcx::with_no_infcx(self).fmt(f) @@ -486,7 +449,6 @@ TrivialTypeTraversalImpls! { crate::ty::IntVarValue, crate::ty::adjustment::PointerCoercion, crate::ty::RegionVid, - crate::ty::UniverseIndex, crate::ty::Variance, ::rustc_span::Span, ::rustc_span::symbol::Ident, @@ -503,7 +465,6 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_hir::Mutability, ::rustc_hir::Unsafety, ::rustc_target::spec::abi::Abi, - crate::ty::AliasRelationDirection, crate::ty::ClosureKind, crate::ty::ParamConst, crate::ty::ParamTy, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 53260a3996cfb..44592b10d5574 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -32,10 +32,12 @@ use std::fmt; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; +use rustc_type_ir::ClauseKind as IrClauseKind; use rustc_type_ir::CollectAndApply; use rustc_type_ir::ConstKind as IrConstKind; use rustc_type_ir::DebugWithInfcx; use rustc_type_ir::DynKind; +use rustc_type_ir::PredicateKind as IrPredicateKind; use rustc_type_ir::RegionKind as IrRegionKind; use rustc_type_ir::TyKind as IrTyKind; use rustc_type_ir::TyKind::*; @@ -47,6 +49,8 @@ use super::GenericParamDefKind; pub type TyKind<'tcx> = IrTyKind>; pub type RegionKind<'tcx> = IrRegionKind>; pub type ConstKind<'tcx> = IrConstKind>; +pub type PredicateKind<'tcx> = IrPredicateKind>; +pub type ClauseKind<'tcx> = IrClauseKind>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 7d516410b201f..58ad1eb900fd5 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -594,10 +594,27 @@ pub struct CanonicalUserTypeAnnotation<'tcx> { /// Canonical user type annotation. pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>; -impl<'tcx> CanonicalUserType<'tcx> { +/// A user-given type annotation attached to a constant. These arise +/// from constants that are named via paths, like `Foo::::new` and +/// so forth. +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] +#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub enum UserType<'tcx> { + Ty(Ty<'tcx>), + + /// The canonical type is the result of `type_of(def_id)` with the + /// given substitutions applied. + TypeOf(DefId, UserArgs<'tcx>), +} + +pub trait IsIdentity { + fn is_identity(&self) -> bool; +} + +impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { /// Returns `true` if this represents a substitution of the form `[?0, ?1, ?2]`, /// i.e., each thing is mapped to a canonical variable with the same index. - pub fn is_identity(&self) -> bool { + fn is_identity(&self) -> bool { match self.value { UserType::Ty(_) => false, UserType::TypeOf(_, user_args) => { @@ -640,19 +657,6 @@ impl<'tcx> CanonicalUserType<'tcx> { } } -/// A user-given type annotation attached to a constant. These arise -/// from constants that are named via paths, like `Foo::::new` and -/// so forth. -#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] -#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub enum UserType<'tcx> { - Ty(Ty<'tcx>), - - /// The canonical type is the result of `type_of(def_id)` with the - /// given substitutions applied. - TypeOf(DefId, UserArgs<'tcx>), -} - impl<'tcx> std::fmt::Display for UserType<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 15de994864462..ab0999b3f197f 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -48,7 +48,7 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable> { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } fn has_coroutines(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_GENERATOR) + self.has_type_flags(TypeFlags::HAS_TY_COROUTINE) } fn references_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_ERROR) diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index db3886b32beb4..32711c23dc4e4 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -379,6 +379,5 @@ mir_build_unused_unsafe = unnecessary `unsafe` block .label = unnecessary `unsafe` block mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block -mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn mir_build_variant_defined_here = not covered diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 1cf8c202ea4be..681e311a0170c 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [ 0. Pre-match ] /// | /// [ 1. Evaluate Scrutinee (expression being matched on) ] - /// [ (fake read of scrutinee) ] + /// [ (PlaceMention of scrutinee) ] /// | /// [ 2. Decision tree -- check discriminants ] <--------+ /// | | @@ -184,7 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// We generate MIR in the following steps: /// - /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]). + /// 1. Evaluate the scrutinee and add the PlaceMention of it ([Builder::lower_scrutinee]). /// 2. Create the decision tree ([Builder::lower_match_tree]). /// 3. Determine the fake borrows that are needed from the places that were /// matched against and create the required temporaries for them @@ -223,6 +223,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_temps = self.lower_match_tree( block, scrutinee_span, + &scrutinee_place, match_start_span, match_has_guard, &mut candidates, @@ -238,7 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) } - /// Evaluate the scrutinee and add the fake read of it. + /// Evaluate the scrutinee and add the PlaceMention for it. fn lower_scrutinee( &mut self, mut block: BasicBlock, @@ -246,26 +247,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span: Span, ) -> BlockAnd> { let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee)); - // Matching on a `scrutinee_place` with an uninhabited type doesn't - // generate any memory reads by itself, and so if the place "expression" - // contains unsafe operations like raw pointer dereferences or union - // field projections, we wouldn't know to require an `unsafe` block - // around a `match` equivalent to `std::intrinsics::unreachable()`. - // See issue #47412 for this hole being discovered in the wild. - // - // HACK(eddyb) Work around the above issue by adding a dummy inspection - // of `scrutinee_place`, specifically by applying `ReadForMatch`. - // - // NOTE: ReadForMatch also checks that the scrutinee is initialized. - // This is currently needed to not allow matching on an uninitialized, - // uninhabited value. If we get never patterns, those will check that - // the place is initialized, and so this read would only be used to - // check safety. - let cause_matched_place = FakeReadCause::ForMatchedPlace(None); - let source_info = self.source_info(scrutinee_span); - if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { - self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); + let source_info = self.source_info(scrutinee_span); + self.cfg.push_place_mention(block, source_info, scrutinee_place); } block.and(scrutinee_place_builder) @@ -304,6 +288,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, block: BasicBlock, scrutinee_span: Span, + scrutinee_place_builder: &PlaceBuilder<'tcx>, match_start_span: Span, match_has_guard: bool, candidates: &mut [&mut Candidate<'pat, 'tcx>], @@ -331,6 +316,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // otherwise block. Match checking will ensure this is actually // unreachable. let source_info = self.source_info(scrutinee_span); + + // Matching on a `scrutinee_place` with an uninhabited type doesn't + // generate any memory reads by itself, and so if the place "expression" + // contains unsafe operations like raw pointer dereferences or union + // field projections, we wouldn't know to require an `unsafe` block + // around a `match` equivalent to `std::intrinsics::unreachable()`. + // See issue #47412 for this hole being discovered in the wild. + // + // HACK(eddyb) Work around the above issue by adding a dummy inspection + // of `scrutinee_place`, specifically by applying `ReadForMatch`. + // + // NOTE: ReadForMatch also checks that the scrutinee is initialized. + // This is currently needed to not allow matching on an uninitialized, + // uninhabited value. If we get never patterns, those will check that + // the place is initialized, and so this read would only be used to + // check safety. + let cause_matched_place = FakeReadCause::ForMatchedPlace(None); + + if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { + self.cfg.push_fake_read( + otherwise_block, + source_info, + cause_matched_place, + scrutinee_place, + ); + } + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); } @@ -599,13 +611,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } _ => { - let place_builder = unpack!(block = self.as_place_builder(block, initializer)); - - if let Some(place) = place_builder.try_to_place(self) { - let source_info = self.source_info(initializer.span); - self.cfg.push_place_mention(block, source_info, place); - } - + let place_builder = + unpack!(block = self.lower_scrutinee(block, initializer, initializer.span)); self.place_into_pattern(block, &irrefutable_pat, place_builder, true) } } @@ -622,6 +629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_temps = self.lower_match_tree( block, irrefutable_pat.span, + &initializer, irrefutable_pat.span, false, &mut [&mut candidate], @@ -1841,6 +1849,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_temps = self.lower_match_tree( block, pat.span, + &expr_place_builder, pat.span, false, &mut [&mut guard_candidate, &mut otherwise_candidate], @@ -2342,6 +2351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_temps = this.lower_match_tree( block, initializer_span, + &scrutinee, pattern.span, false, &mut [&mut candidate, &mut wildcard], diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 3265f92e514d4..45be501537130 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -13,6 +13,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use std::mem; use std::ops::Bound; struct UnsafetyVisitor<'a, 'tcx> { @@ -24,7 +25,6 @@ struct UnsafetyVisitor<'a, 'tcx> { /// The current "safety context". This notably tracks whether we are in an /// `unsafe` block, and whether it has been used. safety_context: SafetyContext, - body_unsafety: BodyUnsafety, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). body_target_features: &'tcx [Symbol], @@ -34,43 +34,50 @@ struct UnsafetyVisitor<'a, 'tcx> { in_union_destructure: bool, param_env: ParamEnv<'tcx>, inside_adt: bool, + warnings: &'a mut Vec, } impl<'tcx> UnsafetyVisitor<'_, 'tcx> { fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) { - if let ( - SafetyContext::UnsafeBlock { span: enclosing_span, .. }, - SafetyContext::UnsafeBlock { span: block_span, hir_id, .. }, - ) = (self.safety_context, safety_context) + let prev_context = mem::replace(&mut self.safety_context, safety_context); + + f(self); + + let safety_context = mem::replace(&mut self.safety_context, prev_context); + if let SafetyContext::UnsafeBlock { used, span, hir_id, nested_used_blocks } = + safety_context { - self.warn_unused_unsafe( - hir_id, - block_span, - Some(UnusedUnsafeEnclosing::Block { - span: self.tcx.sess.source_map().guess_head_span(enclosing_span), - }), - ); - f(self); - } else { - let prev_context = self.safety_context; - self.safety_context = safety_context; + if !used { + self.warn_unused_unsafe(hir_id, span, None); - f(self); + if let SafetyContext::UnsafeBlock { + nested_used_blocks: ref mut prev_nested_used_blocks, + .. + } = self.safety_context + { + prev_nested_used_blocks.extend(nested_used_blocks); + } + } else { + for block in nested_used_blocks { + self.warn_unused_unsafe( + block.hir_id, + block.span, + Some(UnusedUnsafeEnclosing::Block { + span: self.tcx.sess.source_map().guess_head_span(span), + }), + ); + } - if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context { - self.warn_unused_unsafe( - hir_id, - span, - if self.unsafe_op_in_unsafe_fn_allowed() { - self.body_unsafety - .unsafe_fn_sig_span() - .map(|span| UnusedUnsafeEnclosing::Function { span }) - } else { - None - }, - ); + match self.safety_context { + SafetyContext::UnsafeBlock { + nested_used_blocks: ref mut prev_nested_used_blocks, + .. + } => { + prev_nested_used_blocks.push(NestedUsedBlock { hir_id, span }); + } + _ => (), + } } - self.safety_context = prev_context; } } @@ -102,18 +109,12 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } fn warn_unused_unsafe( - &self, + &mut self, hir_id: hir::HirId, block_span: Span, enclosing_unsafe: Option, ) { - let block_span = self.tcx.sess.source_map().guess_head_span(block_span); - self.tcx.emit_spanned_lint( - UNUSED_UNSAFE, - hir_id, - block_span, - UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, - ); + self.warnings.push(UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe }); } /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. @@ -128,7 +129,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { self.tcx.ensure_with_value().mir_built(def); let inner_thir = &inner_thir.steal(); let hir_context = self.tcx.hir().local_def_id_to_hir_id(def); - let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self }; + let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe); + let mut inner_visitor = UnsafetyVisitor { + thir: inner_thir, + hir_context, + safety_context, + warnings: self.warnings, + ..*self + }; inner_visitor.visit_expr(&inner_thir[expr]); // Unsafe blocks can be used in the inner body, make sure to take it into account self.safety_context = inner_visitor.safety_context; @@ -195,8 +203,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }); } BlockSafety::ExplicitUnsafe(hir_id) => { + let used = + matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _)); self.in_safety_context( - SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false }, + SafetyContext::UnsafeBlock { + span: block.span, + hir_id, + used, + nested_used_blocks: Vec::new(), + }, |this| visit::walk_block(this, block), ); } @@ -481,36 +496,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } -#[derive(Clone, Copy)] +#[derive(Clone)] enum SafetyContext { Safe, BuiltinUnsafeBlock, UnsafeFn, - UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool }, + UnsafeBlock { + span: Span, + hir_id: hir::HirId, + used: bool, + nested_used_blocks: Vec, + }, } #[derive(Clone, Copy)] -enum BodyUnsafety { - /// The body is not unsafe. - Safe, - /// The body is an unsafe function. The span points to - /// the signature of the function. - Unsafe(Span), +struct NestedUsedBlock { + hir_id: hir::HirId, + span: Span, } -impl BodyUnsafety { - /// Returns whether the body is unsafe. - fn is_unsafe(&self) -> bool { - matches!(self, BodyUnsafety::Unsafe(_)) - } - - /// If the body is unsafe, returns the `Span` of its signature. - fn unsafe_fn_sig_span(self) -> Option { - match self { - BodyUnsafety::Unsafe(span) => Some(span), - BodyUnsafety::Safe => None, - } - } +struct UnusedUnsafeWarning { + hir_id: hir::HirId, + block_span: Span, + enclosing_unsafe: Option, } #[derive(Clone, Copy, PartialEq)] @@ -803,27 +811,37 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { } let hir_id = tcx.hir().local_def_id_to_hir_id(def); - let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| { + let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { if fn_sig.header.unsafety == hir::Unsafety::Unsafe { - BodyUnsafety::Unsafe(fn_sig.span) + SafetyContext::UnsafeFn } else { - BodyUnsafety::Safe + SafetyContext::Safe } }); let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features; - let safety_context = - if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }; + let mut warnings = Vec::new(); let mut visitor = UnsafetyVisitor { tcx, thir, safety_context, hir_context: hir_id, - body_unsafety, body_target_features, assignment_info: None, in_union_destructure: false, param_env: tcx.param_env(def), inside_adt: false, + warnings: &mut warnings, }; visitor.visit_expr(&thir[expr]); + + warnings.sort_by_key(|w| w.block_span); + for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings { + let block_span = tcx.sess.source_map().guess_head_span(block_span); + tcx.emit_spanned_lint( + UNUSED_UNSAFE, + hir_id, + block_span, + UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, + ); + } } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index c09dd1864185c..730670a8369cc 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -392,11 +392,6 @@ pub enum UnusedUnsafeEnclosing { #[primary_span] span: Span, }, - #[label(mir_build_unused_unsafe_enclosing_fn_label)] - Function { - #[primary_span] - span: Span, - }, } pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 099fefbf06871..745c3046d22c2 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -20,7 +20,7 @@ mod build; mod check_unsafety; mod errors; pub mod lints; -pub mod thir; +mod thir; use rustc_middle::query::Providers; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 840c6e13bef31..6c3564a20f6bf 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -191,11 +191,16 @@ impl<'tcx> Cx<'tcx> { source: self.mirror_expr(source), cast: PointerCoercion::ArrayToPointer, } - } else { - // check whether this is casting an enum variant discriminant - // to prevent cycles, we refer to the discriminant initializer + } else if let hir::ExprKind::Path(ref qpath) = source.kind + && let res = self.typeck_results().qpath_res(qpath, source.hir_id) + && let ty = self.typeck_results().node_type(source.hir_id) + && let ty::Adt(adt_def, args) = ty.kind() + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res + { + // Check whether this is casting an enum variant discriminant. + // To prevent cycles, we refer to the discriminant initializer, // which is always an integer and thus doesn't need to know the - // enum's layout (or its tag type) to compute it during const eval + // enum's layout (or its tag type) to compute it during const eval. // Example: // enum Foo { // A, @@ -204,21 +209,6 @@ impl<'tcx> Cx<'tcx> { // The correct solution would be to add symbolic computations to miri, // so we wouldn't have to compute and store the actual value - let hir::ExprKind::Path(ref qpath) = source.kind else { - return ExprKind::Cast { source: self.mirror_expr(source) }; - }; - - let res = self.typeck_results().qpath_res(qpath, source.hir_id); - let ty = self.typeck_results().node_type(source.hir_id); - let ty::Adt(adt_def, args) = ty.kind() else { - return ExprKind::Cast { source: self.mirror_expr(source) }; - }; - - let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res - else { - return ExprKind::Cast { source: self.mirror_expr(source) }; - }; - let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); let (discr_did, discr_offset) = adt_def.discriminant_def_for_variant(idx); @@ -255,6 +245,10 @@ impl<'tcx> Cx<'tcx> { }; ExprKind::Cast { source } + } else { + // Default to `ExprKind::Cast` for all explicit casts. + // MIR building then picks the right MIR casts based on the types. + ExprKind::Cast { source: self.mirror_expr(source) } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 2255220808ea7..186c77795e494 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -53,14 +53,13 @@ use smallvec::{smallvec, SmallVec}; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{HirId, RangeEnd}; +use rustc_hir::RangeEnd; use rustc_index::Idx; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; @@ -68,7 +67,6 @@ use self::Constructor::*; use self::SliceKind::*; use super::usefulness::{MatchCheckCtxt, PatCtxt}; -use crate::errors::{Overlap, OverlappingRangeEndpoints}; /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { @@ -111,15 +109,15 @@ pub(crate) struct IntRange { impl IntRange { #[inline] - fn is_integral(ty: Ty<'_>) -> bool { + pub(super) fn is_integral(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) } - fn is_singleton(&self) -> bool { + pub(super) fn is_singleton(&self) -> bool { self.range.start() == self.range.end() } - fn boundaries(&self) -> (u128, u128) { + pub(super) fn boundaries(&self) -> (u128, u128) { (*self.range.start(), *self.range.end()) } @@ -177,23 +175,6 @@ impl IntRange { } } - fn suspicious_intersection(&self, other: &Self) -> bool { - // `false` in the following cases: - // 1 ---- // 1 ---------- // 1 ---- // 1 ---- - // 2 ---------- // 2 ---- // 2 ---- // 2 ---- - // - // The following are currently `false`, but could be `true` in the future (#64007): - // 1 --------- // 1 --------- - // 2 ---------- // 2 ---------- - // - // `true` in the following cases: - // 1 ------- // 1 ------- - // 2 -------- // 2 ------- - let (lo, hi) = self.boundaries(); - let (other_lo, other_hi) = other.boundaries(); - (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton() - } - /// Partition a range of integers into disjoint subranges. This does constructor splitting for /// integer ranges as explained at the top of the file. /// @@ -293,7 +274,7 @@ impl IntRange { } /// Only used for displaying the range. - fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { + pub(super) fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { let (lo, hi) = self.boundaries(); let bias = IntRange::signed_bias(tcx, ty); @@ -315,51 +296,6 @@ impl IntRange { Pat { ty, span: DUMMY_SP, kind } } - - /// Lint on likely incorrect range patterns (#63987) - pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( - &self, - pcx: &PatCtxt<'_, 'p, 'tcx>, - pats: impl Iterator>, - column_count: usize, - lint_root: HirId, - ) { - if self.is_singleton() { - return; - } - - if column_count != 1 { - // FIXME: for now, only check for overlapping ranges on simple range - // patterns. Otherwise with the current logic the following is detected - // as overlapping: - // ``` - // match (0u8, true) { - // (0 ..= 125, false) => {} - // (125 ..= 255, true) => {} - // _ => {} - // } - // ``` - return; - } - - let overlap: Vec<_> = pats - .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) - .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| Overlap { - range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), - span, - }) - .collect(); - - if !overlap.is_empty() { - pcx.cx.tcx.emit_spanned_lint( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - lint_root, - pcx.span, - OverlappingRangeEndpoints { overlap, range: pcx.span }, - ); - } - } } /// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and @@ -644,7 +580,7 @@ impl<'tcx> Constructor<'tcx> { _ => None, } } - fn as_int_range(&self) -> Option<&IntRange> { + pub(super) fn as_int_range(&self) -> Option<&IntRange> { match self { IntRange(range) => Some(range), _ => None, diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 5218f772484fd..25e0f3ceaa473 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -307,8 +307,10 @@ use self::ArmType::*; use self::Usefulness::*; -use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat}; -use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; +use super::deconstruct_pat::{ + Constructor, ConstructorSet, DeconstructedPat, IntRange, SplitConstructorSet, WitnessPat, +}; +use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered}; use rustc_data_structures::captures::Captures; @@ -317,6 +319,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::lint; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::{Span, DUMMY_SP}; @@ -473,11 +476,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { Matrix { patterns: vec![] } } - /// Number of columns of this matrix. `None` is the matrix is empty. - pub(super) fn column_count(&self) -> Option { - self.patterns.get(0).map(|r| r.len()) - } - /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. fn push(&mut self, row: PatStack<'p, 'tcx>) { @@ -833,15 +831,6 @@ fn is_useful<'p, 'tcx>( let v_ctor = v.head().ctor(); debug!(?v_ctor); - if let Constructor::IntRange(ctor_range) = &v_ctor { - // Lint on likely incorrect range patterns (#63987) - ctor_range.lint_overlapping_range_endpoints( - pcx, - matrix.heads(), - matrix.column_count().unwrap_or(0), - lint_root, - ) - } // We split the head constructor of `v`. let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); // For each constructor, we compute whether there's a value that starts with it that would @@ -875,22 +864,102 @@ fn is_useful<'p, 'tcx>( ret } +/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that +/// inspect the same subvalue". +/// This is used to traverse patterns column-by-column for lints. Despite similarities with +/// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns, +/// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete). +#[derive(Debug)] +struct PatternColumn<'p, 'tcx> { + patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>, +} + +impl<'p, 'tcx> PatternColumn<'p, 'tcx> { + fn new(patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>) -> Self { + Self { patterns } + } + + fn is_empty(&self) -> bool { + self.patterns.is_empty() + } + fn head_ty(&self) -> Option> { + if self.patterns.len() == 0 { + return None; + } + // If the type is opaque and it is revealed anywhere in the column, we take the revealed + // version. Otherwise we could encounter constructors for the revealed type and crash. + let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..)); + let first_ty = self.patterns[0].ty(); + if is_opaque(first_ty) { + for pat in &self.patterns { + let ty = pat.ty(); + if !is_opaque(ty) { + return Some(ty); + } + } + } + Some(first_ty) + } + + fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> { + let column_ctors = self.patterns.iter().map(|p| p.ctor()); + ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column_ctors) + } + fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { + self.patterns.iter().copied() + } + + /// Does specialization: given a constructor, this takes the patterns from the column that match + /// the constructor, and outputs their fields. + /// This returns one column per field of the constructor. The normally all have the same length + /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns + /// which may change the lengths. + fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec { + let arity = ctor.arity(pcx); + if arity == 0 { + return Vec::new(); + } + + // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These + // columns may have different lengths in the presence of or-patterns (this is why we can't + // reuse `Matrix`). + let mut specialized_columns: Vec<_> = + (0..arity).map(|_| Self { patterns: Vec::new() }).collect(); + let relevant_patterns = + self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); + for pat in relevant_patterns { + let specialized = pat.specialize(pcx, &ctor); + for (subpat, column) in specialized.iter().zip(&mut specialized_columns) { + if subpat.is_or_pat() { + column.patterns.extend(subpat.iter_fields()) + } else { + column.patterns.push(subpat) + } + } + } + + assert!( + !specialized_columns[0].is_empty(), + "ctor {ctor:?} was listed as present but isn't; + there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`" + ); + specialized_columns + } +} + /// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned -/// in a given column. This traverses patterns column-by-column, where a column is the intuitive -/// notion of "subpatterns that inspect the same subvalue". -/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the -/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete). +/// in a given column. +#[instrument(level = "debug", skip(cx), ret)] fn collect_nonexhaustive_missing_variants<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - column: &[&DeconstructedPat<'p, 'tcx>], + column: &PatternColumn<'p, 'tcx>, ) -> Vec> { - if column.is_empty() { + let Some(ty) = column.head_ty() else { return Vec::new(); - } - let ty = column[0].ty(); + }; let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false }; - let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor())); + let set = column.analyze_ctors(pcx); if set.present.is_empty() { // We can't consistently handle the case where no constructors are present (since this would // require digging deep through any type in case there's a non_exhaustive enum somewhere), @@ -911,35 +980,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( // Recurse into the fields. for ctor in set.present { - let arity = ctor.arity(pcx); - if arity == 0 { - continue; - } - - // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These - // columns may have different lengths in the presence of or-patterns (this is why we can't - // reuse `Matrix`). - let mut specialized_columns: Vec> = (0..arity).map(|_| Vec::new()).collect(); - let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); - for pat in relevant_patterns { - let specialized = pat.specialize(pcx, &ctor); - for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) { - if subpat.is_or_pat() { - sub_column.extend(subpat.iter_fields()) - } else { - sub_column.push(subpat) - } - } - } - debug_assert!( - !specialized_columns[0].is_empty(), - "ctor {ctor:?} was listed as present but isn't" - ); - + let specialized_columns = column.specialize(pcx, &ctor); let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor); for (i, col_i) in specialized_columns.iter().enumerate() { // Compute witnesses for each column. - let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice()); + let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i); // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`, // adding enough wildcards to match `arity`. for wit in wits_for_col_i { @@ -952,6 +997,81 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( witnesses } +/// Traverse the patterns to warn the user about ranges that overlap on their endpoints. +#[instrument(level = "debug", skip(cx, lint_root))] +fn lint_overlapping_range_endpoints<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + column: &PatternColumn<'p, 'tcx>, + lint_root: HirId, +) { + let Some(ty) = column.head_ty() else { + return; + }; + let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false }; + + let set = column.analyze_ctors(pcx); + + if IntRange::is_integral(ty) { + let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { + let overlap_as_pat = overlap.to_pat(cx.tcx, ty); + let overlaps: Vec<_> = overlapped_spans + .iter() + .copied() + .map(|span| Overlap { range: overlap_as_pat.clone(), span }) + .collect(); + cx.tcx.emit_spanned_lint( + lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, + lint_root, + this_span, + OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, + ); + }; + + // If two ranges overlapped, the split set will contain their intersection as a singleton. + let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range()); + for overlap_range in split_int_ranges.clone() { + if overlap_range.is_singleton() { + let overlap: u128 = overlap_range.boundaries().0; + // Spans of ranges that start or end with the overlap. + let mut prefixes: SmallVec<[_; 1]> = Default::default(); + let mut suffixes: SmallVec<[_; 1]> = Default::default(); + // Iterate on patterns that contained `overlap`. + for pat in column.iter() { + let this_span = pat.span(); + let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + if this_range.is_singleton() { + // Don't lint when one of the ranges is a singleton. + continue; + } + let (start, end) = this_range.boundaries(); + if start == overlap { + // `this_range` looks like `overlap..=end`; it overlaps with any ranges that + // look like `start..=overlap`. + if !prefixes.is_empty() { + emit_lint(overlap_range, this_span, &prefixes); + } + suffixes.push(this_span) + } else if end == overlap { + // `this_range` looks like `start..=overlap`; it overlaps with any ranges + // that look like `overlap..=end`. + if !suffixes.is_empty() { + emit_lint(overlap_range, this_span, &suffixes); + } + prefixes.push(this_span) + } + } + } + } + } else { + // Recurse into the fields. + for ctor in set.present { + for col in column.specialize(pcx, &ctor) { + lint_overlapping_range_endpoints(cx, &col, lint_root); + } + } + } +} + /// The arm of a match expression. #[derive(Clone, Copy, Debug)] pub(crate) struct MatchArm<'p, 'tcx> { @@ -1022,6 +1142,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( NoWitnesses { .. } => bug!(), }; + let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::>(); + let pat_column = PatternColumn::new(pat_column); + lint_overlapping_range_endpoints(cx, &pat_column, lint_root); + // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. if cx.refutable @@ -1031,9 +1155,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( rustc_session::lint::Level::Allow ) { - let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::>(); let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column); - if !witnesses.is_empty() { // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` // is not exhaustive enough. diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 8b33e00c63cca..277060573bcb9 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -438,10 +438,8 @@ impl<'tcx> Inliner<'tcx> { return Err("incompatible instruction set"); } - for feature in &callee_attrs.target_features { - if !self.codegen_fn_attrs.target_features.contains(feature) { - return Err("incompatible target feature"); - } + if callee_attrs.target_features != self.codegen_fn_attrs.target_features { + return Err("incompatible target features"); } Ok(()) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9aaa54110bdfe..68b8911824c2e 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -20,6 +20,7 @@ extern crate tracing; #[macro_use] extern crate rustc_middle; +use hir::ConstContext; use required_consts::RequiredConstsVisitor; use rustc_const_eval::util; use rustc_data_structures::fx::FxIndexSet; @@ -251,8 +252,13 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { let const_kind = tcx.hir().body_const_context(def); // No need to const-check a non-const `fn`. - if const_kind.is_none() { - return Default::default(); + match const_kind { + Some(ConstContext::Const { .. } | ConstContext::Static(_)) + | Some(ConstContext::ConstFn) => {} + None => span_bug!( + tcx.def_span(def), + "`mir_const_qualif` should only be called on const fns and const items" + ), } // N.B., this `borrow()` is guaranteed to be valid (i.e., the value @@ -317,7 +323,21 @@ fn mir_promoted( // Ensure that we compute the `mir_const_qualif` for constants at // this point, before we steal the mir-const result. // Also this means promotion can rely on all const checks having been done. - let const_qualifs = tcx.mir_const_qualif(def); + + let const_qualifs = match tcx.def_kind(def) { + DefKind::Fn | DefKind::AssocFn | DefKind::Closure + if tcx.constness(def) == hir::Constness::Const + || tcx.is_const_default_method(def.to_def_id()) => + { + tcx.mir_const_qualif(def) + } + DefKind::AssocConst + | DefKind::Const + | DefKind::Static(_) + | DefKind::InlineConst + | DefKind::AnonConst => tcx.mir_const_qualif(def), + _ => ConstQualifs::default(), + }; let mut body = tcx.mir_const(def).steal(); if let Some(error_reported) = const_qualifs.tainted_by_errors { body.tainted_by_errors = Some(error_reported); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9f8361a4b1ee2..7b5bb319ed8d1 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -504,8 +504,10 @@ impl<'a> Parser<'a> { // Special-case "expected `;`" errors if expected.contains(&TokenType::Token(token::Semi)) { - if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() { - return Ok(true); + // If the user is trying to write a ternary expression, recover it and + // return an Err to prevent a cascade of irrelevant diagnostics + if self.prev_token == token::Question && let Err(e) = self.maybe_recover_from_ternary_operator() { + return Err(e); } if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { @@ -1428,10 +1430,10 @@ impl<'a> Parser<'a> { /// Rust has no ternary operator (`cond ? then : else`). Parse it and try /// to recover from it if `then` and `else` are valid expressions. Returns - /// whether it was a ternary operator. - pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool { + /// an err if this appears to be a ternary expression. + pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> { if self.prev_token != token::Question { - return false; + return PResult::Ok(()); } let lo = self.prev_token.span.lo(); @@ -1449,20 +1451,18 @@ impl<'a> Parser<'a> { if self.eat_noexpect(&token::Colon) { match self.parse_expr() { Ok(_) => { - self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) }); - return true; + return Err(self + .sess + .create_err(TernaryOperator { span: self.token.span.with_lo(lo) })); } Err(err) => { err.cancel(); - self.restore_snapshot(snapshot); } }; } - } else { - self.restore_snapshot(snapshot); - }; - - false + } + self.restore_snapshot(snapshot); + Ok(()) } pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 597303cae73e2..41b19ecb63a44 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -159,8 +159,9 @@ pub struct Parser<'a> { /// appropriately. /// /// See the comments in the `parse_path_segment` function for more details. - unmatched_angle_bracket_count: u32, - max_angle_bracket_count: u32, + unmatched_angle_bracket_count: u16, + max_angle_bracket_count: u16, + angle_bracket_nesting: u16, last_unexpected_token_span: Option, /// If present, this `Parser` is not parsing Rust code but rather a macro call. @@ -394,6 +395,7 @@ impl<'a> Parser<'a> { break_last_token: false, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, + angle_bracket_nesting: 0, last_unexpected_token_span: None, subparser_name, capture_state: CaptureState { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2fcb9a78cfd4b..4969e672a72d5 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -487,10 +487,24 @@ impl<'a> Parser<'a> { // Take a snapshot before attempting to parse - we can restore this later. let snapshot = is_first_invocation.then(|| self.clone()); + self.angle_bracket_nesting += 1; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); match self.parse_angle_args(ty_generics) { - Ok(args) => Ok(args), + Ok(args) => { + self.angle_bracket_nesting -= 1; + Ok(args) + } + Err(mut e) if self.angle_bracket_nesting > 10 => { + self.angle_bracket_nesting -= 1; + // When encountering severely malformed code where there are several levels of + // nested unclosed angle args (`f:: 0 => { + self.angle_bracket_nesting -= 1; + // Swap `self` with our backup of the parser state before attempting to parse // generic arguments. let snapshot = mem::replace(self, snapshot.unwrap()); @@ -520,8 +534,8 @@ impl<'a> Parser<'a> { // Make a span over ${unmatched angle bracket count} characters. // This is safe because `all_angle_brackets` ensures that there are only `<`s, // i.e. no multibyte characters, in this range. - let span = - lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count)); + let span = lo + .with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count.into())); self.sess.emit_err(errors::UnmatchedAngle { span, plural: snapshot.unmatched_angle_bracket_count > 1, @@ -531,7 +545,10 @@ impl<'a> Parser<'a> { self.parse_angle_args(ty_generics) } } - Err(e) => Err(e), + Err(e) => { + self.angle_bracket_nesting -= 1; + Err(e) + } } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 25ef5245cf169..026186cbe6cd3 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -393,6 +393,9 @@ passes_invalid_attr_at_crate_level = `{$name}` attribute cannot be used at crate level .suggestion = perhaps you meant to use an outer attribute +passes_invalid_attr_at_crate_level_item = + the inner attribute doesn't annotate this {$kind} + passes_invalid_deprecation_version = invalid deprecation version found .label = invalid deprecation version @@ -402,11 +405,6 @@ passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export] passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments -passes_invalid_stability = - invalid stability version found - .label = invalid stability version - .item = the stability attribute annotates this item - passes_lang_item_fn_with_target_feature = `{$name}` language item function is not allowed to have `#[target_feature]` .label = `{$name}` language item function is not allowed to have `#[target_feature]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dfe75ea5e8e13..a8a27e761cb3f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2534,10 +2534,30 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if attr.style == AttrStyle::Inner { for attr_to_check in ATTRS_TO_CHECK { if attr.has_name(*attr_to_check) { + let item = tcx + .hir() + .items() + .map(|id| tcx.hir().item(id)) + .find(|item| !item.span.is_dummy()) // Skip prelude `use`s + .map(|item| errors::ItemFollowingInnerAttr { + span: item.ident.span, + kind: item.kind.descr(), + }); tcx.sess.emit_err(errors::InvalidAttrAtCrateLevel { span: attr.span, - snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(), + sugg_span: tcx + .sess + .source_map() + .span_to_snippet(attr.span) + .ok() + .filter(|src| src.starts_with("#![")) + .map(|_| { + attr.span + .with_lo(attr.span.lo() + BytePos(1)) + .with_hi(attr.span.lo() + BytePos(2)) + }), name: *attr_to_check, + item, }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6f87b56c636d3..eca0fb7748b15 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -856,8 +856,15 @@ pub struct UnknownLangItem { pub struct InvalidAttrAtCrateLevel { pub span: Span, - pub snippet: Option, + pub sugg_span: Option, pub name: Symbol, + pub item: Option, +} + +#[derive(Clone, Copy)] +pub struct ItemFollowingInnerAttr { + pub span: Span, + pub kind: &'static str, } impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel { @@ -871,15 +878,18 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel { diag.set_arg("name", self.name); // Only emit an error with a suggestion if we can create a string out // of the attribute span - if let Some(src) = self.snippet { - let replacement = src.replace("#!", "#"); + if let Some(span) = self.sugg_span { diag.span_suggestion_verbose( - self.span, + span, fluent::passes_suggestion, - replacement, + String::new(), rustc_errors::Applicability::MachineApplicable, ); } + if let Some(item) = self.item { + diag.set_arg("kind", item.kind); + diag.span_label(item.span, fluent::passes_invalid_attr_at_crate_level_item); + } diag } } @@ -1504,16 +1514,6 @@ pub struct UselessStability { pub item_sp: Span, } -#[derive(Diagnostic)] -#[diag(passes_invalid_stability)] -pub struct InvalidStability { - #[primary_span] - #[label] - pub span: Span, - #[label(passes_item)] - pub item_sp: Span, -} - #[derive(Diagnostic)] #[diag(passes_cannot_stabilize_deprecated)] pub struct CannotStabilizeDeprecated { diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index ceb8f58cac0c2..0daa273db6761 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -5,7 +5,7 @@ //! collect them instead. use rustc_ast::Attribute; -use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER}; +use rustc_attr::VERSION_PLACEHOLDER; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::LibFeatures; @@ -59,7 +59,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER { - since = Some(rust_version_symbol()); + since = Some(sym::env_CFG_RELEASE); } if let Some(feature) = feature { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index bb23dc257d732..7bfb0742b8be9 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -3,8 +3,8 @@ use crate::errors; use rustc_attr::{ - self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable, - UnstableReason, VERSION_PLACEHOLDER, + self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason, + VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; @@ -226,37 +226,43 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { - // Explicit version of iter::order::lt to handle parse errors properly - for (dep_v, stab_v) in - iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.')) - { - match stab_v.parse::() { - Err(_) => { - self.tcx.sess.emit_err(errors::InvalidStability { span, item_sp }); - break; - } - Ok(stab_vp) => match dep_v.parse::() { - Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { - Ordering::Less => { - self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { - span, - item_sp, - }); + match stab_since { + Since::Current => { + self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); + } + Since::Version(stab_since) => { + // Explicit version of iter::order::lt to handle parse errors properly + for (dep_v, stab_v) in iter::zip( + dep_since.as_str().split('.'), + [stab_since.major, stab_since.minor, stab_since.patch], + ) { + match dep_v.parse::() { + Ok(dep_vp) => match dep_vp.cmp(&u64::from(stab_v)) { + Ordering::Less => { + self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { + span, + item_sp, + }); + break; + } + Ordering::Equal => continue, + Ordering::Greater => break, + }, + Err(_) => { + if dep_v != "TBD" { + self.tcx.sess.emit_err(errors::InvalidDeprecationVersion { + span, + item_sp, + }); + } break; } - Ordering::Equal => continue, - Ordering::Greater => break, - }, - Err(_) => { - if dep_v != "TBD" { - self.tcx.sess.emit_err(errors::InvalidDeprecationVersion { - span, - item_sp, - }); - } - break; } - }, + } + } + Since::Err => { + // An error already reported. Assume the unparseable stabilization + // version is older than the deprecation version. } } } @@ -1109,7 +1115,7 @@ fn unnecessary_stable_feature_lint( mut since: Symbol, ) { if since.as_str() == VERSION_PLACEHOLDER { - since = rust_version_symbol(); + since = sym::env_CFG_RELEASE; } tcx.emit_spanned_lint( lint::builtin::STABLE_FEATURES, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index f2d6a0dff9c38..4bb7e65747f70 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -210,22 +210,7 @@ where } } } - ty::Alias(ty::Weak, alias) => { - self.def_id_visitor.visit_def_id(alias.def_id, "type alias", &ty); - } - ty::Alias(ty::Projection, proj) => { - if V::SKIP_ASSOC_TYS { - // Visitors searching for minimal visibility/reachability want to - // conservatively approximate associated types like `::Alias` - // as visible/reachable even if both `Type` and `Trait` are private. - // Ideally, associated types should be substituted in the same way as - // free type aliases, but this isn't done yet. - return ControlFlow::Continue(()); - } - // This will also visit args if necessary, so we don't need to recurse. - return self.visit_projection_ty(proj); - } - ty::Alias(ty::Inherent, data) => { + ty::Alias(kind @ (ty::Inherent | ty::Weak | ty::Projection), data) => { if V::SKIP_ASSOC_TYS { // Visitors searching for minimal visibility/reachability want to // conservatively approximate associated types like `Type::Alias` @@ -235,9 +220,14 @@ where return ControlFlow::Continue(()); } + let kind = match kind { + ty::Inherent | ty::Projection => "associated type", + ty::Weak => "type alias", + ty::Opaque => unreachable!(), + }; self.def_id_visitor.visit_def_id( data.def_id, - "associated type", + kind, &LazyDefPathStr { def_id: data.def_id, tcx }, )?; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4516708ce17c9..536c0a20e2a22 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -197,6 +197,9 @@ macro_rules! handle_cycle_error { ([(fatal_cycle) $($rest:tt)*]) => {{ rustc_query_system::HandleCycleError::Fatal }}; + ([(cycle_stash) $($rest:tt)*]) => {{ + rustc_query_system::HandleCycleError::Stash + }}; ([(cycle_delay_bug) $($rest:tt)*]) => {{ rustc_query_system::HandleCycleError::DelayBug }}; diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index e49e78cc7c410..5829e17ec1625 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -15,6 +15,7 @@ pub enum HandleCycleError { Error, Fatal, DelayBug, + Stash, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 69a9c0eb95a98..1f3403d09be63 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -19,7 +19,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lock; #[cfg(parallel_compiler)] use rustc_data_structures::{outline, sync}; -use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError, StashKey}; use rustc_span::{Span, DUMMY_SP}; use std::cell::Cell; use std::collections::hash_map::Entry; @@ -133,6 +133,17 @@ where let guar = error.delay_as_bug(); query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar) } + Stash => { + let guar = if let Some(root) = cycle_error.cycle.first() + && let Some(span) = root.query.span + { + error.stash(span, StashKey::Cycle); + qcx.dep_context().sess().delay_span_bug(span, "delayed cycle error") + } else { + error.emit() + }; + query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar) + } } } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 352bf98d01fb2..3b1f957c89099 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -335,7 +335,7 @@ impl Resolver<'_, '_> { for unused in visitor.unused_imports.values() { let mut fixes = Vec::new(); - let mut spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) { + let spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) { UnusedSpanResult::Used => continue, UnusedSpanResult::FlatUnused(span, remove) => { fixes.push((remove, String::new())); @@ -353,20 +353,19 @@ impl Resolver<'_, '_> { } }; - let len = spans.len(); - spans.sort(); - let ms = MultiSpan::from_spans(spans.clone()); - let mut span_snippets = spans + let ms = MultiSpan::from_spans(spans); + + let mut span_snippets = ms + .primary_spans() .iter() - .filter_map(|s| match tcx.sess.source_map().span_to_snippet(*s) { - Ok(s) => Some(format!("`{s}`")), - _ => None, - }) + .filter_map(|span| tcx.sess.source_map().span_to_snippet(*span).ok()) + .map(|s| format!("`{s}`")) .collect::>(); span_snippets.sort(); + let msg = format!( "unused import{}{}", - pluralize!(len), + pluralize!(ms.primary_spans().len()), if !span_snippets.is_empty() { format!(": {}", span_snippets.join(", ")) } else { @@ -376,7 +375,7 @@ impl Resolver<'_, '_> { let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span { "remove the whole `use` item" - } else if spans.len() > 1 { + } else if ms.primary_spans().len() > 1 { "remove the unused imports" } else { "remove the unused import" diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4ad838e5aed20..edcc22d56c682 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -25,7 +25,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span, SyntaxContext}; -use thin_vec::ThinVec; +use thin_vec::{thin_vec, ThinVec}; use crate::errors::{ AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, @@ -1147,7 +1147,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { namespace: Namespace, parent_scope: &ParentScope<'a>, start_module: Module<'a>, - crate_name: Ident, + crate_path: ThinVec, filter_fn: FilterFn, ) -> Vec where @@ -1163,8 +1163,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(x) => Some(x), } { let in_module_is_extern = !in_module.def_id().is_local(); - // We have to visit module children in deterministic order to avoid - // instabilities in reported imports (#43552). in_module.for_each_child(self, |this, ident, ns, name_binding| { // avoid non-importable candidates if !name_binding.is_importable() { @@ -1214,12 +1212,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let res = name_binding.res(); if filter_fn(res) { // create the path - let mut segms = path_segments.clone(); - if lookup_ident.span.at_least_rust_2018() { + let mut segms = if lookup_ident.span.at_least_rust_2018() { // crate-local absolute paths start with `crate::` in edition 2018 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) - segms.insert(0, ast::PathSegment::from_ident(crate_name)); - } + crate_path.clone() + } else { + ThinVec::new() + }; + segms.append(&mut path_segments.clone()); segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms, tokens: None }; @@ -1318,18 +1318,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { where FilterFn: Fn(Res) -> bool, { + let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; let mut suggestions = self.lookup_import_candidates_from_module( lookup_ident, namespace, parent_scope, self.graph_root, - Ident::with_dummy_span(kw::Crate), + crate_path, &filter_fn, ); if lookup_ident.span.at_least_rust_2018() { - let extern_prelude_names = self.extern_prelude.clone(); - for (ident, _) in extern_prelude_names.into_iter() { + for ident in self.extern_prelude.clone().into_keys() { if ident.span.from_expansion() { // Idents are adjusted to the root context before being // resolved in the extern prelude, so reporting this to the @@ -1340,13 +1340,43 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)); if let Some(crate_id) = crate_id { - let crate_root = self.expect_module(crate_id.as_def_id()); + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == ident + && let Some(binding) = name_resolution.borrow().binding + { + match binding.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); + } + crate_path.push(ast::PathSegment::from_ident(ident)); + suggestions.extend(self.lookup_import_candidates_from_module( lookup_ident, namespace, parent_scope, crate_root, - ident, + crate_path, &filter_fn, )); } @@ -2554,7 +2584,7 @@ fn show_candidates( candidates.iter().for_each(|c| { (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings }) - .push((path_names_to_string(&c.path), c.descr, c.did, &c.note, c.via_import)) + .push((pprust::path_to_string(&c.path), c.descr, c.did, &c.note, c.via_import)) }); // we want consistent results across executions, but candidates are produced diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a5f8f61f3db50..fd5d6fabf021d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1570,7 +1570,26 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { err.set_primary_message( "cannot initialize a tuple struct which contains private fields", ); - + if !def_id.is_local() + && self + .r + .tcx + .inherent_impls(def_id) + .iter() + .flat_map(|impl_def_id| { + self.r.tcx.provided_trait_methods(*impl_def_id) + }) + .any(|assoc| !assoc.fn_has_self_parameter && assoc.name == sym::new) + { + // FIXME: look for associated functions with Self return type, + // instead of relying only on the name and lack of self receiver. + err.span_suggestion_verbose( + span.shrink_to_hi(), + "you might have meant to use the `new` associated function", + "::new".to_string(), + Applicability::MaybeIncorrect, + ); + } // Use spans of the tuple struct definition. self.r.field_def_ids(def_id).map(|field_ids| { field_ids diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 84933588f17fe..7aced414ed67a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2739,32 +2739,25 @@ pub fn build_session_options( _ => {} } - // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes - // precedence. - match (cg.instrument_coverage, unstable_opts.instrument_coverage) { - (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { - handler.early_error( - "incompatible values passed for `-C instrument-coverage` \ - and `-Z instrument-coverage`", - ); - } - (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} - (Some(_), _) if !unstable_opts.unstable_options => { - handler.early_error( - "`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \ - require `-Z unstable-options`", - ); - } - (None, None) => {} - (None, ic) => { - handler - .early_warn("`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`"); - cg.instrument_coverage = ic; + // Check for unstable values of `-C instrument-coverage`. + // This is what prevents them from being used on stable compilers. + match cg.instrument_coverage { + // Stable values: + InstrumentCoverage::All | InstrumentCoverage::Off => {} + // Unstable values: + InstrumentCoverage::Branch + | InstrumentCoverage::ExceptUnusedFunctions + | InstrumentCoverage::ExceptUnusedGenerics => { + if !unstable_opts.unstable_options { + handler.early_error( + "`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \ + require `-Z unstable-options`", + ); + } } - _ => {} } - if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { + if cg.instrument_coverage != InstrumentCoverage::Off { if cg.profile_generate.enabled() || cg.profile_use.is_some() { handler.early_error( "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 77aaf951f0db0..fd473acbd3c31 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -294,7 +294,7 @@ impl CodegenOptions { // JUSTIFICATION: defn of the suggested wrapper fn #[allow(rustc::bad_opt_access)] pub fn instrument_coverage(&self) -> InstrumentCoverage { - self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + self.instrument_coverage } } @@ -913,23 +913,23 @@ mod parse { } pub(crate) fn parse_instrument_coverage( - slot: &mut Option, + slot: &mut InstrumentCoverage, v: Option<&str>, ) -> bool { if v.is_some() { - let mut bool_arg = None; - if parse_opt_bool(&mut bool_arg, v) { - *slot = bool_arg.unwrap().then_some(InstrumentCoverage::All); + let mut bool_arg = false; + if parse_bool(&mut bool_arg, v) { + *slot = if bool_arg { InstrumentCoverage::All } else { InstrumentCoverage::Off }; return true; } } let Some(v) = v else { - *slot = Some(InstrumentCoverage::All); + *slot = InstrumentCoverage::All; return true; }; - *slot = Some(match v { + *slot = match v { "all" => InstrumentCoverage::All, "branch" => InstrumentCoverage::Branch, "except-unused-generics" | "except_unused_generics" => { @@ -940,7 +940,7 @@ mod parse { } "off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off, _ => return false, - }); + }; true } @@ -1352,7 +1352,7 @@ options! { inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] - instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], + instrument_coverage: InstrumentCoverage = (InstrumentCoverage::Off, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ implies `-C symbol-mangling-version=v0`. Optional values are: @@ -1593,16 +1593,6 @@ options! { "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] - instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], - "instrument the generated code to support LLVM source-based code coverage \ - reports (note, the compiler build config must include `profiler = true`); \ - implies `-C symbol-mangling-version=v0`. Optional values are: - `=all` (implicit value) - `=branch` - `=except-unused-generics` - `=except-unused-functions` - `=off` (default)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), instrument_xray: Option = (None, parse_instrument_xray, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 94fe38b72ad16..f823b55615402 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -45,7 +45,7 @@ use std::fmt; use std::ops::{Div, Mul}; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{atomic::AtomicBool, Arc}; use std::time::Duration; pub struct OptimizationFuel { @@ -202,6 +202,12 @@ pub struct Session { /// The version of the rustc process, possibly including a commit hash and description. pub cfg_version: &'static str, + /// The inner atomic value is set to true when a feature marked as `internal` is + /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with + /// internal features are wontfix, and they are usually the cause of the ICEs. + /// None signifies that this is not tracked. + pub using_internal_features: Arc, + /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows /// This is mainly useful for other tools that reads that debuginfo to figure out @@ -1389,6 +1395,7 @@ pub fn build_session( target_override: Option, cfg_version: &'static str, ice_file: Option, + using_internal_features: Arc, expanded_args: Vec, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override @@ -1525,6 +1532,7 @@ pub fn build_session( target_features: Default::default(), unstable_target_features: Default::default(), cfg_version, + using_internal_features, expanded_args, }; diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index c24b9efe86545..dcf6b90407748 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -13,6 +13,7 @@ #![cfg_attr(not(bootstrap), doc(rust_logo))] #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![cfg_attr(not(bootstrap), allow(internal_features))] +#![allow(rustc::usage_of_ty_tykind)] pub mod rustc_internal; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index f42a973932065..78144524ac508 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -4,7 +4,7 @@ //! due to incomplete stable coverage. // Prefer importing stable_mir over internal rustc constructs to make this file more readable. -use crate::rustc_smir::{MaybeStable, Tables}; +use crate::rustc_smir::Tables; use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy}; use stable_mir::ty::{Const, GenericArgKind, GenericArgs, Region, Ty}; use stable_mir::DefId; @@ -31,7 +31,7 @@ impl<'tcx> RustcInternal<'tcx> for GenericArgKind { match self { GenericArgKind::Lifetime(reg) => reg.internal(tables).into(), GenericArgKind::Type(ty) => ty.internal(tables).into(), - GenericArgKind::Const(cnst) => cnst.internal(tables).into(), + GenericArgKind::Const(cnst) => ty_const(cnst, tables).into(), } } } @@ -46,16 +46,22 @@ impl<'tcx> RustcInternal<'tcx> for Region { impl<'tcx> RustcInternal<'tcx> for Ty { type T = InternalTy<'tcx>; fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { - match tables.types[self.0] { - MaybeStable::Stable(_) => todo!(), - MaybeStable::Rustc(ty) => ty, + tables.types[*self] + } +} + +fn ty_const<'tcx>(constant: &Const, tables: &mut Tables<'tcx>) -> rustc_ty::Const<'tcx> { + match constant.internal(tables) { + rustc_middle::mir::Const::Ty(c) => c, + cnst => { + panic!("Trying to covert constant `{constant:?}` to type constant, but found {cnst:?}") } } } impl<'tcx> RustcInternal<'tcx> for Const { - type T = rustc_ty::Const<'tcx>; - fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { - todo!() + type T = rustc_middle::mir::Const<'tcx>; + fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.constants[self.id] } } diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index d3ea8cdc699a0..4d2a518226d89 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -167,8 +167,9 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { def_ids: IndexMap::default(), alloc_ids: IndexMap::default(), spans: IndexMap::default(), - types: vec![], + types: IndexMap::default(), instances: IndexMap::default(), + constants: IndexMap::default(), })); stable_mir::run(&tables, || init(&tables, f)); } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index d5a5f800a40c0..eb8689130174d 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -20,7 +20,8 @@ use rustc_target::abi::FieldIdx; use stable_mir::mir::mono::InstanceDef; use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx}; use stable_mir::ty::{ - FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, TyKind, UintTy, + Const, ConstId, ConstantKind, FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, + Span, TyKind, UintTy, }; use stable_mir::{self, opaque, Context, Filename}; use std::cell::RefCell; @@ -147,14 +148,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { let mut tables = self.0.borrow_mut(); - tables.types[ty.0].clone().stable(&mut *tables) - } - - fn mk_ty(&self, kind: TyKind) -> stable_mir::ty::Ty { - let mut tables = self.0.borrow_mut(); - let n = tables.types.len(); - tables.types.push(MaybeStable::Stable(kind)); - stable_mir::ty::Ty(n) + tables.types[ty].kind().stable(&mut *tables) } fn generics_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::Generics { @@ -213,8 +207,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn instance_ty(&self, def: InstanceDef) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; - let ty = instance.ty(tables.tcx, ParamEnv::empty()); - tables.intern_ty(ty) + instance.ty(tables.tcx, ParamEnv::empty()).stable(&mut *tables) } fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId { @@ -252,33 +245,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } -#[derive(Clone)] -pub enum MaybeStable { - Stable(S), - Rustc(R), -} - -impl<'tcx, S, R> MaybeStable { - fn stable(self, tables: &mut Tables<'tcx>) -> S - where - R: Stable<'tcx, T = S>, - { - match self { - MaybeStable::Stable(s) => s, - MaybeStable::Rustc(r) => r.stable(tables), - } - } -} - -impl PartialEq for MaybeStable { - fn eq(&self, other: &R) -> bool { - match self { - MaybeStable::Stable(_) => false, - MaybeStable::Rustc(r) => r == other, - } - } -} - pub(crate) struct TablesWrapper<'tcx>(pub(crate) RefCell>); pub struct Tables<'tcx> { @@ -286,18 +252,18 @@ pub struct Tables<'tcx> { pub(crate) def_ids: IndexMap, pub(crate) alloc_ids: IndexMap, pub(crate) spans: IndexMap, - pub(crate) types: Vec>>, + pub(crate) types: IndexMap, stable_mir::ty::Ty>, pub(crate) instances: IndexMap, InstanceDef>, + pub(crate) constants: IndexMap, ConstId>, } impl<'tcx> Tables<'tcx> { fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty { - if let Some(id) = self.types.iter().position(|t| *t == ty) { - return stable_mir::ty::Ty(id); - } - let id = self.types.len(); - self.types.push(MaybeStable::Rustc(ty)); - stable_mir::ty::Ty(id) + self.types.create_or_fetch(ty) + } + + fn intern_const(&mut self, constant: mir::Const<'tcx>) -> ConstId { + self.constants.create_or_fetch(constant) } } @@ -321,9 +287,8 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { type T = stable_mir::mir::Body; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - stable_mir::mir::Body { - blocks: self - .basic_blocks + stable_mir::mir::Body::new( + self.basic_blocks .iter() .map(|block| stable_mir::mir::BasicBlock { terminator: block.terminator().stable(tables), @@ -334,15 +299,15 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { .collect(), }) .collect(), - locals: self - .local_decls + self.local_decls .iter() .map(|decl| stable_mir::mir::LocalDecl { - ty: tables.intern_ty(decl.ty), + ty: decl.ty.stable(tables), span: decl.source_info.span.stable(tables), }) .collect(), - } + self.arg_count, + ) } } @@ -436,7 +401,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { Cast(cast_kind, op, ty) => stable_mir::mir::Rvalue::Cast( cast_kind.stable(tables), op.stable(tables), - tables.intern_ty(*ty), + ty.stable(tables), ), BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp( bin_op.stable(tables), @@ -449,7 +414,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { ops.1.stable(tables), ), NullaryOp(null_op, ty) => { - stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), tables.intern_ty(*ty)) + stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), ty.stable(tables)) } UnaryOp(un_op, op) => { stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables)) @@ -460,7 +425,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { stable_mir::mir::Rvalue::Aggregate(agg_kind.stable(tables), operands) } ShallowInitBox(op, ty) => { - stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), tables.intern_ty(*ty)) + stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables)) } CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)), } @@ -604,7 +569,7 @@ impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use stable_mir::ty::TermKind; match self { - ty::TermKind::Ty(ty) => TermKind::Type(tables.intern_ty(*ty)), + ty::TermKind::Ty(ty) => TermKind::Type(ty.stable(tables)), ty::TermKind::Const(cnst) => { let cnst = cnst.stable(tables); TermKind::Const(cnst) @@ -885,7 +850,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { match self { mir::AggregateKind::Array(ty) => { - stable_mir::mir::AggregateKind::Array(tables.intern_ty(*ty)) + stable_mir::mir::AggregateKind::Array(ty.stable(tables)) } mir::AggregateKind::Tuple => stable_mir::mir::AggregateKind::Tuple, mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => { @@ -917,13 +882,13 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind { type T = stable_mir::mir::CoroutineKind; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - use rustc_hir::{AsyncCoroutineKind, CoroutineKind}; + use rustc_hir::{CoroutineKind, CoroutineSource}; match self { CoroutineKind::Async(async_gen) => { let async_gen = match async_gen { - AsyncCoroutineKind::Block => stable_mir::mir::AsyncCoroutineKind::Block, - AsyncCoroutineKind::Closure => stable_mir::mir::AsyncCoroutineKind::Closure, - AsyncCoroutineKind::Fn => stable_mir::mir::AsyncCoroutineKind::Fn, + CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block, + CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure, + CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn, }; stable_mir::mir::CoroutineKind::Async(async_gen) } @@ -1053,7 +1018,7 @@ impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> { use stable_mir::ty::GenericArgKind; match self { ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(region.stable(tables)), - ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(*ty)), + ty::GenericArgKind::Type(ty) => GenericArgKind::Type(ty.stable(tables)), ty::GenericArgKind::Const(cnst) => GenericArgKind::Const(cnst.stable(tables)), } } @@ -1099,11 +1064,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { use stable_mir::ty::{Abi, FnSig}; FnSig { - inputs_and_output: self - .inputs_and_output - .iter() - .map(|ty| tables.intern_ty(ty)) - .collect(), + inputs_and_output: self.inputs_and_output.iter().map(|ty| ty.stable(tables)).collect(), c_variadic: self.c_variadic, unsafety: self.unsafety.stable(tables), abi: match self.abi { @@ -1241,9 +1202,16 @@ impl<'tcx> Stable<'tcx> for hir::Movability { } impl<'tcx> Stable<'tcx> for Ty<'tcx> { + type T = stable_mir::ty::Ty; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + tables.intern_ty(*self) + } +} + +impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { type T = stable_mir::ty::TyKind; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - match self.kind() { + match self { ty::Bool => TyKind::RigidTy(RigidTy::Bool), ty::Char => TyKind::RigidTy(RigidTy::Char), ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables))), @@ -1256,15 +1224,15 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { ty::Foreign(def_id) => TyKind::RigidTy(RigidTy::Foreign(tables.foreign_def(*def_id))), ty::Str => TyKind::RigidTy(RigidTy::Str), ty::Array(ty, constant) => { - TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), constant.stable(tables))) + TyKind::RigidTy(RigidTy::Array(ty.stable(tables), constant.stable(tables))) } - ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(tables.intern_ty(*ty))), + ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - TyKind::RigidTy(RigidTy::RawPtr(tables.intern_ty(*ty), mutbl.stable(tables))) + TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables))) } ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( region.stable(tables), - tables.intern_ty(*ty), + ty.stable(tables), mutbl.stable(tables), )), ty::FnDef(def_id, generic_args) => { @@ -1291,9 +1259,9 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { movability.stable(tables), )), ty::Never => TyKind::RigidTy(RigidTy::Never), - ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( - fields.iter().map(|ty| tables.intern_ty(ty)).collect(), - )), + ty::Tuple(fields) => { + TyKind::RigidTy(RigidTy::Tuple(fields.iter().map(|ty| ty.stable(tables)).collect())) + } ty::Alias(alias_kind, alias_ty) => { TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables)) } @@ -1312,32 +1280,36 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { type T = stable_mir::ty::Const; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - stable_mir::ty::Const { - literal: match self.kind() { - ty::Value(val) => { - let const_val = tables.tcx.valtree_to_const_val((self.ty(), val)); + let kind = match self.kind() { + ty::Value(val) => { + let const_val = tables.tcx.valtree_to_const_val((self.ty(), val)); + if matches!(const_val, mir::ConstValue::ZeroSized) { + ConstantKind::ZeroSized + } else { stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( self.ty(), const_val, tables, )) } - ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), - ty::ErrorCt(_) => unreachable!(), - ty::InferCt(_) => unreachable!(), - ty::BoundCt(_, _) => unimplemented!(), - ty::PlaceholderCt(_) => unimplemented!(), - ty::Unevaluated(uv) => { - stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { - def: tables.const_def(uv.def), - args: uv.args.stable(tables), - promoted: None, - }) - } - ty::ExprCt(_) => unimplemented!(), - }, - ty: tables.intern_ty(self.ty()), - } + } + ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), + ty::ErrorCt(_) => unreachable!(), + ty::InferCt(_) => unreachable!(), + ty::BoundCt(_, _) => unimplemented!(), + ty::PlaceholderCt(_) => unimplemented!(), + ty::Unevaluated(uv) => { + stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { + def: tables.const_def(uv.def), + args: uv.args.stable(tables), + promoted: None, + }) + } + ty::ExprCt(_) => unimplemented!(), + }; + let ty = self.ty().stable(tables); + let id = tables.intern_const(mir::Const::Ty(*self)); + Const::new(kind, ty, id) } } @@ -1422,22 +1394,28 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { match *self { mir::Const::Ty(c) => c.stable(tables), - mir::Const::Unevaluated(unev_const, ty) => stable_mir::ty::Const { - literal: stable_mir::ty::ConstantKind::Unevaluated( - stable_mir::ty::UnevaluatedConst { + mir::Const::Unevaluated(unev_const, ty) => { + let kind = + stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { def: tables.const_def(unev_const.def), args: unev_const.args.stable(tables), promoted: unev_const.promoted.map(|u| u.as_u32()), - }, - ), - ty: tables.intern_ty(ty), - }, - mir::Const::Val(val, ty) => stable_mir::ty::Const { - literal: stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( - ty, val, tables, - )), - ty: tables.intern_ty(ty), - }, + }); + let ty = ty.stable(tables); + let id = tables.intern_const(*self); + Const::new(kind, ty, id) + } + mir::Const::Val(val, ty) if matches!(val, mir::ConstValue::ZeroSized) => { + let ty = ty.stable(tables); + let id = tables.intern_const(*self); + Const::new(ConstantKind::ZeroSized, ty, id) + } + mir::Const::Val(val, ty) => { + let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables)); + let ty = ty.stable(tables); + let id = tables.intern_const(*self); + Const::new(kind, ty, id) + } } } } @@ -1551,30 +1529,32 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { type T = stable_mir::ty::ClauseKind; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - use ty::ClauseKind::*; + use ty::ClauseKind; match *self { - Trait(trait_object) => stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables)), - RegionOutlives(region_outlives) => { + ClauseKind::Trait(trait_object) => { + stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables)) + } + ClauseKind::RegionOutlives(region_outlives) => { stable_mir::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables)) } - TypeOutlives(type_outlives) => { + ClauseKind::TypeOutlives(type_outlives) => { let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives; stable_mir::ty::ClauseKind::TypeOutlives(stable_mir::ty::OutlivesPredicate( - tables.intern_ty(a), + a.stable(tables), b.stable(tables), )) } - Projection(projection_predicate) => { + ClauseKind::Projection(projection_predicate) => { stable_mir::ty::ClauseKind::Projection(projection_predicate.stable(tables)) } - ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType( + ClauseKind::ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType( const_.stable(tables), - tables.intern_ty(ty), + ty.stable(tables), ), - WellFormed(generic_arg) => { + ClauseKind::WellFormed(generic_arg) => { stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables)) } - ConstEvaluatable(const_) => { + ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) } } @@ -1599,7 +1579,7 @@ impl<'tcx> Stable<'tcx> for ty::SubtypePredicate<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { let ty::SubtypePredicate { a, b, a_is_expected: _ } = self; - stable_mir::ty::SubtypePredicate { a: tables.intern_ty(*a), b: tables.intern_ty(*b) } + stable_mir::ty::SubtypePredicate { a: a.stable(tables), b: b.stable(tables) } } } @@ -1608,7 +1588,7 @@ impl<'tcx> Stable<'tcx> for ty::CoercePredicate<'tcx> { fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { let ty::CoercePredicate { a, b } = self; - stable_mir::ty::CoercePredicate { a: tables.intern_ty(*a), b: tables.intern_ty(*b) } + stable_mir::ty::CoercePredicate { a: a.stable(tables), b: b.stable(tables) } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d3205f8eca12a..88d9dab2ba560 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -713,6 +713,7 @@ symbols! { encode, end, env, + env_CFG_RELEASE: env!("CFG_RELEASE"), eprint_macro, eprintln_macro, eq, @@ -910,6 +911,7 @@ symbols! { iter, iter_mut, iter_repeat, + iterator, iterator_collect_fn, kcfi, keyword, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 052ef8bb94c3e..d53bc5b6a8e04 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -15,9 +15,7 @@ twox-hash = "1.6.3" rustc_span = { path = "../rustc_span" } rustc_middle = { path = "../rustc_middle" } rustc_hir = { path = "../rustc_hir" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_target = { path = "../rustc_target" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_session = { path = "../rustc_session" } -rustc_macros = { path = "../rustc_macros" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_symbol_mangling/messages.ftl b/compiler/rustc_symbol_mangling/messages.ftl deleted file mode 100644 index b7d48280f4619..0000000000000 --- a/compiler/rustc_symbol_mangling/messages.ftl +++ /dev/null @@ -1 +0,0 @@ -symbol_mangling_test_output = {$kind}({$content}) diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs index f4d0751f75370..2e081e555313c 100644 --- a/compiler/rustc_symbol_mangling/src/errors.rs +++ b/compiler/rustc_symbol_mangling/src/errors.rs @@ -1,18 +1,32 @@ //! Errors emitted by symbol_mangling. -use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; -use rustc_macros::Diagnostic; +use rustc_errors::{ErrorGuaranteed, IntoDiagnostic}; use rustc_span::Span; +use std::fmt; -#[derive(Diagnostic)] -#[diag(symbol_mangling_test_output)] pub struct TestOutput { - #[primary_span] pub span: Span, pub kind: Kind, pub content: String, } +// This diagnostic doesn't need translation because (a) it doesn't contain any +// natural language, and (b) it's only used in tests. So we construct it +// manually and avoid the fluent machinery. +impl IntoDiagnostic<'_> for TestOutput { + fn into_diagnostic( + self, + handler: &'_ rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let TestOutput { span, kind, content } = self; + + #[allow(rustc::untranslatable_diagnostic)] + let mut diag = handler.struct_err(format!("{kind}({content})")); + diag.set_span(span); + diag + } +} + pub enum Kind { SymbolName, Demangling, @@ -20,15 +34,13 @@ pub enum Kind { DefPath, } -impl IntoDiagnosticArg for Kind { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - let kind = match self { - Kind::SymbolName => "symbol-name", - Kind::Demangling => "demangling", - Kind::DemanglingAlt => "demangling-alt", - Kind::DefPath => "def-path", +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::SymbolName => write!(f, "symbol-name"), + Kind::Demangling => write!(f, "demangling"), + Kind::DemanglingAlt => write!(f, "demangling-alt"), + Kind::DefPath => write!(f, "def-path"), } - .into(); - DiagnosticArgValue::Str(kind) } } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 7b4ab67a24dfa..14dd8b4e56fb3 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -103,8 +103,6 @@ extern crate rustc_middle; #[macro_use] extern crate tracing; -use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; -use rustc_fluent_macro::fluent_messages; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -121,8 +119,6 @@ pub mod errors; pub mod test; pub mod typeid; -fluent_messages! { "../messages.ftl" } - /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is /// instantiated in crate Y, this is the symbol name this instance would have. diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/abi/call/csky.rs index bbe95fa20ac55..706493b0a6a69 100644 --- a/compiler/rustc_target/src/abi/call/csky.rs +++ b/compiler/rustc_target/src/abi/call/csky.rs @@ -1,17 +1,39 @@ -// See https://github.com/llvm/llvm-project/blob/d85b94bf0080dcd780656c0f5e6342800720eba9/llvm/lib/Target/CSKY/CSKYCallingConv.td -use crate::abi::call::{ArgAbi, FnAbi}; +// Reference: CSKY ABI Manual +// https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1695027452256/T-HEAD_800_Series_ABI_Standards_Manual.pdf +// +// Reference: Clang CSKY lowering code +// https://github.com/llvm/llvm-project/blob/4a074f32a6914f2a8d7215d78758c24942dddc3d/clang/lib/CodeGen/Targets/CSKY.cpp#L76-L162 -fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - if ret.layout.is_aggregate() || ret.layout.size.bits() > 64 { - ret.make_indirect(); +use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; + +fn classify_ret(arg: &mut ArgAbi<'_, Ty>) { + // For return type, aggregate which <= 2*XLen will be returned in registers. + // Otherwise, aggregate will be returned indirectly. + if arg.layout.is_aggregate() { + let total = arg.layout.size; + if total.bits() > 64 { + arg.make_indirect(); + } else if total.bits() > 32 { + arg.cast_to(Uniform { unit: Reg::i32(), total }); + } else { + arg.cast_to(Reg::i32()); + } } else { - ret.extend_integer_width_to(32); + arg.extend_integer_width_to(32); } } fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { - if arg.layout.is_aggregate() || arg.layout.size.bits() > 64 { - arg.make_indirect(); + // For argument type, the first 4*XLen parts of aggregate will be passed + // in registers, and the rest will be passed in stack. + // So we can coerce to integers directly and let backend handle it correctly. + if arg.layout.is_aggregate() { + let total = arg.layout.size; + if total.bits() > 32 { + arg.cast_to(Uniform { unit: Reg::i32(), total }); + } else { + arg.cast_to(Reg::i32()); + } } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index c126390f5a908..53f710b8f9e14 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -40,11 +40,9 @@ //! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) //! when linking in self-contained mode. -use crate::json::{Json, ToJson}; use crate::spec::LinkOutputKind; use std::borrow::Cow; use std::collections::BTreeMap; -use std::str::FromStr; pub type CrtObjects = BTreeMap>>; @@ -123,39 +121,3 @@ pub(super) fn pre_wasi_self_contained() -> CrtObjects { pub(super) fn post_wasi_self_contained() -> CrtObjects { new(&[]) } - -/// Which logic to use to determine whether to use self-contained linking mode -/// if `-Clink-self-contained` is not specified explicitly. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LinkSelfContainedDefault { - False, - True, - Musl, - Mingw, -} - -impl FromStr for LinkSelfContainedDefault { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "false" => LinkSelfContainedDefault::False, - "true" | "wasm" => LinkSelfContainedDefault::True, - "musl" => LinkSelfContainedDefault::Musl, - "mingw" => LinkSelfContainedDefault::Mingw, - _ => return Err(()), - }) - } -} - -impl ToJson for LinkSelfContainedDefault { - fn to_json(&self) -> Json { - match *self { - LinkSelfContainedDefault::False => "false", - LinkSelfContainedDefault::True => "true", - LinkSelfContainedDefault::Musl => "musl", - LinkSelfContainedDefault::Mingw => "mingw", - } - .to_json() - } -} diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 61553e71b4500..b698bcbcef698 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,5 +1,5 @@ -use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; -use crate::spec::TargetOptions; +use crate::spec::crt_objects; +use crate::spec::{LinkSelfContainedDefault, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); @@ -7,7 +7,7 @@ pub fn opts() -> TargetOptions { base.env = "musl".into(); base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); - base.link_self_contained = LinkSelfContainedDefault::Musl; + base.link_self_contained = LinkSelfContainedDefault::InferredForMusl; // These targets statically link libc by default base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f1c7513d88560..3541810d437ba 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -38,7 +38,7 @@ use crate::abi::call::Conv; use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use crate::spec::crt_objects::CrtObjects; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_fs_util::try_canonicalize; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -278,6 +278,7 @@ impl LinkerFlavor { } } + /// Returns the corresponding backwards-compatible CLI flavor. fn to_cli(self) -> LinkerFlavorCli { match self { LinkerFlavor::Gnu(Cc::Yes, _) @@ -298,6 +299,20 @@ impl LinkerFlavor { } } + /// Returns the modern CLI flavor that is the counterpart of this flavor. + fn to_cli_counterpart(self) -> LinkerFlavorCli { + match self { + LinkerFlavor::Gnu(cc, lld) => LinkerFlavorCli::Gnu(cc, lld), + LinkerFlavor::Darwin(cc, lld) => LinkerFlavorCli::Darwin(cc, lld), + LinkerFlavor::WasmLld(cc) => LinkerFlavorCli::WasmLld(cc), + LinkerFlavor::Unix(cc) => LinkerFlavorCli::Unix(cc), + LinkerFlavor::Msvc(lld) => LinkerFlavorCli::Msvc(lld), + LinkerFlavor::EmCc => LinkerFlavorCli::EmCc, + LinkerFlavor::Bpf => LinkerFlavorCli::Bpf, + LinkerFlavor::Ptx => LinkerFlavorCli::Ptx, + } + } + fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option, Option) { match cli { LinkerFlavorCli::Gnu(cc, lld) | LinkerFlavorCli::Darwin(cc, lld) => { @@ -520,6 +535,98 @@ impl ToJson for LinkerFlavorCli { } } +/// The different `-Clink-self-contained` options that can be specified in a target spec: +/// - enabling or disabling in bulk +/// - some target-specific pieces of inference to determine whether to use self-contained linking +/// if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw) +/// - explicitly enabling some of the self-contained linking components, e.g. the linker component +/// to use `rust-lld` +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum LinkSelfContainedDefault { + /// The target spec explicitly enables self-contained linking. + True, + + /// The target spec explicitly disables self-contained linking. + False, + + /// The target spec requests that the self-contained mode is inferred, in the context of musl. + InferredForMusl, + + /// The target spec requests that the self-contained mode is inferred, in the context of mingw. + InferredForMingw, + + /// The target spec explicitly enables a list of self-contained linking components: e.g. for + /// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`. + WithComponents(LinkSelfContainedComponents), +} + +/// Parses a backwards-compatible `-Clink-self-contained` option string, without components. +impl FromStr for LinkSelfContainedDefault { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "false" => LinkSelfContainedDefault::False, + "true" | "wasm" => LinkSelfContainedDefault::True, + "musl" => LinkSelfContainedDefault::InferredForMusl, + "mingw" => LinkSelfContainedDefault::InferredForMingw, + _ => return Err(()), + }) + } +} + +impl ToJson for LinkSelfContainedDefault { + fn to_json(&self) -> Json { + match *self { + LinkSelfContainedDefault::WithComponents(components) => { + // Serialize the components in a json object's `components` field, to prepare for a + // future where `crt-objects-fallback` is removed from the json specs and + // incorporated as a field here. + let mut map = BTreeMap::new(); + map.insert("components", components); + map.to_json() + } + + // Stable backwards-compatible values + LinkSelfContainedDefault::True => "true".to_json(), + LinkSelfContainedDefault::False => "false".to_json(), + LinkSelfContainedDefault::InferredForMusl => "musl".to_json(), + LinkSelfContainedDefault::InferredForMingw => "mingw".to_json(), + } + } +} + +impl LinkSelfContainedDefault { + /// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit + /// errors if the user then enables it on the CLI. + pub fn is_disabled(self) -> bool { + self == LinkSelfContainedDefault::False + } + + /// Returns whether the target spec explictly requests self-contained linking, i.e. not via + /// inference. + pub fn is_linker_enabled(self) -> bool { + match self { + LinkSelfContainedDefault::True => true, + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::WithComponents(c) => { + c.contains(LinkSelfContainedComponents::LINKER) + } + _ => false, + } + } + + /// Returns the key to use when serializing the setting to json: + /// - individual components in a `link-self-contained` object value + /// - the other variants as a backwards-compatible `crt-objects-fallback` string + fn json_key(self) -> &'static str { + match self { + LinkSelfContainedDefault::WithComponents(_) => "link-self-contained", + _ => "crt-objects-fallback", + } + } +} + bitflags::bitflags! { #[derive(Default)] /// The `-C link-self-contained` components that can individually be enabled or disabled. @@ -579,6 +686,21 @@ impl LinkSelfContainedComponents { LinkSelfContainedComponents::MINGW, ] } + + /// Returns whether at least a component is enabled. + pub fn are_any_components_enabled(self) -> bool { + !self.is_empty() + } + + /// Returns whether `LinkSelfContainedComponents::LINKER` is enabled. + pub fn is_linker_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::LINKER) + } + + /// Returns whether `LinkSelfContainedComponents::CRT_OBJECTS` is enabled. + pub fn is_crt_objects_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::CRT_OBJECTS) + } } impl IntoIterator for LinkSelfContainedComponents { @@ -594,6 +716,22 @@ impl IntoIterator for LinkSelfContainedComponents { } } +impl ToJson for LinkSelfContainedComponents { + fn to_json(&self) -> Json { + let components: Vec<_> = Self::all_components() + .into_iter() + .filter(|c| self.contains(*c)) + .map(|c| { + // We can unwrap because we're iterating over all the known singular components, + // not an actual set of flags where `as_str` can fail. + c.as_str().unwrap().to_owned() + }) + .collect(); + + components.to_json() + } +} + #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum PanicStrategy { Unwind, @@ -1770,6 +1908,8 @@ pub struct TargetOptions { /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled. pub pre_link_objects_self_contained: CrtObjects, pub post_link_objects_self_contained: CrtObjects, + /// Behavior for the self-contained linking mode: inferred for some targets, or explicitly + /// enabled (in bulk, or with individual components). pub link_self_contained: LinkSelfContainedDefault, /// Linker arguments that are passed *before* any user-defined libraries. @@ -2170,7 +2310,7 @@ impl TargetOptions { } fn update_to_cli(&mut self) { - self.linker_flavor_json = self.linker_flavor.to_cli(); + self.linker_flavor_json = self.linker_flavor.to_cli_counterpart(); self.lld_flavor_json = self.linker_flavor.lld_flavor(); self.linker_is_gnu_json = self.linker_flavor.is_gnu(); for (args, args_json) in [ @@ -2180,8 +2320,10 @@ impl TargetOptions { (&self.late_link_args_static, &mut self.late_link_args_static_json), (&self.post_link_args, &mut self.post_link_args_json), ] { - *args_json = - args.iter().map(|(flavor, args)| (flavor.to_cli(), args.clone())).collect(); + *args_json = args + .iter() + .map(|(flavor, args)| (flavor.to_cli_counterpart(), args.clone())) + .collect(); } } } @@ -2724,8 +2866,43 @@ impl Target { } Ok::<(), String>(()) } ); - - ($key_name:ident = $json_name:expr, link_self_contained) => ( { + ($key_name:ident, link_self_contained_components) => ( { + // Skeleton of what needs to be parsed: + // + // ``` + // $name: { + // "components": [ + // + // ] + // } + // ``` + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + if let Some(o) = o.as_object() { + let component_array = o.get("components") + .ok_or_else(|| format!("{name}: expected a \ + JSON object with a `components` field."))?; + let component_array = component_array.as_array() + .ok_or_else(|| format!("{name}.components: expected a JSON array"))?; + let mut components = LinkSelfContainedComponents::empty(); + for s in component_array { + components |= match s.as_str() { + Some(s) => { + LinkSelfContainedComponents::from_str(s) + .ok_or_else(|| format!("unknown \ + `-Clink-self-contained` component: {s}"))? + }, + _ => return Err(format!("not a string: {:?}", s)), + }; + } + base.$key_name = LinkSelfContainedDefault::WithComponents(components); + } else { + incorrect_type.push(name) + } + } + Ok::<(), String>(()) + } ); + ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { let name = $json_name; obj.remove(name).and_then(|o| o.as_str().and_then(|s| { match s.parse::() { @@ -2878,7 +3055,13 @@ impl Target { key!(post_link_objects = "post-link-objects", link_objects); key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); - key!(link_self_contained = "crt-objects-fallback", link_self_contained)?; + // Deserializes the backwards-compatible variants of `-Clink-self-contained` + key!( + link_self_contained = "crt-objects-fallback", + link_self_contained_backwards_compatible + )?; + // Deserializes the components variant of `-Clink-self-contained` + key!(link_self_contained, link_self_contained_components)?; key!(pre_link_args_json = "pre-link-args", link_args); key!(late_link_args_json = "late-link-args", link_args); key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); @@ -3134,7 +3317,6 @@ impl ToJson for Target { target_option_val!(post_link_objects); target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); - target_option_val!(link_self_contained, "crt-objects-fallback"); target_option_val!(link_args - pre_link_args_json, "pre-link-args"); target_option_val!(link_args - late_link_args_json, "late-link-args"); target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic"); @@ -3231,6 +3413,10 @@ impl ToJson for Target { d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); } + // Serializing `-Clink-self-contained` needs a dynamic key to support the + // backwards-compatible variants. + d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json()); + Json::Object(d) } } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index e0ecf8037c3e5..257867b1b807b 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -97,7 +97,7 @@ impl Target { ); } - if self.link_self_contained == LinkSelfContainedDefault::False { + if self.link_self_contained.is_disabled() { assert!( self.pre_link_objects_self_contained.is_empty() && self.post_link_objects_self_contained.is_empty() diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index a0476d542e642..23fabcdc90de5 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -72,7 +72,8 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::crt_objects::{self, LinkSelfContainedDefault}; +use super::crt_objects; +use super::LinkSelfContainedDefault; use super::{wasm_base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs index c567155fee653..ba9a99ae380e4 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs @@ -72,8 +72,8 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::crt_objects::{self, LinkSelfContainedDefault}; -use super::{wasm_base, Cc, LinkerFlavor, Target}; +use super::{crt_objects, wasm_base}; +use super::{Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; pub fn target() -> Target { let mut options = wasm_base::options(); diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 341763aadbaf1..82a3afeae31e1 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,4 +1,4 @@ -use super::crt_objects::LinkSelfContainedDefault; +use super::LinkSelfContainedDefault; use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; pub fn options() -> TargetOptions { diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 2231983f07126..b84e0fc078303 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,4 +1,5 @@ -use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; +use crate::spec::crt_objects; +use crate::spec::LinkSelfContainedDefault; use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions}; use std::borrow::Cow; @@ -90,7 +91,7 @@ pub fn opts() -> TargetOptions { post_link_objects: crt_objects::post_mingw(), pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), - link_self_contained: LinkSelfContainedDefault::Mingw, + link_self_contained: LinkSelfContainedDefault::InferredForMingw, late_link_args, late_link_args_dynamic, late_link_args_static, diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 20253b32add8c..a9792ca2795c9 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -28,6 +28,11 @@ trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-claus .label = invalid on-clause here trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute + .help = only `message`, `note` and `label` are allowed as options + .label = invalid option found here + +trait_selection_missing_options_for_on_unimplemented_attr = missing options for `on_unimplemented` attribute + .help = at least one of the `message`, `note` and `label` options are expected trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> [none] {""} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 0c214ca875382..6562bc86f99c3 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -37,6 +37,8 @@ pub(super) trait GoalKind<'tcx>: fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; + fn polarity(self) -> ty::ImplPolarity; + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs index 4950a444ffb50..be631552c577c 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -101,6 +101,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { self.projection_ty.trait_ref(tcx) } + fn polarity(self) -> ty::ImplPolarity { + ty::ImplPolarity::Positive + } + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { self.with_self_ty(tcx, self_ty) } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index fa91a125b6acc..7c4f6562f10de 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -22,6 +22,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { self.trait_ref } + fn polarity(self) -> ty::ImplPolarity { + self.polarity + } + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { self.with_self_ty(tcx, self_ty) } @@ -238,14 +242,25 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } - - if let ty::FnPtr(..) = goal.predicate.self_ty().kind() { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - Err(NoSolution) + let self_ty = goal.predicate.self_ty(); + match goal.predicate.polarity { + ty::ImplPolarity::Positive => { + if self_ty.is_fn_ptr() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + ty::ImplPolarity::Negative => { + // If a type is rigid and not a fn ptr, then we know for certain + // that it does *not* implement `FnPtr`. + if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + ty::ImplPolarity::Reservation => bug!(), } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 29b0c7189f937..dcf5fd869290f 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -9,20 +9,18 @@ use crate::infer::InferOk; use crate::solve::inspect; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::traits::engine::TraitEngineExt; -use crate::traits::outlives_bounds::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs}; use crate::traits::structural_normalize::StructurallyNormalizeExt; -use crate::traits::util::impl_subject_and_oblig; use crate::traits::NormalizeExt; use crate::traits::SkipLeakCheck; use crate::traits::{ - self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations, + Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::query::NoSolution; @@ -32,12 +30,11 @@ use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; -use std::iter; use std::ops::ControlFlow; /// Whether we do the orphan check relative to this crate or @@ -142,16 +139,13 @@ pub fn overlapping_impls( Some(overlap) } -fn with_fresh_ty_vars<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: DefId, -) -> ty::ImplHeader<'tcx> { - let tcx = selcx.tcx(); - let impl_args = selcx.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); +fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ty::ImplHeader<'tcx> { + let tcx = infcx.tcx; + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); - let header = ty::ImplHeader { + ty::ImplHeader { impl_def_id, + impl_args, self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args), trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)), predicates: tcx @@ -160,10 +154,18 @@ fn with_fresh_ty_vars<'cx, 'tcx>( .iter() .map(|(c, _)| c.as_predicate()) .collect(), - }; + } +} + +fn fresh_impl_header_normalized<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, +) -> ty::ImplHeader<'tcx> { + let header = fresh_impl_header(infcx, impl_def_id); let InferOk { value: mut header, obligations } = - selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(header); + infcx.at(&ObligationCause::dummy(), param_env).normalize(header); header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); header @@ -206,12 +208,13 @@ fn overlap<'tcx>( // empty environment. let param_env = ty::ParamEnv::empty(); - let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id); - let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id); + let impl1_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id); + let impl2_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id); // Equate the headers to find their intersection (the general type, with infer vars, // that may apply both impls). - let mut obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; + let mut obligations = + equate_impl_headers(selcx.infcx, param_env, &impl1_header, &impl2_header)?; debug!("overlap: unification check succeeded"); obligations.extend( @@ -312,20 +315,22 @@ fn overlap<'tcx>( #[instrument(level = "debug", skip(infcx), ret)] fn equate_impl_headers<'tcx>( infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, impl1: &ty::ImplHeader<'tcx>, impl2: &ty::ImplHeader<'tcx>, ) -> Option> { - let result = match (impl1.trait_ref, impl2.trait_ref) { - (Some(impl1_ref), Some(impl2_ref)) => infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref), - (None, None) => infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::Yes, - impl1.self_ty, - impl2.self_ty, - ), - _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), - }; + let result = + match (impl1.trait_ref, impl2.trait_ref) { + (Some(impl1_ref), Some(impl2_ref)) => infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref), + (None, None) => infcx.at(&ObligationCause::dummy(), param_env).eq( + DefineOpaqueTypes::Yes, + impl1.self_ty, + impl2.self_ty, + ), + _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), + }; result.map(|infer_ok| infer_ok.obligations).ok() } @@ -391,107 +396,182 @@ fn impl_intersection_has_negative_obligation( ) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); - // Create an infcx, taking the predicates of impl1 as assumptions: - let infcx = tcx.infer_ctxt().build(); - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let impl_env = tcx.param_env(impl1_def_id); - let subject1 = match traits::fully_normalize( - &infcx, - ObligationCause::dummy(), - impl_env, - tcx.impl_subject(impl1_def_id).instantiate_identity(), - ) { - Ok(s) => s, - Err(err) => { - tcx.sess.delay_span_bug( - tcx.def_span(impl1_def_id), - format!("failed to fully normalize {impl1_def_id:?}: {err:?}"), - ); - return false; - } - }; + let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build(); + let universe = infcx.universe(); - // Attempt to prove that impl2 applies, given all of the above. - let selcx = &mut SelectionContext::new(&infcx); - let impl2_args = infcx.fresh_args_for_item(DUMMY_SP, impl2_def_id); - let (subject2, normalization_obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_args, |_, _| { - ObligationCause::dummy() - }); - - // do the impls unify? If not, then it's not currently possible to prove any - // obligations about their intersection. - let Ok(InferOk { obligations: equate_obligations, .. }) = - infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No, subject1, subject2) + let impl1_header = fresh_impl_header(infcx, impl1_def_id); + let param_env = + ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args); + + let impl2_header = fresh_impl_header(infcx, impl2_def_id); + + // Equate the headers to find their intersection (the general type, with infer vars, + // that may apply both impls). + let Some(_equate_obligations) = + equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) else { - debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2); return false; }; - for obligation in normalization_obligations.into_iter().chain(equate_obligations) { - if negative_impl_exists(&infcx, &obligation, impl1_def_id) { - debug!("overlap: obligation unsatisfiable {:?}", obligation); - return true; - } - } + plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args)); - false + util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) + .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) } -/// Try to prove that a negative impl exist for the obligation or its supertraits. -/// -/// If such a negative impl exists, then the obligation definitely must not hold -/// due to coherence, even if it's not necessarily "knowable" in this crate. Any -/// valid impl downstream would not be able to exist due to the overlapping -/// negative impl. -#[instrument(level = "debug", skip(infcx))] -fn negative_impl_exists<'tcx>( +fn plug_infer_with_placeholders<'tcx>( infcx: &InferCtxt<'tcx>, - o: &PredicateObligation<'tcx>, - body_def_id: DefId, -) -> bool { - // Try to prove a negative obligation exists for super predicates - for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) { - if prove_negated_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) { - return true; + universe: ty::UniverseIndex, + value: impl TypeVisitable>, +) { + struct PlugInferWithPlaceholder<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + universe: ty::UniverseIndex, + var: ty::BoundVar, + } + + impl<'tcx> PlugInferWithPlaceholder<'_, 'tcx> { + fn next_var(&mut self) -> ty::BoundVar { + let var = self.var; + self.var = self.var + 1; + var + } + } + + impl<'tcx> TypeVisitor> for PlugInferWithPlaceholder<'_, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + let ty = self.infcx.shallow_resolve(ty); + if ty.is_ty_var() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + ty, + Ty::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundTy { + var: self.next_var(), + kind: ty::BoundTyKind::Anon, + }, + }, + ), + ) + else { + bug!() + }; + assert_eq!(obligations, &[]); + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { + let ct = self.infcx.shallow_resolve(ct); + if ct.is_ct_infer() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + ct, + ty::Const::new_placeholder( + self.infcx.tcx, + ty::Placeholder { universe: self.universe, bound: self.next_var() }, + ct.ty(), + ), + ) + else { + bug!() + }; + assert_eq!(obligations, &[]); + ControlFlow::Continue(()) + } else { + ct.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + if let ty::ReVar(vid) = *r { + let r = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid); + if r.is_var() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + r, + ty::Region::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundRegion { + var: self.next_var(), + kind: ty::BoundRegionKind::BrAnon, + }, + }, + ), + ) + else { + bug!() + }; + assert_eq!(obligations, &[]); + } + } + ControlFlow::Continue(()) } } - false + value.visit_with(&mut PlugInferWithPlaceholder { + infcx, + universe, + var: ty::BoundVar::from_u32(0), + }); } -#[instrument(level = "debug", skip(infcx))] -fn prove_negated_obligation<'tcx>( - infcx: InferCtxt<'tcx>, - o: &PredicateObligation<'tcx>, - body_def_id: DefId, +fn try_prove_negated_where_clause<'tcx>( + root_infcx: &InferCtxt<'tcx>, + clause: ty::Clause<'tcx>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { - let tcx = infcx.tcx; - - let Some(o) = o.flip_polarity(tcx) else { + let Some(negative_predicate) = clause.as_predicate().flip_polarity(root_infcx.tcx) else { return false; }; - let param_env = o.param_env; - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligation(o); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { + // FIXME(with_negative_coherence): the infcx has region contraints from equating + // the impl headers as requirements. Given that the only region constraints we + // get are involving inference regions in the root, it shouldn't matter, but + // still sus. + // + // We probably should just throw away the region obligations registered up until + // now, or ideally use them as assumptions when proving the region obligations + // that we get from proving the negative predicate below. + let ref infcx = root_infcx.fork(); + let ocx = ObligationCtxt::new(infcx); + + ocx.register_obligation(Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + param_env, + negative_predicate, + )); + if !ocx.select_all_or_error().is_empty() { return false; } - let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID); + // FIXME: We could use the assumed_wf_types from both impls, I think, + // if that wasn't implemented just for LocalDefId, and we'd need to do + // the normalization ourselves since this is totally fallible... + let outlives_env = OutlivesEnvironment::new(param_env); - let ocx = ObligationCtxt::new(&infcx); - let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id) else { + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { return false; - }; + } - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, body_def_id, wf_tys), - ); - infcx.resolve_regions(&outlives_env).is_empty() + true } /// Returns whether all impls which would apply to the `trait_ref` @@ -506,13 +586,6 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>( trait_ref: ty::TraitRef<'tcx>, mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, ) -> Result, E> { - if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() { - // The only types implementing `FnPtr` are function pointers, - // so if there's no impl of `FnPtr` in the current crate, - // then such an impl will never be added in the future. - return Ok(Ok(())); - } - if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 88f47b03cc8df..d9a1a98191d08 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -37,10 +37,10 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { Box::new(FulfillmentContext::new(infcx)) } - (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + (TraitSolver::Classic | TraitSolver::Next | TraitSolver::NextCoherence, true) => { Box::new(NextFulfillmentCtxt::new(infcx)) } - _ => bug!( + (TraitSolver::Next, false) => bug!( "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver() diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 016c44c20f65a..c96e41b88bd78 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -1,5 +1,8 @@ use super::{ObligationCauseCode, PredicateObligation}; use crate::infer::error_reporting::TypeErrCtxt; +use rustc_ast::AttrArgs; +use rustc_ast::AttrArgsEq; +use rustc_ast::AttrKind; use rustc_ast::{Attribute, MetaItem, NestedMetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; @@ -342,7 +345,22 @@ pub enum AppendConstMessage { #[derive(LintDiagnostic)] #[diag(trait_selection_malformed_on_unimplemented_attr)] -pub struct NoValueInOnUnimplementedLint; +#[help] +pub struct MalformedOnUnimplementedAttrLint { + #[label] + pub span: Span, +} + +impl MalformedOnUnimplementedAttrLint { + fn new(span: Span) -> Self { + Self { span } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_missing_options_for_on_unimplemented_attr)] +#[help] +pub struct MissingOptionsForOnUnimplementedAttr; impl<'tcx> OnUnimplementedDirective { fn parse( @@ -453,7 +471,7 @@ impl<'tcx> OnUnimplementedDirective { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), vec![item.span()], - NoValueInOnUnimplementedLint, + MalformedOnUnimplementedAttrLint::new(item.span()), ); } else { // nothing found @@ -530,21 +548,40 @@ impl<'tcx> OnUnimplementedDirective { append_const_msg: None, })) } else { + let item = attr.get_normal_item(); + let report_span = match &item.args { + AttrArgs::Empty => item.path.span, + AttrArgs::Delimited(args) => args.dspan.entire(), + AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => eq_span.to(expr.span), + AttrArgs::Eq(span, AttrArgsEq::Hir(expr)) => span.to(expr.span), + }; + tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), - attr.span, - NoValueInOnUnimplementedLint, + report_span, + MalformedOnUnimplementedAttrLint::new(report_span), ); Ok(None) } } else if is_diagnostic_namespace_variant { - tcx.emit_spanned_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), - attr.span, - NoValueInOnUnimplementedLint, - ); + match &attr.kind { + AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MalformedOnUnimplementedAttrLint::new(attr.span), + ); + } + _ => tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MissingOptionsForOnUnimplementedAttr, + ), + }; + Ok(None) } else { let reported = diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 78b466907b32b..947ea3eece3f0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -22,7 +22,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; -use rustc_hir::{AsyncCoroutineKind, CoroutineKind, Node}; +use rustc_hir::{CoroutineKind, CoroutineSource, Node}; use rustc_hir::{Expr, HirId}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -2410,7 +2410,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .and_then(|coroutine_did| { Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() { CoroutineKind::Coroutine => format!("coroutine is not {trait_name}"), - CoroutineKind::Async(AsyncCoroutineKind::Fn) => self + CoroutineKind::Async(CoroutineSource::Fn) => self .tcx .parent(coroutine_did) .as_local() @@ -2419,10 +2419,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|name| { format!("future returned by `{name}` is not {trait_name}") })?, - CoroutineKind::Async(AsyncCoroutineKind::Block) => { + CoroutineKind::Async(CoroutineSource::Block) => { format!("future created by async block is not {trait_name}") } - CoroutineKind::Async(AsyncCoroutineKind::Closure) => { + CoroutineKind::Async(CoroutineSource::Closure) => { format!("future created by async closure is not {trait_name}") } }) @@ -2995,11 +2995,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let sp = self.tcx.def_span(def_id); // Special-case this to say "async block" instead of `[static coroutine]`. - let kind = tcx.coroutine_kind(def_id).unwrap().descr(); + let kind = tcx.coroutine_kind(def_id).unwrap(); err.span_note( sp, with_forced_trimmed_paths!(format!( - "required because it's used within this {kind}", + "required because it's used within this {kind:#}", )), ) } @@ -3465,11 +3465,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) && let Some(typeck_results) = &self.typeck_results { - if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr { - let expr = expr.peel_blocks(); - let ty = - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)); - let span = expr.span; + if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { + let inner_expr = expr.peel_blocks(); + let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + .unwrap_or(Ty::new_misc_error(tcx)); + let span = inner_expr.span; if Some(span) != err.span.primary_span() { err.span_label( span, @@ -3480,6 +3480,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("this tail expression is of type `{ty}`") }, ); + if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() + && let ty::ClauseKind::Trait(pred) = clause + && [ + tcx.lang_items().fn_once_trait(), + tcx.lang_items().fn_mut_trait(), + tcx.lang_items().fn_trait(), + ].contains(&Some(pred.def_id())) + { + if let [stmt, ..] = block.stmts + && let hir::StmtKind::Semi(value) = stmt.kind + && let hir::ExprKind::Closure(hir::Closure { + body, + fn_decl_span, + .. + }) = value.kind + && let body = hir.body(*body) + && !matches!(body.value.kind, hir::ExprKind::Block(..)) + { + // Check if the failed predicate was an expectation of a closure type + // and if there might have been a `{ |args|` typo instead of `|args| {`. + err.multipart_suggestion( + "you might have meant to open the closure body instead of placing \ + a closure within a block", + vec![ + (expr.span.with_hi(value.span.lo()), String::new()), + (fn_decl_span.shrink_to_hi(), " {".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + // Maybe the bare block was meant to be a closure. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to create the closure instead of a block", + format!( + "|{}| ", + (0..pred.trait_ref.args.len() - 1).map(|_| "_") + .collect::>() + .join(", ")), + Applicability::MaybeIncorrect, + ); + } + } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 1245b4a7756e1..7d6aa657104d7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -98,6 +98,8 @@ pub trait TypeErrCtxtExt<'tcx> { error: &SelectionError<'tcx>, ); + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -157,12 +159,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { predicate: error.obligation.predicate, index: Some(index), }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate); } // We do this in 2 passes because we want to display errors in order, though @@ -200,6 +196,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for (error, suppressed) in iter::zip(&errors, &is_suppressed) { if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { self.report_fulfillment_error(error); + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate); } } } @@ -412,6 +420,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { return; } + if self.fn_arg_obligation(&obligation) { + // Silence redundant errors on binding acccess that are already + // reported on the binding definition (#56607). + return; + } let trait_ref = trait_predicate.to_poly_trait_ref(); let (post_message, pre_message, type_def) = self @@ -908,6 +921,26 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); } + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if let ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id, + .. + } = obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) + && let arg = arg.peel_borrows() + && let hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = arg.kind + && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) + && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) + && preds.contains(&obligation.predicate) + { + return true; + } + false + } + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -1611,9 +1644,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn describe_coroutine(&self, body_id: hir::BodyId) -> Option<&'static str> { self.tcx.hir().body(body_id).coroutine_kind.map(|gen_kind| match gen_kind { hir::CoroutineKind::Coroutine => "a coroutine", - hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Block) => "an async block", - hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Fn) => "an async function", - hir::CoroutineKind::Async(hir::AsyncCoroutineKind::Closure) => "an async closure", + hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block", + hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function", + hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure", }) } @@ -3071,6 +3104,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } }; + + if let Some(diag) = + self.tcx.sess.diagnostic().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) + { + diag.cancel(); + } + err } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 2d05f934393aa..dfc4bdf148467 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -97,6 +97,10 @@ fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { /// object. Note that object-safe traits can have some /// non-vtable-safe methods, so long as they require `Self: Sized` or /// otherwise ensure that they cannot be used when `Self = Trait`. +/// +/// [`MethodViolationCode::WhereClauseReferencesSelf`] is considered object safe due to backwards +/// compatibility, see and +/// [`WHERE_CLAUSES_OBJECT_SAFETY`]. pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); @@ -105,10 +109,9 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A return false; } - match virtual_call_violation_for_method(tcx, trait_def_id, method) { - None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true, - Some(_) => false, - } + virtual_call_violations_for_method(tcx, trait_def_id, method) + .iter() + .all(|v| matches!(v, MethodViolationCode::WhereClauseReferencesSelf)) } fn object_safety_violations_for_trait( @@ -119,7 +122,7 @@ fn object_safety_violations_for_trait( let mut violations: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item)) + .flat_map(|&item| object_safety_violations_for_assoc_item(tcx, trait_def_id, item)) .collect(); // Check the trait itself. @@ -357,49 +360,52 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// Returns `Some(_)` if this item makes the containing trait not object safe. #[instrument(level = "debug", skip(tcx), ret)] -fn object_safety_violation_for_assoc_item( +fn object_safety_violations_for_assoc_item( tcx: TyCtxt<'_>, trait_def_id: DefId, item: ty::AssocItem, -) -> Option { +) -> Vec { // Any item that has a `Self : Sized` requisite is otherwise // exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { - return None; + return Vec::new(); } match item.kind { // Associated consts are never object safe, as they can't have `where` bounds yet at all, // and associated const bounds in trait objects aren't a thing yet either. ty::AssocKind::Const => { - Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)) + vec![ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)] } - ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| { - let node = tcx.hir().get_if_local(item.def_id); - // Get an accurate span depending on the violation. - let span = match (&v, node) { - (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, - (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) - } - _ => item.ident(tcx).span, - }; + ty::AssocKind::Fn => virtual_call_violations_for_method(tcx, trait_def_id, item) + .into_iter() + .map(|v| { + let node = tcx.hir().get_if_local(item.def_id); + // Get an accurate span depending on the violation. + let span = match (&v, node) { + (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, + (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) + } + _ => item.ident(tcx).span, + }; - ObjectSafetyViolation::Method(item.name, v, span) - }), + ObjectSafetyViolation::Method(item.name, v, span) + }) + .collect(), // Associated types can only be object safe if they have `Self: Sized` bounds. ty::AssocKind::Type => { if !tcx.features().generic_associated_types_extended && !tcx.generics_of(item.def_id).params.is_empty() && !item.is_impl_trait_in_trait() { - Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)) + vec![ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)] } else { // We will permit associated types if they are explicitly mentioned in the trait object. // We can't check this here, as here we only check if it is guaranteed to not be possible. - None + Vec::new() } } } @@ -409,11 +415,11 @@ fn object_safety_violation_for_assoc_item( /// object; this does not necessarily imply that the enclosing trait /// is not object safe, because the method might have a where clause /// `Self:Sized`. -fn virtual_call_violation_for_method<'tcx>( +fn virtual_call_violations_for_method<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem, -) -> Option { +) -> Vec { let sig = tcx.fn_sig(method.def_id).instantiate_identity(); // The method's first parameter must be named `self` @@ -438,9 +444,14 @@ fn virtual_call_violation_for_method<'tcx>( } else { None }; - return Some(MethodViolationCode::StaticMethod(sugg)); + + // Not having `self` parameter messes up the later checks, + // so we need to return instead of pushing + return vec![MethodViolationCode::StaticMethod(sugg)]; } + let mut errors = Vec::new(); + for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { let span = if let Some(hir::Node::TraitItem(hir::TraitItem { @@ -452,20 +463,20 @@ fn virtual_call_violation_for_method<'tcx>( } else { None }; - return Some(MethodViolationCode::ReferencesSelfInput(span)); + errors.push(MethodViolationCode::ReferencesSelfInput(span)); } } if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { - return Some(MethodViolationCode::ReferencesSelfOutput); + errors.push(MethodViolationCode::ReferencesSelfOutput); } if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { - return Some(code); + errors.push(code); } // We can't monomorphize things like `fn foo(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); - if own_counts.types + own_counts.consts != 0 { - return Some(MethodViolationCode::Generic); + if own_counts.types > 0 || own_counts.consts > 0 { + errors.push(MethodViolationCode::Generic); } let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); @@ -485,7 +496,7 @@ fn virtual_call_violation_for_method<'tcx>( } else { None }; - return Some(MethodViolationCode::UndispatchableReceiver(span)); + errors.push(MethodViolationCode::UndispatchableReceiver(span)); } else { // Do sanity check to make sure the receiver actually has the layout of a pointer. @@ -593,10 +604,10 @@ fn virtual_call_violation_for_method<'tcx>( contains_illegal_self_type_reference(tcx, trait_def_id, pred) }) { - return Some(MethodViolationCode::WhereClauseReferencesSelf); + errors.push(MethodViolationCode::WhereClauseReferencesSelf); } - None + errors } /// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`. @@ -709,7 +720,6 @@ fn object_ty_for_trait<'tcx>( // FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this // fallback query: `Receiver: Unsize U]>` to support receivers like // `self: Wrapper`. -#[allow(dead_code)] fn receiver_is_dispatchable<'tcx>( tcx: TyCtxt<'tcx>, method: ty::AssocItem, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c3f56ae2756c6..af30d9121d1a8 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -898,7 +898,10 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - bug!("Bound vars outside of `self.universe_indices`"); + bug!( + "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); } ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); @@ -916,7 +919,10 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - bug!("Bound vars outside of `self.universe_indices`"); + bug!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); } ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); @@ -935,7 +941,10 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { - bug!("Bound vars outside of `self.universe_indices`"); + bug!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); } ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 349741a698c9a..c761d0d103ea2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -293,7 +293,7 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> _ => unreachable!(), }?; // We don't expect ambiguity. - if result.is_ambiguous() { + if !result.value.is_proven() { // Rustdoc normalizes possibly not well-formed types, so only // treat this as a bug if we're not in rustdoc. if !tcx.sess.opts.actually_rustdoc { 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 ee50a36be55b1..123881d9bc698 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -52,8 +52,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - // The only way to prove a NotImplemented(T: Foo) predicate is via a negative impl. - // There are no compiler built-in rules for this. + // Negative trait predicates have different rules than positive trait predicates. if obligation.polarity() == ty::ImplPolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); @@ -1064,6 +1063,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), ty::Bool diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 1a9de1500415e..dabe25589a06c 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -9,6 +9,7 @@ #![cfg_attr(not(bootstrap), feature(rustdoc_internals))] #![cfg_attr(not(bootstrap), allow(internal_features))] #![feature(assert_matches)] +#![feature(associated_type_defaults)] #![feature(iterator_try_collect)] #![feature(let_chains)] #![feature(if_let_guard)] @@ -39,6 +40,7 @@ mod layout_sanity_check; mod needs_drop; mod opaque_types; pub mod representability; +pub mod sig_types; mod structural_match; mod ty; diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 0010570e7b348..d1a154fe20d83 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -53,14 +53,10 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { fn parent(&self) -> Option { match self.tcx.def_kind(self.item) { - DefKind::AnonConst | DefKind::InlineConst | DefKind::Fn | DefKind::TyAlias => None, DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { Some(self.tcx.local_parent(self.item)) } - other => span_bug!( - self.tcx.def_span(self.item), - "unhandled item with opaque types: {other:?}" - ), + _ => None, } } @@ -98,14 +94,6 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { hir_id == scope } - fn collect_body_and_predicate_taits(&mut self) { - // Look at all where bounds. - self.tcx.predicates_of(self.item).instantiate_identity(self.tcx).visit_with(self); - // An item is allowed to constrain opaques declared within its own body (but not nested within - // nested functions). - self.collect_taits_declared_in_body(); - } - #[instrument(level = "trace", skip(self))] fn collect_taits_declared_in_body(&mut self) { let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(self.item)).value; @@ -132,6 +120,13 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } } +impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> { + fn visit(&mut self, span: Span, value: impl TypeVisitable>) -> ControlFlow { + self.visit_spanned(span, value); + ControlFlow::Continue(()) + } +} + impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { #[instrument(skip(self), ret, level = "trace")] fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { @@ -269,41 +264,27 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { } } -fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [LocalDefId] { +fn opaque_types_defined_by<'tcx>( + tcx: TyCtxt<'tcx>, + item: LocalDefId, +) -> &'tcx ty::List { let kind = tcx.def_kind(item); trace!(?kind); let mut collector = OpaqueTypeCollector::new(tcx, item); + super::sig_types::walk_types(tcx, item, &mut collector); match kind { - // Walk over the signature of the function-like to find the opaques. - DefKind::AssocFn | DefKind::Fn => { - let ty_sig = tcx.fn_sig(item).instantiate_identity(); - let hir_sig = tcx.hir().get_by_def_id(item).fn_sig().unwrap(); - // Walk over the inputs and outputs manually in order to get good spans for them. - collector.visit_spanned(hir_sig.decl.output.span(), ty_sig.output()); - for (hir, ty) in hir_sig.decl.inputs.iter().zip(ty_sig.inputs().iter()) { - collector.visit_spanned(hir.span, ty.map_bound(|x| *x)); - } - collector.collect_body_and_predicate_taits(); - } - // Walk over the type of the item to find opaques. - DefKind::Static(_) | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => { - let span = match tcx.hir().get_by_def_id(item).ty() { - Some(ty) => ty.span, - _ => tcx.def_span(item), - }; - collector.visit_spanned(span, tcx.type_of(item).instantiate_identity()); - collector.collect_body_and_predicate_taits(); - } - // We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds` - DefKind::TyAlias | DefKind::AssocTy => { - tcx.type_of(item).instantiate_identity().visit_with(&mut collector); - } - DefKind::OpaqueTy => { - for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() { - collector.visit_spanned(span, pred); - } + DefKind::AssocFn + | DefKind::Fn + | DefKind::Static(_) + | DefKind::Const + | DefKind::AssocConst + | DefKind::AnonConst => { + collector.collect_taits_declared_in_body(); } - DefKind::Mod + DefKind::OpaqueTy + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::Mod | DefKind::Struct | DefKind::Union | DefKind::Enum @@ -322,12 +303,13 @@ fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [ | DefKind::LifetimeParam | DefKind::GlobalAsm | DefKind::Impl { .. } => {} - // Closures and coroutines are type checked with their parent, so there is no difference here. + // Closures and coroutines are type checked with their parent, so we need to allow all + // opaques from the closure signature *and* from the parent body. DefKind::Closure | DefKind::Coroutine | DefKind::InlineConst => { - return tcx.opaque_types_defined_by(tcx.local_parent(item)); + collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); } } - tcx.arena.alloc_from_iter(collector.opaques) + tcx.mk_local_def_ids(&collector.opaques) } pub(super) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs new file mode 100644 index 0000000000000..1ab39974e0f2a --- /dev/null +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -0,0 +1,129 @@ +//! This module contains helpers for walking all types of +//! a signature, while preserving spans as much as possible + +use std::ops::ControlFlow; + +use rustc_hir::{def::DefKind, def_id::LocalDefId}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; +use rustc_type_ir::visit::TypeVisitable; + +pub trait SpannedTypeVisitor<'tcx> { + type BreakTy = !; + fn visit( + &mut self, + span: Span, + value: impl TypeVisitable>, + ) -> ControlFlow; +} + +pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( + tcx: TyCtxt<'tcx>, + item: LocalDefId, + visitor: &mut V, +) -> ControlFlow { + let kind = tcx.def_kind(item); + trace!(?kind); + match kind { + DefKind::Coroutine => { + match tcx.type_of(item).instantiate_identity().kind() { + ty::Coroutine(_, args, _) => visitor.visit(tcx.def_span(item), args.as_coroutine().sig())?, + _ => bug!(), + } + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + // Walk over the signature of the function-like + DefKind::Closure | DefKind::AssocFn | DefKind::Fn => { + let ty_sig = match kind { + DefKind::Closure => match tcx.type_of(item).instantiate_identity().kind() { + ty::Closure(_, args) => args.as_closure().sig(), + _ => bug!(), + }, + _ => tcx.fn_sig(item).instantiate_identity(), + }; + let hir_sig = tcx.hir().get_by_def_id(item).fn_decl().unwrap(); + // Walk over the inputs and outputs manually in order to get good spans for them. + visitor.visit(hir_sig.output.span(), ty_sig.output()); + for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) { + visitor.visit(hir.span, ty.map_bound(|x| *x))?; + } + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + // Walk over the type behind the alias + DefKind::TyAlias {..} | DefKind::AssocTy | + // Walk over the type of the item + DefKind::Static(_) | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => { + let span = match tcx.hir().get_by_def_id(item).ty() { + Some(ty) => ty.span, + _ => tcx.def_span(item), + }; + visitor.visit(span, tcx.type_of(item).instantiate_identity()); + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + DefKind::OpaqueTy => { + for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() { + visitor.visit(span, pred)?; + } + } + // Look at field types + DefKind::Struct | DefKind::Union | DefKind::Enum => { + let span = tcx.def_ident_span(item).unwrap(); + visitor.visit(span, tcx.type_of(item).instantiate_identity()); + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + // Does not have a syntactical signature + DefKind::InlineConst => {} + DefKind::Impl { of_trait } => { + if of_trait { + let span = tcx.hir().get_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span; + let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().args[1..]; + visitor.visit(span, args)?; + } + let span = match tcx.hir().get_by_def_id(item).ty() { + Some(ty) => ty.span, + _ => tcx.def_span(item), + }; + visitor.visit(span, tcx.type_of(item).instantiate_identity()); + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + }} + DefKind::Trait => { + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + DefKind::TraitAlias => { + for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + visitor.visit(span, pred)?; + } + } + | DefKind::Variant + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Ctor(_, _) + | DefKind::Field + | DefKind::LifetimeParam => { + span_bug!( + tcx.def_span(item), + "{kind:?} has not seen any uses of `walk_types` yet, ping oli-obk if you'd like any help" + ) + } + // These don't have any types. + | DefKind::ExternCrate + | DefKind::ForeignMod + | DefKind::Macro(_) + | DefKind::GlobalAsm + | DefKind::Mod + | DefKind::Use => {} + } + ControlFlow::Continue(()) +} diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs new file mode 100644 index 0000000000000..c0b6aed98ef2e --- /dev/null +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -0,0 +1,169 @@ +use std::fmt; +use std::hash; +use std::ops::ControlFlow; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_serialize::{Decodable, Encodable}; + +use crate::fold::{FallibleTypeFolder, TypeFoldable}; +use crate::visit::{TypeVisitable, TypeVisitor}; +use crate::TyDecoder; +use crate::{HashStableContext, Interner, TyEncoder, UniverseIndex}; + +/// A "canonicalized" type `V` is one where all free inference +/// variables have been rewritten to "canonical vars". These are +/// numbered starting from 0 in order of first appearance. +pub struct Canonical { + pub value: V, + pub max_universe: UniverseIndex, + pub variables: I::CanonicalVars, +} + +impl Canonical { + /// Allows you to map the `value` of a canonical while keeping the + /// same set of bound variables. + /// + /// **WARNING:** This function is very easy to mis-use, hence the + /// name! In particular, the new value `W` must use all **the + /// same type/region variables** in **precisely the same order** + /// as the original! (The ordering is defined by the + /// `TypeFoldable` implementation of the type in question.) + /// + /// An example of a **correct** use of this: + /// + /// ```rust,ignore (not real code) + /// let a: Canonical = ...; + /// let b: Canonical = a.unchecked_map(|v| (v, )); + /// ``` + /// + /// An example of an **incorrect** use of this: + /// + /// ```rust,ignore (not real code) + /// let a: Canonical = ...; + /// let ty: Ty = ...; + /// let b: Canonical)> = a.unchecked_map(|v| (v, ty)); + /// ``` + pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical { + let Canonical { max_universe, variables, value } = self; + Canonical { max_universe, variables, value: map_op(value) } + } + + /// Allows you to map the `value` of a canonical while keeping the same set of + /// bound variables. + /// + /// **WARNING:** This function is very easy to mis-use, hence the name! See + /// the comment of [Canonical::unchecked_map] for more details. + pub fn unchecked_rebind(self, value: W) -> Canonical { + let Canonical { max_universe, variables, value: _ } = self; + Canonical { max_universe, variables, value } + } +} + +impl hash::Hash for Canonical { + fn hash(&self, state: &mut H) { + self.value.hash(state); + self.max_universe.hash(state); + self.variables.hash(state); + } +} + +impl> HashStable for Canonical +where + I::CanonicalVars: HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.value.hash_stable(hcx, hasher); + self.max_universe.hash_stable(hcx, hasher); + self.variables.hash_stable(hcx, hasher); + } +} + +impl Eq for Canonical {} + +impl PartialEq for Canonical { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + && self.max_universe == other.max_universe + && self.variables == other.variables + } +} + +impl fmt::Display for Canonical { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}", + self.value, self.max_universe, self.variables + ) + } +} + +impl fmt::Debug for Canonical { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Canonical") + .field("value", &self.value) + .field("max_universe", &self.max_universe) + .field("variables", &self.variables) + .finish() + } +} + +impl Clone for Canonical { + fn clone(&self) -> Self { + Canonical { + value: self.value.clone(), + max_universe: self.max_universe.clone(), + variables: self.variables.clone(), + } + } +} + +impl Copy for Canonical where I::CanonicalVars: Copy {} + +impl> TypeFoldable for Canonical +where + I::CanonicalVars: TypeFoldable, +{ + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(Canonical { + value: self.value.try_fold_with(folder)?, + max_universe: self.max_universe.try_fold_with(folder)?, + variables: self.variables.try_fold_with(folder)?, + }) + } +} + +impl> TypeVisitable for Canonical +where + I::CanonicalVars: TypeVisitable, +{ + fn visit_with>(&self, folder: &mut F) -> ControlFlow { + self.value.visit_with(folder)?; + self.max_universe.visit_with(folder)?; + self.variables.visit_with(folder) + } +} + +impl, V: Encodable> Encodable for Canonical +where + I::CanonicalVars: Encodable, +{ + fn encode(&self, s: &mut E) { + self.value.encode(s); + self.max_universe.encode(s); + self.variables.encode(s); + } +} + +impl, V: Decodable> Decodable for Canonical +where + I::CanonicalVars: Decodable, +{ + fn decode(d: &mut D) -> Self { + Canonical { + value: Decodable::decode(d), + max_universe: Decodable::decode(d), + variables: Decodable::decode(d), + } + } +} diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs index 3b638934629b5..2fbc8f76fa4f9 100644 --- a/compiler/rustc_type_ir/src/codec.rs +++ b/compiler/rustc_type_ir/src/codec.rs @@ -1,4 +1,4 @@ -use crate::Interner; +use crate::{Interner, PredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decoder, Encoder}; @@ -30,9 +30,7 @@ pub trait TyEncoder: Encoder { fn type_shorthands(&mut self) -> &mut FxHashMap<::Ty, usize>; - fn predicate_shorthands( - &mut self, - ) -> &mut FxHashMap<::PredicateKind, usize>; + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; fn encode_alloc_id(&mut self, alloc_id: &::AllocId); } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index fbd403c82c6f8..a40c41583af5c 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,4 +1,5 @@ use rustc_data_structures::stable_hasher::HashStable; +use rustc_data_structures::stable_hasher::StableHasher; use rustc_serialize::{Decodable, Decoder, Encodable}; use std::cmp::Ordering; use std::fmt; @@ -86,11 +87,7 @@ where I::ErrorGuaranteed: HashStable, I::ExprConst: HashStable, { - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { const_kind_discriminant(self).hash_stable(hcx, hasher); match self { Param(p) => p.hash_stable(hcx, hasher), diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 2acb903c217a1..d5cadd4e83a7c 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -113,7 +113,7 @@ bitflags! { /// Does this value have `InferConst::Fresh`? const HAS_CT_FRESH = 1 << 22; - /// Does this have `Generator` or `GeneratorWitness`? - const HAS_TY_GENERATOR = 1 << 23; + /// Does this have `Coroutine` or `CoroutineWitness`? + const HAS_TY_COROUTINE = 1 << 23; } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 60e4c58799358..7f75e5b35a204 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -14,14 +14,11 @@ pub trait Interner: Sized { + Ord + IntoIterator; type GenericArg: Clone + DebugWithInfcx + Hash + Ord; + type Term: Clone + Debug + Hash + Ord; type Binder; - - // Predicates - type Predicate; - type PredicateKind: Clone + Debug + Hash + PartialEq + Eq; - type TypeAndMut: Clone + Debug + Hash + Ord; + type CanonicalVars: Clone + Debug + Hash + Eq; // Kinds of tys type Ty: Clone + DebugWithInfcx + Hash + Ord; @@ -56,6 +53,16 @@ pub trait Interner: Sized { type InferRegion: Clone + DebugWithInfcx + Hash + Ord; type PlaceholderRegion: Clone + Debug + Hash + Ord; + // Predicates + type Predicate: Clone + Debug + Hash + Eq; + type TraitPredicate: Clone + Debug + Hash + Eq; + type RegionOutlivesPredicate: Clone + Debug + Hash + Eq; + type TypeOutlivesPredicate: Clone + Debug + Hash + Eq; + type ProjectionPredicate: Clone + Debug + Hash + Eq; + type SubtypePredicate: Clone + Debug + Hash + Eq; + type CoercePredicate: Clone + Debug + Hash + Eq; + type ClosureKind: Clone + Debug + Hash + Eq; + fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability); } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 1ff220f3ed6b2..a056fbeda9811 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -26,17 +26,21 @@ pub mod visit; #[macro_use] mod macros; +mod canonical; mod const_kind; mod debug; mod flags; mod interner; +mod predicate_kind; mod region_kind; +pub use canonical::*; pub use codec::*; pub use const_kind::*; pub use debug::{DebugWithInfcx, InferCtxtLike, WithInfcx}; pub use flags::*; pub use interner::*; +pub use predicate_kind::*; pub use region_kind::*; pub use ty_info::*; pub use ty_kind::*; diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 9e10c65c20dc4..cfed84a35c671 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -49,4 +49,6 @@ TrivialTypeTraversalImpls! { u64, String, crate::DebruijnIndex, + crate::AliasRelationDirection, + crate::UniverseIndex, } diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs new file mode 100644 index 0000000000000..f6fabe691c6a7 --- /dev/null +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -0,0 +1,626 @@ +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_serialize::Decoder; +use rustc_serialize::{Decodable, Encodable}; +use std::fmt; +use std::hash; +use std::ops::ControlFlow; + +use crate::fold::{FallibleTypeFolder, TypeFoldable}; +use crate::visit::{TypeVisitable, TypeVisitor}; +use crate::{HashStableContext, Interner}; +use crate::{TyDecoder, TyEncoder}; + +/// A clause is something that can appear in where bounds or be inferred +/// by implied bounds. +pub enum ClauseKind { + /// Corresponds to `where Foo: Bar`. `Foo` here would be + /// the `Self` type of the trait reference and `A`, `B`, and `C` + /// would be the type parameters. + Trait(I::TraitPredicate), + + /// `where 'a: 'b` + RegionOutlives(I::RegionOutlivesPredicate), + + /// `where T: 'a` + TypeOutlives(I::TypeOutlivesPredicate), + + /// `where ::Name == X`, approximately. + /// See the `ProjectionPredicate` struct for details. + Projection(I::ProjectionPredicate), + + /// Ensures that a const generic argument to a parameter `const N: u8` + /// is of type `u8`. + ConstArgHasType(I::Const, I::Ty), + + /// No syntax: `T` well-formed. + WellFormed(I::GenericArg), + + /// Constant initializer must evaluate successfully. + ConstEvaluatable(I::Const), +} + +impl Clone for ClauseKind { + fn clone(&self) -> Self { + match self { + Self::Trait(arg0) => Self::Trait(arg0.clone()), + Self::RegionOutlives(arg0) => Self::RegionOutlives(arg0.clone()), + Self::TypeOutlives(arg0) => Self::TypeOutlives(arg0.clone()), + Self::Projection(arg0) => Self::Projection(arg0.clone()), + Self::ConstArgHasType(arg0, arg1) => Self::ConstArgHasType(arg0.clone(), arg1.clone()), + Self::WellFormed(arg0) => Self::WellFormed(arg0.clone()), + Self::ConstEvaluatable(arg0) => Self::ConstEvaluatable(arg0.clone()), + } + } +} + +impl Copy for ClauseKind +where + I::Ty: Copy, + I::Const: Copy, + I::GenericArg: Copy, + I::TraitPredicate: Copy, + I::ProjectionPredicate: Copy, + I::TypeOutlivesPredicate: Copy, + I::RegionOutlivesPredicate: Copy, +{ +} + +impl PartialEq for ClauseKind { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Trait(l0), Self::Trait(r0)) => l0 == r0, + (Self::RegionOutlives(l0), Self::RegionOutlives(r0)) => l0 == r0, + (Self::TypeOutlives(l0), Self::TypeOutlives(r0)) => l0 == r0, + (Self::Projection(l0), Self::Projection(r0)) => l0 == r0, + (Self::ConstArgHasType(l0, l1), Self::ConstArgHasType(r0, r1)) => l0 == r0 && l1 == r1, + (Self::WellFormed(l0), Self::WellFormed(r0)) => l0 == r0, + (Self::ConstEvaluatable(l0), Self::ConstEvaluatable(r0)) => l0 == r0, + _ => false, + } + } +} + +impl Eq for ClauseKind {} + +fn clause_kind_discriminant(value: &ClauseKind) -> usize { + match value { + ClauseKind::Trait(_) => 0, + ClauseKind::RegionOutlives(_) => 1, + ClauseKind::TypeOutlives(_) => 2, + ClauseKind::Projection(_) => 3, + ClauseKind::ConstArgHasType(_, _) => 4, + ClauseKind::WellFormed(_) => 5, + ClauseKind::ConstEvaluatable(_) => 6, + } +} + +impl hash::Hash for ClauseKind { + fn hash(&self, state: &mut H) { + clause_kind_discriminant(self).hash(state); + match self { + ClauseKind::Trait(p) => p.hash(state), + ClauseKind::RegionOutlives(p) => p.hash(state), + ClauseKind::TypeOutlives(p) => p.hash(state), + ClauseKind::Projection(p) => p.hash(state), + ClauseKind::ConstArgHasType(c, t) => { + c.hash(state); + t.hash(state); + } + ClauseKind::WellFormed(t) => t.hash(state), + ClauseKind::ConstEvaluatable(c) => c.hash(state), + } + } +} + +impl HashStable for ClauseKind +where + I::Ty: HashStable, + I::Const: HashStable, + I::GenericArg: HashStable, + I::TraitPredicate: HashStable, + I::ProjectionPredicate: HashStable, + I::TypeOutlivesPredicate: HashStable, + I::RegionOutlivesPredicate: HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + clause_kind_discriminant(self).hash_stable(hcx, hasher); + match self { + ClauseKind::Trait(p) => p.hash_stable(hcx, hasher), + ClauseKind::RegionOutlives(p) => p.hash_stable(hcx, hasher), + ClauseKind::TypeOutlives(p) => p.hash_stable(hcx, hasher), + ClauseKind::Projection(p) => p.hash_stable(hcx, hasher), + ClauseKind::ConstArgHasType(c, t) => { + c.hash_stable(hcx, hasher); + t.hash_stable(hcx, hasher); + } + ClauseKind::WellFormed(t) => t.hash_stable(hcx, hasher), + ClauseKind::ConstEvaluatable(c) => c.hash_stable(hcx, hasher), + } + } +} + +impl TypeFoldable for ClauseKind +where + I::Ty: TypeFoldable, + I::Const: TypeFoldable, + I::GenericArg: TypeFoldable, + I::TraitPredicate: TypeFoldable, + I::ProjectionPredicate: TypeFoldable, + I::TypeOutlivesPredicate: TypeFoldable, + I::RegionOutlivesPredicate: TypeFoldable, +{ + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + ClauseKind::Trait(p) => ClauseKind::Trait(p.try_fold_with(folder)?), + ClauseKind::RegionOutlives(p) => ClauseKind::RegionOutlives(p.try_fold_with(folder)?), + ClauseKind::TypeOutlives(p) => ClauseKind::TypeOutlives(p.try_fold_with(folder)?), + ClauseKind::Projection(p) => ClauseKind::Projection(p.try_fold_with(folder)?), + ClauseKind::ConstArgHasType(c, t) => { + ClauseKind::ConstArgHasType(c.try_fold_with(folder)?, t.try_fold_with(folder)?) + } + ClauseKind::WellFormed(p) => ClauseKind::WellFormed(p.try_fold_with(folder)?), + ClauseKind::ConstEvaluatable(p) => { + ClauseKind::ConstEvaluatable(p.try_fold_with(folder)?) + } + }) + } +} + +impl TypeVisitable for ClauseKind +where + I::Ty: TypeVisitable, + I::Const: TypeVisitable, + I::GenericArg: TypeVisitable, + I::TraitPredicate: TypeVisitable, + I::ProjectionPredicate: TypeVisitable, + I::TypeOutlivesPredicate: TypeVisitable, + I::RegionOutlivesPredicate: TypeVisitable, +{ + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + ClauseKind::Trait(p) => p.visit_with(visitor), + ClauseKind::RegionOutlives(p) => p.visit_with(visitor), + ClauseKind::TypeOutlives(p) => p.visit_with(visitor), + ClauseKind::Projection(p) => p.visit_with(visitor), + ClauseKind::ConstArgHasType(c, t) => { + c.visit_with(visitor)?; + t.visit_with(visitor) + } + ClauseKind::WellFormed(p) => p.visit_with(visitor), + ClauseKind::ConstEvaluatable(p) => p.visit_with(visitor), + } + } +} + +impl> Decodable for ClauseKind +where + I::Ty: Decodable, + I::Const: Decodable, + I::GenericArg: Decodable, + I::TraitPredicate: Decodable, + I::ProjectionPredicate: Decodable, + I::TypeOutlivesPredicate: Decodable, + I::RegionOutlivesPredicate: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => ClauseKind::Trait(Decodable::decode(d)), + 1 => ClauseKind::RegionOutlives(Decodable::decode(d)), + 2 => ClauseKind::TypeOutlives(Decodable::decode(d)), + 3 => ClauseKind::Projection(Decodable::decode(d)), + 4 => ClauseKind::ConstArgHasType(Decodable::decode(d), Decodable::decode(d)), + 5 => ClauseKind::WellFormed(Decodable::decode(d)), + 6 => ClauseKind::ConstEvaluatable(Decodable::decode(d)), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "ClauseKind", 7, + ) + ), + } + } +} + +impl Encodable for ClauseKind +where + I::Ty: Encodable, + I::Const: Encodable, + I::GenericArg: Encodable, + I::TraitPredicate: Encodable, + I::ProjectionPredicate: Encodable, + I::TypeOutlivesPredicate: Encodable, + I::RegionOutlivesPredicate: Encodable, +{ + fn encode(&self, s: &mut E) { + let discriminant = clause_kind_discriminant(self); + match self { + ClauseKind::Trait(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::RegionOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::TypeOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::Projection(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::ConstArgHasType(c, t) => s.emit_enum_variant(discriminant, |s| { + c.encode(s); + t.encode(s); + }), + ClauseKind::WellFormed(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::ConstEvaluatable(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + } + } +} + +pub enum PredicateKind { + /// Prove a clause + Clause(ClauseKind), + + /// Trait must be object-safe. + ObjectSafe(I::DefId), + + /// No direct syntax. May be thought of as `where T: FnFoo<...>` + /// for some generic args `...` and `T` being a closure type. + /// Satisfied (or refuted) once we know the closure's kind. + ClosureKind(I::DefId, I::GenericArgs, I::ClosureKind), + + /// `T1 <: T2` + /// + /// This obligation is created most often when we have two + /// unresolved type variables and hence don't have enough + /// information to process the subtyping obligation yet. + Subtype(I::SubtypePredicate), + + /// `T1` coerced to `T2` + /// + /// Like a subtyping obligation, this is created most often + /// when we have two unresolved type variables and hence + /// don't have enough information to process the coercion + /// obligation yet. At the moment, we actually process coercions + /// very much like subtyping and don't handle the full coercion + /// logic. + Coerce(I::CoercePredicate), + + /// Constants must be equal. The first component is the const that is expected. + ConstEquate(I::Const, I::Const), + + /// A marker predicate that is always ambiguous. + /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. + Ambiguous, + + /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. + /// This predicate requires two terms to be equal to eachother. + /// + /// Only used for new solver + AliasRelate(I::Term, I::Term, AliasRelationDirection), +} + +impl Copy for PredicateKind +where + I::DefId: Copy, + I::Const: Copy, + I::GenericArgs: Copy, + I::Term: Copy, + I::CoercePredicate: Copy, + I::SubtypePredicate: Copy, + I::ClosureKind: Copy, + ClauseKind: Copy, +{ +} + +impl Clone for PredicateKind { + fn clone(&self) -> Self { + match self { + Self::Clause(arg0) => Self::Clause(arg0.clone()), + Self::ObjectSafe(arg0) => Self::ObjectSafe(arg0.clone()), + Self::ClosureKind(arg0, arg1, arg2) => { + Self::ClosureKind(arg0.clone(), arg1.clone(), arg2.clone()) + } + Self::Subtype(arg0) => Self::Subtype(arg0.clone()), + Self::Coerce(arg0) => Self::Coerce(arg0.clone()), + Self::ConstEquate(arg0, arg1) => Self::ConstEquate(arg0.clone(), arg1.clone()), + Self::Ambiguous => Self::Ambiguous, + Self::AliasRelate(arg0, arg1, arg2) => { + Self::AliasRelate(arg0.clone(), arg1.clone(), arg2.clone()) + } + } + } +} + +impl PartialEq for PredicateKind { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Clause(l0), Self::Clause(r0)) => l0 == r0, + (Self::ObjectSafe(l0), Self::ObjectSafe(r0)) => l0 == r0, + (Self::ClosureKind(l0, l1, l2), Self::ClosureKind(r0, r1, r2)) => { + l0 == r0 && l1 == r1 && l2 == r2 + } + (Self::Subtype(l0), Self::Subtype(r0)) => l0 == r0, + (Self::Coerce(l0), Self::Coerce(r0)) => l0 == r0, + (Self::ConstEquate(l0, l1), Self::ConstEquate(r0, r1)) => l0 == r0 && l1 == r1, + (Self::AliasRelate(l0, l1, l2), Self::AliasRelate(r0, r1, r2)) => { + l0 == r0 && l1 == r1 && l2 == r2 + } + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +impl Eq for PredicateKind {} + +fn predicate_kind_discriminant(value: &PredicateKind) -> usize { + match value { + PredicateKind::Clause(_) => 0, + PredicateKind::ObjectSafe(_) => 1, + PredicateKind::ClosureKind(_, _, _) => 2, + PredicateKind::Subtype(_) => 3, + PredicateKind::Coerce(_) => 4, + PredicateKind::ConstEquate(_, _) => 5, + PredicateKind::Ambiguous => 6, + PredicateKind::AliasRelate(_, _, _) => 7, + } +} + +impl hash::Hash for PredicateKind { + fn hash(&self, state: &mut H) { + predicate_kind_discriminant(self).hash(state); + match self { + PredicateKind::Clause(p) => p.hash(state), + PredicateKind::ObjectSafe(d) => d.hash(state), + PredicateKind::ClosureKind(d, g, k) => { + d.hash(state); + g.hash(state); + k.hash(state); + } + PredicateKind::Subtype(p) => p.hash(state), + PredicateKind::Coerce(p) => p.hash(state), + PredicateKind::ConstEquate(c1, c2) => { + c1.hash(state); + c2.hash(state); + } + PredicateKind::Ambiguous => {} + PredicateKind::AliasRelate(t1, t2, r) => { + t1.hash(state); + t2.hash(state); + r.hash(state); + } + } + } +} + +impl HashStable for PredicateKind +where + I::DefId: HashStable, + I::Const: HashStable, + I::GenericArgs: HashStable, + I::Term: HashStable, + I::CoercePredicate: HashStable, + I::SubtypePredicate: HashStable, + I::ClosureKind: HashStable, + ClauseKind: HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + predicate_kind_discriminant(self).hash_stable(hcx, hasher); + match self { + PredicateKind::Clause(p) => p.hash_stable(hcx, hasher), + PredicateKind::ObjectSafe(d) => d.hash_stable(hcx, hasher), + PredicateKind::ClosureKind(d, g, k) => { + d.hash_stable(hcx, hasher); + g.hash_stable(hcx, hasher); + k.hash_stable(hcx, hasher); + } + PredicateKind::Subtype(p) => p.hash_stable(hcx, hasher), + PredicateKind::Coerce(p) => p.hash_stable(hcx, hasher), + PredicateKind::ConstEquate(c1, c2) => { + c1.hash_stable(hcx, hasher); + c2.hash_stable(hcx, hasher); + } + PredicateKind::Ambiguous => {} + PredicateKind::AliasRelate(t1, t2, r) => { + t1.hash_stable(hcx, hasher); + t2.hash_stable(hcx, hasher); + r.hash_stable(hcx, hasher); + } + } + } +} + +impl TypeFoldable for PredicateKind +where + I::DefId: TypeFoldable, + I::Const: TypeFoldable, + I::GenericArgs: TypeFoldable, + I::Term: TypeFoldable, + I::CoercePredicate: TypeFoldable, + I::SubtypePredicate: TypeFoldable, + I::ClosureKind: TypeFoldable, + ClauseKind: TypeFoldable, +{ + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + PredicateKind::Clause(c) => PredicateKind::Clause(c.try_fold_with(folder)?), + PredicateKind::ObjectSafe(d) => PredicateKind::ObjectSafe(d.try_fold_with(folder)?), + PredicateKind::ClosureKind(d, g, k) => PredicateKind::ClosureKind( + d.try_fold_with(folder)?, + g.try_fold_with(folder)?, + k.try_fold_with(folder)?, + ), + PredicateKind::Subtype(s) => PredicateKind::Subtype(s.try_fold_with(folder)?), + PredicateKind::Coerce(s) => PredicateKind::Coerce(s.try_fold_with(folder)?), + PredicateKind::ConstEquate(a, b) => { + PredicateKind::ConstEquate(a.try_fold_with(folder)?, b.try_fold_with(folder)?) + } + PredicateKind::Ambiguous => PredicateKind::Ambiguous, + PredicateKind::AliasRelate(a, b, d) => PredicateKind::AliasRelate( + a.try_fold_with(folder)?, + b.try_fold_with(folder)?, + d.try_fold_with(folder)?, + ), + }) + } +} + +impl TypeVisitable for PredicateKind +where + I::DefId: TypeVisitable, + I::Const: TypeVisitable, + I::GenericArgs: TypeVisitable, + I::Term: TypeVisitable, + I::CoercePredicate: TypeVisitable, + I::SubtypePredicate: TypeVisitable, + I::ClosureKind: TypeVisitable, + ClauseKind: TypeVisitable, +{ + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + PredicateKind::Clause(p) => p.visit_with(visitor), + PredicateKind::ObjectSafe(d) => d.visit_with(visitor), + PredicateKind::ClosureKind(d, g, k) => { + d.visit_with(visitor)?; + g.visit_with(visitor)?; + k.visit_with(visitor) + } + PredicateKind::Subtype(s) => s.visit_with(visitor), + PredicateKind::Coerce(s) => s.visit_with(visitor), + PredicateKind::ConstEquate(a, b) => { + a.visit_with(visitor)?; + b.visit_with(visitor) + } + PredicateKind::Ambiguous => ControlFlow::Continue(()), + PredicateKind::AliasRelate(a, b, d) => { + a.visit_with(visitor)?; + b.visit_with(visitor)?; + d.visit_with(visitor) + } + } + } +} + +impl> Decodable for PredicateKind +where + I::DefId: Decodable, + I::Const: Decodable, + I::GenericArgs: Decodable, + I::Term: Decodable, + I::CoercePredicate: Decodable, + I::SubtypePredicate: Decodable, + I::ClosureKind: Decodable, + ClauseKind: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => PredicateKind::Clause(Decodable::decode(d)), + 1 => PredicateKind::ObjectSafe(Decodable::decode(d)), + 2 => PredicateKind::ClosureKind( + Decodable::decode(d), + Decodable::decode(d), + Decodable::decode(d), + ), + 3 => PredicateKind::Subtype(Decodable::decode(d)), + 4 => PredicateKind::Coerce(Decodable::decode(d)), + 5 => PredicateKind::ConstEquate(Decodable::decode(d), Decodable::decode(d)), + 6 => PredicateKind::Ambiguous, + 7 => PredicateKind::AliasRelate( + Decodable::decode(d), + Decodable::decode(d), + Decodable::decode(d), + ), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "PredicateKind", 8, + ) + ), + } + } +} + +impl Encodable for PredicateKind +where + I::DefId: Encodable, + I::Const: Encodable, + I::GenericArgs: Encodable, + I::Term: Encodable, + I::CoercePredicate: Encodable, + I::SubtypePredicate: Encodable, + I::ClosureKind: Encodable, + ClauseKind: Encodable, +{ + fn encode(&self, s: &mut E) { + let discriminant = predicate_kind_discriminant(self); + match self { + PredicateKind::Clause(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::ObjectSafe(d) => s.emit_enum_variant(discriminant, |s| d.encode(s)), + PredicateKind::ClosureKind(d, g, k) => s.emit_enum_variant(discriminant, |s| { + d.encode(s); + g.encode(s); + k.encode(s); + }), + PredicateKind::Subtype(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::Coerce(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::ConstEquate(a, b) => s.emit_enum_variant(discriminant, |s| { + a.encode(s); + b.encode(s); + }), + PredicateKind::Ambiguous => s.emit_enum_variant(discriminant, |_s| {}), + PredicateKind::AliasRelate(a, b, d) => s.emit_enum_variant(discriminant, |s| { + a.encode(s); + b.encode(s); + d.encode(s); + }), + } + } +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum AliasRelationDirection { + Equate, + Subtype, +} + +impl std::fmt::Display for AliasRelationDirection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AliasRelationDirection::Equate => write!(f, "=="), + AliasRelationDirection::Subtype => write!(f, "<:"), + } + } +} + +// FIXME: Convert to DebugWithInfcx impl +impl fmt::Debug for ClauseKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"), + ClauseKind::Trait(a) => a.fmt(f), + ClauseKind::RegionOutlives(pair) => pair.fmt(f), + ClauseKind::TypeOutlives(pair) => pair.fmt(f), + ClauseKind::Projection(pair) => pair.fmt(f), + ClauseKind::WellFormed(data) => write!(f, "WellFormed({data:?})"), + ClauseKind::ConstEvaluatable(ct) => { + write!(f, "ConstEvaluatable({ct:?})") + } + } + } +} + +// FIXME: Convert to DebugWithInfcx impl +impl fmt::Debug for PredicateKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PredicateKind::Clause(a) => a.fmt(f), + PredicateKind::Subtype(pair) => pair.fmt(f), + PredicateKind::Coerce(pair) => pair.fmt(f), + PredicateKind::ObjectSafe(trait_def_id) => { + write!(f, "ObjectSafe({trait_def_id:?})") + } + PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { + write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})") + } + PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), + PredicateKind::Ambiguous => write!(f, "Ambiguous"), + PredicateKind::AliasRelate(t1, t2, dir) => { + write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") + } + } + } +} diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 23e575cbc3751..19576ea58f1f0 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,4 +1,5 @@ use rustc_data_structures::stable_hasher::HashStable; +use rustc_data_structures::stable_hasher::StableHasher; use rustc_serialize::{Decodable, Decoder, Encodable}; use std::cmp::Ordering; use std::fmt; @@ -306,7 +307,7 @@ impl fmt::Debug for RegionKind { } // This is manually implemented because a derive would require `I: Encodable` -impl Encodable for RegionKind +impl> Encodable for RegionKind where I::EarlyBoundRegion: Encodable, I::BoundRegion: Encodable, @@ -381,11 +382,7 @@ where I::PlaceholderRegion: HashStable, { #[inline] - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { ReErased | ReStatic | ReError(_) => { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 9c719d2d9784b..b542547589ac4 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -622,7 +622,7 @@ impl fmt::Debug for TyKind { } // This is manually implemented because a derive would require `I: Encodable` -impl Encodable for TyKind +impl> Encodable for TyKind where I::ErrorGuaranteed: Encodable, I::AdtDef: Encodable, @@ -640,7 +640,6 @@ where I::BoundTy: Encodable, I::PlaceholderTy: Encodable, I::InferTy: Encodable, - I::PredicateKind: Encodable, I::AllocId: Encodable, { fn encode(&self, e: &mut E) { @@ -753,7 +752,6 @@ where I::BoundTy: Decodable, I::PlaceholderTy: Decodable, I::InferTy: Decodable, - I::PredicateKind: Decodable, I::AllocId: Decodable, { fn decode(d: &mut D) -> Self { @@ -817,11 +815,7 @@ where I::ErrorGuaranteed: HashStable, { #[inline] - fn hash_stable( - &self, - __hcx: &mut CTX, - __hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, __hcx: &mut CTX, __hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(__hcx, __hasher); match self { Bool => {} diff --git a/compiler/stable_mir/src/fold.rs b/compiler/stable_mir/src/fold.rs deleted file mode 100644 index ca6ea92c4a13c..0000000000000 --- a/compiler/stable_mir/src/fold.rs +++ /dev/null @@ -1,245 +0,0 @@ -use std::ops::ControlFlow; - -use crate::Opaque; - -use super::ty::{ - Allocation, Binder, Const, ConstDef, ConstantKind, ExistentialPredicate, FnSig, GenericArgKind, - GenericArgs, Promoted, Region, RigidTy, TermKind, Ty, TyKind, UnevaluatedConst, -}; - -pub trait Folder: Sized { - type Break; - fn fold_ty(&mut self, ty: &Ty) -> ControlFlow { - ty.super_fold(self) - } - fn fold_const(&mut self, c: &Const) -> ControlFlow { - c.super_fold(self) - } - fn fold_reg(&mut self, reg: &Region) -> ControlFlow { - reg.super_fold(self) - } -} - -pub trait Foldable: Sized + Clone { - fn fold(&self, folder: &mut V) -> ControlFlow { - self.super_fold(folder) - } - fn super_fold(&self, folder: &mut V) -> ControlFlow; -} - -impl Foldable for Ty { - fn fold(&self, folder: &mut V) -> ControlFlow { - folder.fold_ty(self) - } - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut kind = self.kind(); - match &mut kind { - super::ty::TyKind::RigidTy(ty) => *ty = ty.fold(folder)?, - super::ty::TyKind::Alias(_, alias) => alias.args = alias.args.fold(folder)?, - super::ty::TyKind::Param(_) => {} - super::ty::TyKind::Bound(_, _) => {} - } - ControlFlow::Continue(kind.into()) - } -} - -impl Foldable for Const { - fn fold(&self, folder: &mut V) -> ControlFlow { - folder.fold_const(self) - } - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut this = self.clone(); - match &mut this.literal { - super::ty::ConstantKind::Allocated(alloc) => *alloc = alloc.fold(folder)?, - super::ty::ConstantKind::Unevaluated(uv) => *uv = uv.fold(folder)?, - super::ty::ConstantKind::Param(_) => {} - } - this.ty = this.ty.fold(folder)?; - ControlFlow::Continue(this) - } -} - -impl Foldable for Opaque { - fn super_fold(&self, _folder: &mut V) -> ControlFlow { - ControlFlow::Continue(self.clone()) - } -} - -impl Foldable for Allocation { - fn super_fold(&self, _folder: &mut V) -> ControlFlow { - ControlFlow::Continue(self.clone()) - } -} - -impl Foldable for UnevaluatedConst { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let UnevaluatedConst { def, args, promoted } = self; - ControlFlow::Continue(UnevaluatedConst { - def: def.fold(folder)?, - args: args.fold(folder)?, - promoted: promoted.fold(folder)?, - }) - } -} - -impl Foldable for ConstDef { - fn super_fold(&self, _folder: &mut V) -> ControlFlow { - ControlFlow::Continue(*self) - } -} - -impl Foldable for Option { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - ControlFlow::Continue(match self { - Some(val) => Some(val.fold(folder)?), - None => None, - }) - } -} - -impl Foldable for Promoted { - fn super_fold(&self, _folder: &mut V) -> ControlFlow { - ControlFlow::Continue(*self) - } -} - -impl Foldable for GenericArgs { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - ControlFlow::Continue(GenericArgs(self.0.fold(folder)?)) - } -} - -impl Foldable for Region { - fn fold(&self, folder: &mut V) -> ControlFlow { - folder.fold_reg(self) - } - fn super_fold(&self, _: &mut V) -> ControlFlow { - ControlFlow::Continue(self.clone()) - } -} - -impl Foldable for GenericArgKind { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut this = self.clone(); - match &mut this { - GenericArgKind::Lifetime(lt) => *lt = lt.fold(folder)?, - GenericArgKind::Type(t) => *t = t.fold(folder)?, - GenericArgKind::Const(c) => *c = c.fold(folder)?, - } - ControlFlow::Continue(this) - } -} - -impl Foldable for RigidTy { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut this = self.clone(); - match &mut this { - RigidTy::Bool - | RigidTy::Char - | RigidTy::Int(_) - | RigidTy::Uint(_) - | RigidTy::Float(_) - | RigidTy::Never - | RigidTy::Foreign(_) - | RigidTy::Str => {} - RigidTy::Array(t, c) => { - *t = t.fold(folder)?; - *c = c.fold(folder)?; - } - RigidTy::Slice(inner) => *inner = inner.fold(folder)?, - RigidTy::RawPtr(ty, _) => *ty = ty.fold(folder)?, - RigidTy::Ref(reg, ty, _) => { - *reg = reg.fold(folder)?; - *ty = ty.fold(folder)? - } - RigidTy::FnDef(_, args) => *args = args.fold(folder)?, - RigidTy::FnPtr(sig) => *sig = sig.fold(folder)?, - RigidTy::Closure(_, args) => *args = args.fold(folder)?, - RigidTy::Coroutine(_, args, _) => *args = args.fold(folder)?, - RigidTy::Dynamic(pred, r, _) => { - *pred = pred.fold(folder)?; - *r = r.fold(folder)?; - } - RigidTy::Tuple(fields) => *fields = fields.fold(folder)?, - RigidTy::Adt(_, args) => *args = args.fold(folder)?, - } - ControlFlow::Continue(this) - } -} - -impl Foldable for Vec { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut this = self.clone(); - for arg in &mut this { - *arg = arg.fold(folder)?; - } - ControlFlow::Continue(this) - } -} - -impl Foldable for Binder { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - ControlFlow::Continue(Self { - value: self.value.fold(folder)?, - bound_vars: self.bound_vars.clone(), - }) - } -} - -impl Foldable for ExistentialPredicate { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - let mut this = self.clone(); - match &mut this { - ExistentialPredicate::Trait(tr) => tr.generic_args = tr.generic_args.fold(folder)?, - ExistentialPredicate::Projection(p) => { - p.term = p.term.fold(folder)?; - p.generic_args = p.generic_args.fold(folder)?; - } - ExistentialPredicate::AutoTrait(_) => {} - } - ControlFlow::Continue(this) - } -} - -impl Foldable for TermKind { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - ControlFlow::Continue(match self { - TermKind::Type(t) => TermKind::Type(t.fold(folder)?), - TermKind::Const(c) => TermKind::Const(c.fold(folder)?), - }) - } -} - -impl Foldable for FnSig { - fn super_fold(&self, folder: &mut V) -> ControlFlow { - ControlFlow::Continue(Self { - inputs_and_output: self.inputs_and_output.fold(folder)?, - c_variadic: self.c_variadic, - unsafety: self.unsafety, - abi: self.abi.clone(), - }) - } -} - -pub enum Never {} - -/// In order to instantiate a `Foldable`'s generic parameters with specific arguments, -/// `GenericArgs` can be used as a `Folder` that replaces all mentions of generic params -/// with the entries in its list. -impl Folder for GenericArgs { - type Break = Never; - - fn fold_ty(&mut self, ty: &Ty) -> ControlFlow { - ControlFlow::Continue(match ty.kind() { - TyKind::Param(p) => self[p], - _ => *ty, - }) - } - - fn fold_const(&mut self, c: &Const) -> ControlFlow { - ControlFlow::Continue(match &c.literal { - ConstantKind::Param(p) => self[p.clone()].clone(), - _ => c.clone(), - }) - } -} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 8cacbdbda487f..38915afaa0c84 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -32,7 +32,6 @@ use self::ty::{ extern crate scoped_tls; pub mod error; -pub mod fold; pub mod mir; pub mod ty; pub mod visitor; @@ -215,9 +214,6 @@ pub trait Context { /// Obtain the representation of a type. fn ty_kind(&self, ty: Ty) -> TyKind; - /// Create a new `Ty` from scratch without information from rustc. - fn mk_ty(&self, kind: TyKind) -> Ty; - /// Get the body of an Instance. /// FIXME: Monomorphize the body. fn instance_body(&self, instance: InstanceDef) -> Body; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 5eee1ec00df50..50c37e8f91033 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -2,10 +2,60 @@ use crate::ty::{AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability use crate::Opaque; use crate::{ty::Ty, Span}; +/// The SMIR representation of a single function. #[derive(Clone, Debug)] pub struct Body { pub blocks: Vec, - pub locals: LocalDecls, + + // Declarations of locals within the function. + // + // The first local is the return value pointer, followed by `arg_count` + // locals for the function arguments, followed by any user-declared + // variables and temporaries. + locals: LocalDecls, + + // The number of arguments this function takes. + arg_count: usize, +} + +impl Body { + /// Constructs a `Body`. + /// + /// A constructor is required to build a `Body` from outside the crate + /// because the `arg_count` and `locals` fields are private. + pub fn new(blocks: Vec, locals: LocalDecls, arg_count: usize) -> Self { + // If locals doesn't contain enough entries, it can lead to panics in + // `ret_local`, `arg_locals`, and `inner_locals`. + assert!( + locals.len() > arg_count, + "A Body must contain at least a local for the return value and each of the function's arguments" + ); + Self { blocks, locals, arg_count } + } + + /// Return local that holds this function's return value. + pub fn ret_local(&self) -> &LocalDecl { + &self.locals[0] + } + + /// Locals in `self` that correspond to this function's arguments. + pub fn arg_locals(&self) -> &[LocalDecl] { + &self.locals[1..][..self.arg_count] + } + + /// Inner locals for this function. These are the locals that are + /// neither the return local nor the argument locals. + pub fn inner_locals(&self) -> &[LocalDecl] { + &self.locals[self.arg_count + 1..] + } + + /// Convenience function to get all the locals in this function. + /// + /// Locals are typically accessed via the more specific methods `ret_local`, + /// `arg_locals`, and `inner_locals`. + pub fn locals(&self) -> &[LocalDecl] { + &self.locals + } } type LocalDecls = Vec; @@ -135,12 +185,12 @@ pub enum UnOp { #[derive(Clone, Debug)] pub enum CoroutineKind { - Async(AsyncCoroutineKind), + Async(CoroutineSource), Coroutine, } #[derive(Clone, Debug)] -pub enum AsyncCoroutineKind { +pub enum CoroutineSource { Block, Closure, Fn, @@ -467,7 +517,7 @@ pub enum NullOp { } impl Operand { - pub fn ty(&self, locals: &LocalDecls) -> Ty { + pub fn ty(&self, locals: &[LocalDecl]) -> Ty { match self { Operand::Copy(place) | Operand::Move(place) => place.ty(locals), Operand::Constant(c) => c.ty(), @@ -477,12 +527,12 @@ impl Operand { impl Constant { pub fn ty(&self) -> Ty { - self.literal.ty + self.literal.ty() } } impl Place { - pub fn ty(&self, locals: &LocalDecls) -> Ty { + pub fn ty(&self, locals: &[LocalDecl]) -> Ty { let _start_ty = locals[self.local].ty; todo!("Implement projection") } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 8f7f8bd4e3845..e7440cc439b16 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -6,7 +6,7 @@ use super::{ use crate::{Filename, Opaque}; use std::fmt::{self, Debug, Formatter}; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Ty(pub usize); impl Debug for Ty { @@ -21,18 +21,37 @@ impl Ty { } } -impl From for Ty { - fn from(value: TyKind) -> Self { - with(|context| context.mk_ty(value)) +/// Represents a constant in MIR or from the Type system. +#[derive(Clone, Debug)] +pub struct Const { + /// The constant kind. + kind: ConstantKind, + /// The constant type. + ty: Ty, + /// Used for internal tracking of the internal constant. + pub id: ConstId, +} + +impl Const { + /// Build a constant. Note that this should only be used by the compiler. + pub fn new(kind: ConstantKind, ty: Ty, id: ConstId) -> Const { + Const { kind, ty, id } } -} -#[derive(Debug, Clone)] -pub struct Const { - pub literal: ConstantKind, - pub ty: Ty, + /// Retrieve the constant kind. + pub fn kind(&self) -> &ConstantKind { + &self.kind + } + + /// Get the constant type. + pub fn ty(&self) -> Ty { + self.ty + } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ConstId(pub usize); + type Ident = Opaque; #[derive(Debug, Clone)] @@ -108,15 +127,6 @@ pub struct LineInfo { pub end_col: usize, } -impl IndexedVal for Span { - fn to_val(index: usize) -> Self { - Span(index) - } - fn to_index(&self) -> usize { - self.0 - } -} - #[derive(Clone, Debug)] pub enum TyKind { RigidTy(RigidTy), @@ -434,6 +444,9 @@ pub enum ConstantKind { Allocated(Allocation), Unevaluated(UnevaluatedConst), Param(ParamConst), + /// Store ZST constants. + /// We have to special handle these constants since its type might be generic. + ZeroSized, } #[derive(Clone, Debug)] @@ -603,3 +616,20 @@ pub trait IndexedVal { fn to_index(&self) -> usize; } + +macro_rules! index_impl { + ($name:ident) => { + impl IndexedVal for $name { + fn to_val(index: usize) -> Self { + $name(index) + } + fn to_index(&self) -> usize { + self.0 + } + } + }; +} + +index_impl!(ConstId); +index_impl!(Ty); +index_impl!(Span); diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index a6020cc5bd9b2..05e0b9b4d7860 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -47,12 +47,12 @@ impl Visitable for Const { visitor.visit_const(self) } fn super_visit(&self, visitor: &mut V) -> ControlFlow { - match &self.literal { + match &self.kind() { super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?, super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?, - super::ty::ConstantKind::Param(_) => {} + super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {} } - self.ty.visit(visitor) + self.ty().visit(visitor) } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f28ae9a07b48f..4f0a02da44079 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -141,7 +141,6 @@ #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(pattern)] -#![feature(pointer_byte_offsets)] #![feature(ptr_addr_eq)] #![feature(ptr_internals)] #![feature(ptr_metadata)] diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 4ac956e7b76dc..7ce33bdd41159 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1450,7 +1450,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphanumeric(&self) -> bool { - matches!(*self, '0'..='9' | 'A'..='Z' | 'a'..='z') + matches!(*self, '0'..='9') | matches!(*self, 'A'..='Z') | matches!(*self, 'a'..='z') } /// Checks if the value is an ASCII decimal digit: @@ -1553,7 +1553,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_hexdigit(&self) -> bool { - matches!(*self, '0'..='9' | 'A'..='F' | 'a'..='f') + matches!(*self, '0'..='9') | matches!(*self, 'A'..='F') | matches!(*self, 'a'..='f') } /// Checks if the value is an ASCII punctuation character: @@ -1591,7 +1591,10 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_punctuation(&self) -> bool { - matches!(*self, '!'..='/' | ':'..='@' | '['..='`' | '{'..='~') + matches!(*self, '!'..='/') + | matches!(*self, ':'..='@') + | matches!(*self, '['..='`') + | matches!(*self, '{'..='~') } /// Checks if the value is an ASCII graphic character: diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index c7ace58afa866..6adea444214eb 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -69,6 +69,7 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} message = "`{Self}` is not an iterator" )] #[doc(notable_trait)] +#[cfg_attr(not(bootstrap), lang = "iterator")] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait Iterator { @@ -2141,7 +2142,7 @@ pub trait Iterator { /// passed collection. The collection is then returned, so the call chain /// can be continued. /// - /// This is useful when you already have a collection and wants to add + /// This is useful when you already have a collection and want to add /// the iterator items to it. /// /// This method is a convenience method to call [Extend::extend](trait.Extend.html), diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 03243e31348e9..0d00899c4de3f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -148,7 +148,6 @@ #![feature(const_option)] #![feature(const_option_ext)] #![feature(const_pin)] -#![feature(const_pointer_byte_offsets)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_is_null)] @@ -253,6 +252,7 @@ #![feature(try_blocks)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] +#![feature(with_negative_coherence)] // tidy-alphabetical-end // // Target features: diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index a79a204e2c6a0..7ef84b0f5b540 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -909,6 +909,10 @@ pub fn take(dest: &mut T) -> T { #[rustc_const_unstable(feature = "const_replace", issue = "83164")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_replace")] pub const fn replace(dest: &mut T, src: T) -> T { + // It may be tempting to use `swap` to avoid `unsafe` here. Don't! + // The compiler optimizes the implementation below to two `memcpy`s + // while `swap` would require at least three. See PR#83022 for details. + // SAFETY: We read from `dest` but directly write `src` into it afterwards, // such that the old value is not duplicated. Nothing is dropped and // nothing here can panic. diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index a6c1adfac6551..2a0b31404f035 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -791,7 +791,7 @@ impl u8 { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphanumeric(&self) -> bool { - matches!(*self, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z') + matches!(*self, b'0'..=b'9') | matches!(*self, b'A'..=b'Z') | matches!(*self, b'a'..=b'z') } /// Checks if the value is an ASCII decimal digit: @@ -894,7 +894,7 @@ impl u8 { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_hexdigit(&self) -> bool { - matches!(*self, b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f') + matches!(*self, b'0'..=b'9') | matches!(*self, b'A'..=b'F') | matches!(*self, b'a'..=b'f') } /// Checks if the value is an ASCII punctuation character: @@ -932,7 +932,10 @@ impl u8 { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_punctuation(&self) -> bool { - matches!(*self, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~') + matches!(*self, b'!'..=b'/') + | matches!(*self, b':'..=b'@') + | matches!(*self, b'['..=b'`') + | matches!(*self, b'{'..=b'~') } /// Checks if the value is an ASCII graphic character: diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index e01a893a06853..cd5ca988f7e03 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -29,7 +29,7 @@ pub enum CoroutineState { /// The trait implemented by builtin coroutine types. /// -/// Coroutines, also commonly referred to as coroutines, are currently an +/// Coroutines are currently an /// experimental language feature in Rust. Added in [RFC 2033] coroutines are /// currently intended to primarily provide a building block for async/await /// syntax but will likely extend to also providing an ergonomic definition for diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a3e4f0fb90abe..97f936fbd9ea4 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -480,8 +480,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. @@ -560,8 +561,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_offset(self, count: isize) -> Self { self.cast::().wrapping_offset(count).with_metadata_of(self) } @@ -726,8 +728,9 @@ impl *const T { /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from(self, origin: *const U) -> isize { // SAFETY: the caller must uphold the safety contract for `offset_from`. @@ -952,8 +955,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. @@ -1045,8 +1049,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. @@ -1125,8 +1130,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_add(self, count: usize) -> Self { self.cast::().wrapping_add(count).with_metadata_of(self) } @@ -1203,8 +1209,9 @@ impl *const T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_sub(self, count: usize) -> Self { self.cast::().wrapping_sub(count).with_metadata_of(self) } @@ -1372,7 +1379,6 @@ impl *const T { /// /// ``` /// #![feature(pointer_is_aligned)] - /// #![feature(pointer_byte_offsets)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1494,7 +1500,6 @@ impl *const T { /// /// ``` /// #![feature(pointer_is_aligned)] - /// #![feature(pointer_byte_offsets)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 109c286929b4e..64695d63f0c9a 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -495,8 +495,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. @@ -574,8 +575,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_offset(self, count: isize) -> Self { self.cast::().wrapping_offset(count).with_metadata_of(self) } @@ -898,8 +900,9 @@ impl *mut T { /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from(self, origin: *const U) -> isize { // SAFETY: the caller must uphold the safety contract for `offset_from`. @@ -1053,8 +1056,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. @@ -1146,8 +1150,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. @@ -1226,8 +1231,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_add(self, count: usize) -> Self { self.cast::().wrapping_add(count).with_metadata_of(self) } @@ -1304,8 +1310,9 @@ impl *mut T { /// leaving the metadata untouched. #[must_use] #[inline(always)] - #[unstable(feature = "pointer_byte_offsets", issue = "96283")] - #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")] + #[stable(feature = "pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(set_ptr_value)] pub const fn wrapping_byte_sub(self, count: usize) -> Self { self.cast::().wrapping_sub(count).with_metadata_of(self) } @@ -1639,7 +1646,6 @@ impl *mut T { /// /// ``` /// #![feature(pointer_is_aligned)] - /// #![feature(pointer_byte_offsets)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1763,7 +1769,6 @@ impl *mut T { /// /// ``` /// #![feature(pointer_is_aligned)] - /// #![feature(pointer_byte_offsets)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 280d2219b9e98..df7b34ce73b42 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -16,7 +16,6 @@ #![feature(const_heap)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] -#![feature(const_pointer_byte_offsets)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] @@ -87,7 +86,6 @@ #![feature(const_waker)] #![feature(never_type)] #![feature(unwrap_infallible)] -#![feature(pointer_byte_offsets)] #![feature(pointer_is_aligned)] #![feature(portable_simd)] #![feature(ptr_metadata)] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index bca5d859b6659..aa9a2482d2d92 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -317,6 +317,7 @@ pub use self::stdio::set_output_capture; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; #[unstable(feature = "print_internals", issue = "none")] +#[doc(hidden)] pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index aaf20875129c4..f57c8d4e7e282 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -331,7 +331,6 @@ #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(pointer_byte_offsets)] #![feature(pointer_is_aligned)] #![feature(portable_simd)] #![feature(prelude_2024)] diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index 73c530786b2fe..3dbab4cc48643 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -134,9 +134,19 @@ mod imp { // OpenBSD requires this flag for stack mapping // otherwise the said mapping will fail as a no-op on most systems // and has a different meaning on FreeBSD - #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))] + #[cfg(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + ))] let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; - #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))] + #[cfg(not(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + )))] let flags = MAP_PRIVATE | MAP_ANON; let stackp = mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 7b26068c294fc..4097eb5549efe 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -545,6 +545,15 @@ impl Builder { scope_data.increment_num_running_threads(); } + let main = Box::new(main); + // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the + // lifetime change is justified. + #[cfg(bootstrap)] + let main = + unsafe { mem::transmute::, Box>(main) }; + #[cfg(not(bootstrap))] + let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; + Ok(JoinInner { // SAFETY: // @@ -559,14 +568,7 @@ impl Builder { // Similarly, the `sys` implementation must guarantee that no references to the closure // exist after the thread has terminated, which is signaled by `Thread::join` // returning. - native: unsafe { - imp::Thread::new( - stack_size, - mem::transmute::, Box>( - Box::new(main), - ), - )? - }, + native: unsafe { imp::Thread::new(stack_size, main)? }, thread: my_thread, packet: my_packet, }) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 96a0eb7558232..19f666d046398 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "fd-lock", "filetime", "hex", + "home", "ignore", "junction", "libc", @@ -350,6 +351,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +dependencies = [ + "winapi", +] + [[package]] name = "ignore" version = "0.4.18" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e236421b7f5f1..bc025f4448428 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -40,6 +40,7 @@ clap_complete = "4.4.3" cmake = "0.1.38" filetime = "0.2" hex = "0.4" +home = "0.5.4" ignore = "0.4.10" libc = "0.2" object = { version = "0.32.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 241ae16e5953d..04bf9723edd40 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -124,6 +124,13 @@ fn main() { if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") { cmd.arg("--remap-path-prefix").arg(&map); } + // The remap flags for Cargo registry sources need to be passed after the remapping for the + // Rust source code directory, to handle cases when $CARGO_HOME is inside the source directory. + if let Ok(maps) = env::var("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP") { + for map in maps.split('\t') { + cmd.arg("--remap-path-prefix").arg(map); + } + } // Force all crates compiled by this compiler to (a) be unstable and (b) // allow the `rustc_private` feature to link to other unstable crates diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index f5f80ba2a0b8b..dbbce6fe22047 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -70,7 +70,9 @@ fn main() { cmd.arg("--cfg=bootstrap"); } cmd.arg("-Zunstable-options"); + // #[cfg(bootstrap)] cmd.arg("--check-cfg=values(bootstrap)"); + // cmd.arg("--check-cfg=cfg(bootstrap)"); if verbose > 1 { eprintln!( diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 831a86940fb48..7fb67eea551ec 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -27,6 +27,7 @@ use crate::core::config::flags::Subcommand; use crate::core::config::TargetSelection; use crate::utils; use crate::utils::cache::{Interned, INTERNER}; +use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{ self, add_link_lib_path, dylib_path, dylib_path_var, output, t, up_to_date, }; @@ -629,7 +630,7 @@ impl Step for Miri { SourceType::InTree, &[], ); - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, host); + let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); cargo.add_rustc_lib_path(builder, compiler); @@ -808,8 +809,8 @@ impl Step for Clippy { let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); - #[allow(deprecated)] // Clippy reports errors if it blessed the outputs - if builder.config.try_run(&mut cargo).is_ok() { + // Clippy reports errors if it blessed the outputs + if builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()) { // The tests succeeded; nothing to do. return; } @@ -1566,10 +1567,12 @@ note: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--coverage-dump-path").arg(coverage_dump); } - if mode == "run-make" || mode == "run-coverage" { + if mode == "run-coverage" { + // The demangler doesn't need the current compiler, so we can avoid + // unnecessary rebuilds by using the bootstrap compiler instead. let rust_demangler = builder .ensure(tool::RustDemangler { - compiler, + compiler: compiler.with_stage(0), target: compiler.host, extra_features: Vec::new(), }) @@ -2999,7 +3002,10 @@ impl Step for CodegenCranelift { let triple = run.target.triple; let target_supported = if triple.contains("linux") { - triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x") + triple.contains("x86_64") + || triple.contains("aarch64") + || triple.contains("s390x") + || triple.contains("riscv64gc") } else if triple.contains("darwin") || triple.contains("windows") { triple.contains("x86_64") } else { @@ -3094,7 +3100,7 @@ impl Step for CodegenCranelift { .arg("testsuite.extended_sysroot"); cargo.args(builder.config.test_args()); - #[allow(deprecated)] - builder.config.try_run(&mut cargo.into()).unwrap(); + let mut cmd: Command = cargo.into(); + builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast()); } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 5702fa62d7ccc..d5f759ea159aa 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -8,6 +8,7 @@ use crate::core::build_steps::toolstate::ToolState; use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; +use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{add_dylib_path, exe, t}; use crate::Compiler; use crate::Mode; @@ -108,8 +109,8 @@ impl Step for ToolBuild { ); let mut cargo = Command::from(cargo); - #[allow(deprecated)] // we check this in `is_optional_tool` in a second - let is_expected = builder.config.try_run(&mut cargo).is_ok(); + // we check this in `is_optional_tool` in a second + let is_expected = builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()); builder.save_toolstate( tool, diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 039a87e760d70..36653dcfb206a 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2,7 +2,7 @@ use std::any::{type_name, Any}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::env; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Write}; use std::fs::{self, File}; use std::hash::Hash; @@ -1401,19 +1401,28 @@ impl<'a> Builder<'a> { rustflags.arg("-Zunstable-options"); } - // Enable cfg checking of cargo features for everything but std and also enable cfg - // checking of names and values. + // #[cfg(bootstrap)] + let use_new_check_cfg_syntax = self.local_rebuild; + + // Enable compile-time checking of `cfg` names, values and Cargo `features`. // // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like // backtrace, core_simd, std_float, ...), those dependencies have their own // features but cargo isn't involved in the #[path] process and so cannot pass the // complete list of features, so for that reason we don't enable checking of // features for std crates. - cargo.arg(if mode != Mode::Std { - "-Zcheck-cfg=names,values,output,features" + if use_new_check_cfg_syntax { + cargo.arg("-Zcheck-cfg"); + if mode == Mode::Std { + rustflags.arg("--check-cfg=cfg(feature,values(any()))"); + } } else { - "-Zcheck-cfg=names,values,output" - }); + cargo.arg(if mode != Mode::Std { + "-Zcheck-cfg=names,values,output,features" + } else { + "-Zcheck-cfg=names,values,output" + }); + } // Add extra cfg not defined in/by rustc // @@ -1433,7 +1442,12 @@ impl<'a> Builder<'a> { .collect::(), None => String::new(), }; - rustflags.arg(&format!("--check-cfg=values({name}{values})")); + if use_new_check_cfg_syntax { + let values = values.strip_prefix(",").unwrap_or(&values); // remove the first `,` + rustflags.arg(&format!("--check-cfg=cfg({name},values({values}))")); + } else { + rustflags.arg(&format!("--check-cfg=values({name}{values})")); + } } } @@ -1449,7 +1463,11 @@ impl<'a> Builder<'a> { // We also declare that the flag is expected, which we need to do to not // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); - hostflags.arg("--check-cfg=values(bootstrap)"); + if use_new_check_cfg_syntax { + hostflags.arg("--check-cfg=cfg(bootstrap)"); + } else { + hostflags.arg("--check-cfg=values(bootstrap)"); + } // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -1767,6 +1785,20 @@ impl<'a> Builder<'a> { cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); } + if self.config.rust_remap_debuginfo { + // FIXME: handle vendored sources + let registry_src = t!(home::cargo_home()).join("registry").join("src"); + let mut env_var = OsString::new(); + for entry in t!(std::fs::read_dir(registry_src)) { + if !env_var.is_empty() { + env_var.push("\t"); + } + env_var.push(t!(entry).path()); + env_var.push("=/rust/deps"); + } + cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); + } + // Enable usage of unstable features cargo.env("RUSTC_BOOTSTRAP", "1"); self.add_rust_test_threads(&mut cargo); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 705c27bb92233..8dd1a698dfa8e 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -23,11 +23,12 @@ use std::fmt::Display; use std::fs::{self, File}; use std::io; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, Output, Stdio}; use std::str; use build_helper::ci::{gha, CiEnv}; use build_helper::exit; +use build_helper::util::fail; use filetime::FileTime; use once_cell::sync::OnceCell; use termcolor::{ColorChoice, StandardStream, WriteColor}; @@ -39,10 +40,8 @@ use crate::core::config::flags; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, - try_run_suppressed, -}; +use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, OutputMode}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; mod core; mod utils; @@ -580,15 +579,15 @@ impl Build { } // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). - #[allow(deprecated)] // diff-index reports the modifications through the exit status - let has_local_modifications = self - .config - .try_run( + // diff-index reports the modifications through the exit status + let has_local_modifications = !self.run_cmd( + BootstrapCommand::from( Command::new("git") .args(&["diff-index", "--quiet", "HEAD"]) .current_dir(&absolute_path), ) - .is_err(); + .allow_failure(), + ); if has_local_modifications { self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path)); } @@ -921,55 +920,103 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { - if self.config.dry_run() { - return; - } - self.verbose(&format!("running: {cmd:?}")); - run(cmd, self.is_verbose()) + self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode( + match self.is_verbose() { + true => OutputMode::PrintAll, + false => OutputMode::PrintOutput, + }, + )); + } + + /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. + pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { + self.run_cmd(BootstrapCommand::from(cmd).delay_failure().output_mode( + match self.is_verbose() { + true => OutputMode::PrintAll, + false => OutputMode::PrintOutput, + }, + )) } /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { - if self.config.dry_run() { - return; - } - self.verbose(&format!("running: {cmd:?}")); - run_suppressed(cmd) + self.run_cmd( + BootstrapCommand::from(cmd).fail_fast().output_mode(OutputMode::SuppressOnSuccess), + ); } /// Runs a command, printing out nice contextual information if it fails. /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn run_quiet_delaying_failure(&self, cmd: &mut Command) -> bool { + self.run_cmd( + BootstrapCommand::from(cmd).delay_failure().output_mode(OutputMode::SuppressOnSuccess), + ) + } + + /// A centralized function for running commands that do not return output. + pub(crate) fn run_cmd<'a, C: Into>>(&self, cmd: C) -> bool { if self.config.dry_run() { return true; } - if !self.fail_fast { - self.verbose(&format!("running: {cmd:?}")); - if !try_run_suppressed(cmd) { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{cmd:?}")); - return false; + + let command = cmd.into(); + self.verbose(&format!("running: {command:?}")); + + let (output, print_error) = match command.output_mode { + mode @ (OutputMode::PrintAll | OutputMode::PrintOutput) => ( + command.command.status().map(|status| Output { + status, + stdout: Vec::new(), + stderr: Vec::new(), + }), + matches!(mode, OutputMode::PrintAll), + ), + OutputMode::SuppressOnSuccess => (command.command.output(), true), + }; + + let output = match output { + Ok(output) => output, + Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", command, e)), + }; + let result = if !output.status.success() { + if print_error { + println!( + "\n\ncommand did not execute successfully: {:?}\n\ + expected success, got: {}\n\n\ + stdout ----\n{}\n\ + stderr ----\n{}\n\n", + command.command, + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); } + Err(()) } else { - self.run_quiet(cmd); - } - true - } + Ok(()) + }; - /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. - pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { - if !self.fail_fast { - #[allow(deprecated)] // can't use Build::try_run, that's us - if self.config.try_run(cmd).is_err() { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{cmd:?}")); - return false; + match result { + Ok(_) => true, + Err(_) => { + match command.failure_behavior { + BehaviorOnFailure::DelayFail => { + if self.fail_fast { + exit!(1); + } + + let mut failures = self.delayed_failures.borrow_mut(); + failures.push(format!("{command:?}")); + } + BehaviorOnFailure::Exit => { + exit!(1); + } + BehaviorOnFailure::Ignore => {} + } + false } - } else { - self.run(cmd); } - true } pub fn is_verbose_than(&self, level: usize) -> bool { diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs new file mode 100644 index 0000000000000..9c1c21cd95894 --- /dev/null +++ b/src/bootstrap/src/utils/exec.rs @@ -0,0 +1,60 @@ +use std::process::Command; + +/// What should be done when the command fails. +#[derive(Debug, Copy, Clone)] +pub enum BehaviorOnFailure { + /// Immediately stop bootstrap. + Exit, + /// Delay failure until the end of bootstrap invocation. + DelayFail, + /// Ignore the failure, the command can fail in an expected way. + Ignore, +} + +/// How should the output of the command be handled. +#[derive(Debug, Copy, Clone)] +pub enum OutputMode { + /// Print both the output (by inheriting stdout/stderr) and also the command itself, if it + /// fails. + PrintAll, + /// Print the output (by inheriting stdout/stderr). + PrintOutput, + /// Suppress the output if the command succeeds, otherwise print the output. + SuppressOnSuccess, +} + +/// Wrapper around `std::process::Command`. +#[derive(Debug)] +pub struct BootstrapCommand<'a> { + pub command: &'a mut Command, + pub failure_behavior: BehaviorOnFailure, + pub output_mode: OutputMode, +} + +impl<'a> BootstrapCommand<'a> { + pub fn delay_failure(self) -> Self { + Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } + } + + pub fn fail_fast(self) -> Self { + Self { failure_behavior: BehaviorOnFailure::Exit, ..self } + } + + pub fn allow_failure(self) -> Self { + Self { failure_behavior: BehaviorOnFailure::Ignore, ..self } + } + + pub fn output_mode(self, output_mode: OutputMode) -> Self { + Self { output_mode, ..self } + } +} + +impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { + fn from(command: &'a mut Command) -> Self { + Self { + command, + failure_behavior: BehaviorOnFailure::Exit, + output_mode: OutputMode::SuppressOnSuccess, + } + } +} diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index bb84b70d987f9..b58a1c2584259 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -3,7 +3,7 @@ //! Simple things like testing the various filesystem operations here and there, //! not a lot of interesting happenings here unfortunately. -use build_helper::util::{fail, try_run}; +use build_helper::util::fail; use std::env; use std::fs; use std::io; @@ -216,12 +216,6 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef>( } } -pub fn run(cmd: &mut Command, print_cmd_on_fail: bool) { - if try_run(cmd, print_cmd_on_fail).is_err() { - crate::exit!(1); - } -} - pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool { let status = match cmd.status() { Ok(status) => status, @@ -239,32 +233,6 @@ pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool { status.success() } -pub fn run_suppressed(cmd: &mut Command) { - if !try_run_suppressed(cmd) { - crate::exit!(1); - } -} - -pub fn try_run_suppressed(cmd: &mut Command) -> bool { - let output = match cmd.output() { - Ok(status) => status, - Err(e) => fail(&format!("failed to execute command: {cmd:?}\nerror: {e}")), - }; - if !output.status.success() { - println!( - "\n\ncommand did not execute successfully: {:?}\n\ - expected success, got: {}\n\n\ - stdout ----\n{}\n\ - stderr ----\n{}\n\n", - cmd, - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - } - output.status.success() -} - pub fn make(host: &str) -> PathBuf { if host.contains("dragonfly") || host.contains("freebsd") diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index 7dcb6a8286202..8ca22d00865b6 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod channel; pub(crate) mod dylib; +pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; #[cfg(feature = "build-metrics")] diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md index 57c717c182de4..a54abcb606ea7 100644 --- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md +++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md @@ -10,9 +10,15 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) Reference: -https://c-sky.github.io/ -https://gitlab.com/c-sky/ +- [CSKY ABI Manual](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1695027452256/T-HEAD_800_Series_ABI_Standards_Manual.pdf) +- [csky-linux-gnuabiv2-toolchain](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource/1356021/1619528643136/csky-linux-gnuabiv2-tools-x86_64-glibc-linux-4.9.56-20210423.tar.gz) +- [csky-linux-gnuabiv2-qemu](https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1689324918932/xuantie-qemu-x86_64-Ubuntu-18.04-20230714-0202.tar.gz) + +other links: + +- https://c-sky.github.io/ +- https://gitlab.com/c-sky/ ## Target maintainers diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index d9cf7ce30f936..38b655b75422b 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -145,3 +145,26 @@ in Clang's documentation is therefore an interesting read for anyone who wants to use PGO with Rust. [clang-pgo]: https://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization + +## Community Maintained Tools + +As an alternative to directly using the compiler for Profile-Guided Optimization, +you may choose to go with `cargo-pgo`, which has an intuitive command-line API +and saves you the trouble of doing all the manual work. You can read more about +it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo + +For the sake of completeness, here are the corresponding steps using `cargo-pgo`: + +```bash +# Install if you haven't already +cargo install cargo-pgo + +cargo pgo build +cargo pgo optimize +``` + +These steps will do the following just as before: + +1. Build an instrumented binary from the source code. +2. Run the instrumented binary to gather PGO profiles. +3. Use the gathered PGO profiles from the last step to build an optimized binary. diff --git a/src/doc/unstable-book/src/language-features/coroutines.md b/src/doc/unstable-book/src/language-features/coroutines.md index fb1ba791aefe1..f8e5a22fbd5c8 100644 --- a/src/doc/unstable-book/src/language-features/coroutines.md +++ b/src/doc/unstable-book/src/language-features/coroutines.md @@ -18,7 +18,7 @@ Coroutines are an extra-unstable feature in the compiler right now. Added in [RFC 2033] they're mostly intended right now as a information/constraint gathering phase. The intent is that experimentation can happen on the nightly compiler before actual stabilization. A further RFC will be required to -stabilize coroutines/coroutines and will likely contain at least a few small +stabilize coroutines and will likely contain at least a few small tweaks to the overall design. [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 38935b7d1bb11..0e01c100f9c14 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" arrayvec = { version = "0.7", default-features = false } askama = { version = "0.12", default-features = false, features = ["config"] } itertools = "0.10.1" +indexmap = "2" minifier = "0.2.3" once_cell = "1.10.0" regex = "1" diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 89796761126df..013814b1f7dca 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -26,6 +26,8 @@ use crate::clean::{ use crate::core::DocContext; use crate::formats::item_type::ItemType; +use super::Item; + /// Attempt to inline a definition into this AST. /// /// This function will fetch the definition specified, and if it is @@ -83,7 +85,7 @@ pub(crate) fn try_inline( Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::TypeAlias); build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TypeAliasItem(build_type_alias(cx, did)) + clean::TypeAliasItem(build_type_alias(cx, did, &mut ret)) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); @@ -281,11 +283,15 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { clean::Union { generics, fields } } -fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box { +fn build_type_alias( + cx: &mut DocContext<'_>, + did: DefId, + ret: &mut Vec, +) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); let ty = cx.tcx.type_of(did).instantiate_identity(); let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None); - let inner_type = clean_ty_alias_inner_type(ty, cx); + let inner_type = clean_ty_alias_inner_type(ty, cx, ret); Box::new(clean::TypeAlias { type_, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 125b1fecf18f3..f2447b877ca80 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -934,11 +934,16 @@ fn clean_ty_generics<'tcx>( fn clean_ty_alias_inner_type<'tcx>( ty: Ty<'tcx>, cx: &mut DocContext<'tcx>, + ret: &mut Vec, ) -> Option { let ty::Adt(adt_def, args) = ty.kind() else { return None; }; + if !adt_def.did().is_local() { + inline::build_impls(cx, adt_def.did(), None, ret); + } + Some(if adt_def.is_enum() { let variants: rustc_index::IndexVec<_, _> = adt_def .variants() @@ -946,6 +951,10 @@ fn clean_ty_alias_inner_type<'tcx>( .map(|variant| clean_variant_def_with_args(variant, args, cx)) .collect(); + if !adt_def.did().is_local() { + inline::record_extern_fqn(cx, adt_def.did(), ItemType::Enum); + } + TypeAliasInnerType::Enum { variants, is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(), @@ -961,8 +970,14 @@ fn clean_ty_alias_inner_type<'tcx>( clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect(); if adt_def.is_struct() { + if !adt_def.did().is_local() { + inline::record_extern_fqn(cx, adt_def.did(), ItemType::Struct); + } TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields } } else { + if !adt_def.did().is_local() { + inline::record_extern_fqn(cx, adt_def.did(), ItemType::Union); + } TypeAliasInnerType::Union { fields } } }) @@ -2744,14 +2759,24 @@ fn clean_maybe_renamed_item<'tcx>( } let ty = cx.tcx.type_of(def_id).instantiate_identity(); - let inner_type = clean_ty_alias_inner_type(ty, cx); - TypeAliasItem(Box::new(TypeAlias { - generics, - inner_type, - type_: rustdoc_ty, - item_type: Some(type_), - })) + let mut ret = Vec::new(); + let inner_type = clean_ty_alias_inner_type(ty, cx, &mut ret); + + ret.push(generate_item_with_correct_attrs( + cx, + TypeAliasItem(Box::new(TypeAlias { + generics, + inner_type, + type_: rustdoc_ty, + item_type: Some(type_), + })), + item.owner_id.def_id.to_def_id(), + name, + import_id, + renamed, + )); + return ret; } ItemKind::Enum(ref def, generics) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a7410144fd50..449aac4cfc8b7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -12,7 +12,7 @@ use thin_vec::ThinVec; use rustc_ast as ast; use rustc_ast_pretty::pprust; -use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; +use rustc_attr::{ConstStability, Deprecation, Since, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -585,14 +585,14 @@ impl Item { }) } - pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { + pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { match self.stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since), StabilityLevel::Unstable { .. } => None, } } - pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { + pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { match self.const_stability(tcx)?.level { StabilityLevel::Stable { since, .. } => Some(since), StabilityLevel::Unstable { .. } => None, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9066061f1a22e..94e557dcfdb54 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -23,6 +23,7 @@ use std::cell::RefCell; use std::mem; use std::rc::Rc; use std::sync::LazyLock; +use std::sync::{atomic::AtomicBool, Arc}; use crate::clean::inline::build_external_trait; use crate::clean::{self, ItemId}; @@ -198,6 +199,7 @@ pub(crate) fn create_config( .. }: RustdocOptions, RenderOptions { document_private, .. }: &RenderOptions, + using_internal_features: Arc, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); @@ -293,6 +295,7 @@ pub(crate) fn create_config( make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, + using_internal_features, expanded_args, } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index db69cf5752d88..97913345e8fb2 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -110,6 +110,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, + using_internal_features: Arc::default(), expanded_args: options.expanded_args.clone(), }; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4ccb5f2be346b..abff77253eadd 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -50,8 +50,8 @@ pub(crate) struct Cache { /// Unlike 'paths', this mapping ignores any renames that occur /// due to 'use' statements. /// - /// This map is used when writing out the special 'implementors' - /// javascript file. By using the exact path that the type + /// This map is used when writing out the `impl.trait` and `impl.type` + /// javascript files. By using the exact path that the type /// is declared with, we ensure that each path will be identical /// to the path used if the corresponding type is inlined. By /// doing this, we can detect duplicate impls on a trait page, and only display diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index e37d16f5bd0b8..def3a90c8e812 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -180,6 +180,9 @@ impl ItemType { pub(crate) fn is_method(&self) -> bool { matches!(*self, ItemType::Method | ItemType::TyMethod) } + pub(crate) fn is_adt(&self) -> bool { + matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum) + } } impl fmt::Display for ItemType { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index bf8d1a80337e8..6da9e45a1da40 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -7,10 +7,8 @@ use std::sync::mpsc::{channel, Receiver}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::source_map::FileName; use rustc_span::{sym, Symbol}; @@ -25,13 +23,13 @@ use super::{ AllTypes, LinkFromSrc, StylePath, }; use crate::clean::utils::has_doc_flag; -use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem}; +use crate::clean::{self, types::ExternalLocation, ExternalCrate}; use crate::config::{ModuleSorting, RenderOptions}; use crate::docfs::{DocFS, PathError}; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::{self, FormatRenderer}; +use crate::formats::FormatRenderer; use crate::html::escape::Escape; use crate::html::format::{join_with_double_colon, Buffer}; use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; @@ -150,53 +148,6 @@ impl SharedContext<'_> { pub(crate) fn edition(&self) -> Edition { self.tcx.sess.edition() } - - /// Returns a list of impls on the given type, and, if it's a type alias, - /// other types that it aliases. - pub(crate) fn all_impls_for_item<'a>( - &'a self, - it: &clean::Item, - did: DefId, - ) -> Vec<&'a formats::Impl> { - let tcx = self.tcx; - let cache = &self.cache; - let mut saw_impls = FxHashSet::default(); - let mut v: Vec<&formats::Impl> = cache - .impls - .get(&did) - .map(Vec::as_slice) - .unwrap_or(&[]) - .iter() - .filter(|i| saw_impls.insert(i.def_id())) - .collect(); - if let TypeAliasItem(ait) = &*it.kind && - let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) && - let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) && - let Some(av) = cache.impls.get(&aliased_type_defid) && - let Some(alias_def_id) = it.item_id.as_def_id() - { - // This branch of the compiler compares types structually, but does - // not check trait bounds. That's probably fine, since type aliases - // don't normally constrain on them anyway. - // https://github.com/rust-lang/rust/issues/21903 - // - // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification. - // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress. - let aliased_ty = tcx.type_of(alias_def_id).skip_binder(); - let reject_cx = DeepRejectCtxt { - treat_obligation_params: TreatParams::AsCandidateKey, - }; - v.extend(av.iter().filter(|impl_| { - if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() { - reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder()) - && saw_impls.insert(impl_def_id) - } else { - false - } - })); - } - v - } } impl<'tcx> Context<'tcx> { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 89e29d8b59b2f..d908643360840 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,7 +48,7 @@ use std::str; use std::string::ToString; use askama::Template; -use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; +use rustc_attr::{ConstStability, Deprecation, Since, StabilityLevel, CURRENT_RUSTC_VERSION}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -911,13 +911,17 @@ fn assoc_method( /// consequence of the above rules. fn render_stability_since_raw_with_extra( w: &mut Buffer, - ver: Option, + ver: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, + containing_ver: Option, + containing_const_ver: Option, extra_class: &str, ) -> bool { - let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver); + let stable_version = if ver != containing_ver && let Some(ver) = &ver { + since_to_string(ver) + } else { + None + }; let mut title = String::new(); let mut stability = String::new(); @@ -931,7 +935,8 @@ fn render_stability_since_raw_with_extra( Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) if Some(since) != containing_const_ver => { - Some((format!("const since {since}"), format!("const: {since}"))) + since_to_string(&since) + .map(|since| (format!("const since {since}"), format!("const: {since}"))) } Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { let unstable = if let Some(n) = issue { @@ -971,13 +976,21 @@ fn render_stability_since_raw_with_extra( !stability.is_empty() } +fn since_to_string(since: &Since) -> Option { + match since { + Since::Version(since) => Some(since.to_string()), + Since::Current => Some(CURRENT_RUSTC_VERSION.to_owned()), + Since::Err => None, + } +} + #[inline] fn render_stability_since_raw( w: &mut Buffer, - ver: Option, + ver: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, + containing_ver: Option, + containing_const_ver: Option, ) -> bool { render_stability_since_raw_with_extra( w, @@ -1132,13 +1145,13 @@ pub(crate) fn render_all_impls( fn render_assoc_items<'a, 'cx: 'a>( cx: &'a mut Context<'cx>, containing_item: &'a clean::Item, - did: DefId, + it: DefId, what: AssocItemRender<'a>, ) -> impl fmt::Display + 'a + Captures<'cx> { let mut derefs = DefIdSet::default(); - derefs.insert(did); + derefs.insert(it); display_fn(move |f| { - render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs); + render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs); Ok(()) }) } @@ -1147,17 +1160,15 @@ fn render_assoc_items_inner( mut w: &mut dyn fmt::Write, cx: &mut Context<'_>, containing_item: &clean::Item, - did: DefId, + it: DefId, what: AssocItemRender<'_>, derefs: &mut DefIdSet, ) { info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); - let v = shared.all_impls_for_item(containing_item, did); - let v = v.as_slice(); - let (non_trait, traits): (Vec<&Impl>, _) = - v.iter().partition(|i| i.inner_impl().trait_.is_none()); - let mut saw_impls = FxHashSet::default(); + let cache = &shared.cache; + let Some(v) = cache.impls.get(&it) else { return }; + let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { let mut tmp_buf = Buffer::html(); let (render_mode, id, class_html) = match what { @@ -1186,9 +1197,6 @@ fn render_assoc_items_inner( }; let mut impls_buf = Buffer::html(); for i in &non_trait { - if !saw_impls.insert(i.def_id()) { - continue; - } render_impl( &mut impls_buf, cx, @@ -1234,10 +1242,8 @@ fn render_assoc_items_inner( let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete - .into_iter() - .filter(|t| saw_impls.insert(t.def_id())) - .partition(|t| t.inner_impl().kind.is_blanket()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = + concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f6432dc61ae06..fdf4556906172 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1066,6 +1066,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } } + // [RUSTDOCIMPL] trait.impl + // // Include implementors in crates that depend on the current crate. // // This is complicated by the way rustdoc is invoked, which is basically @@ -1101,7 +1103,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // ``` // // Basically, we want `C::Baz` and `A::Foo` to show the same set of - // impls, which is easier if they both treat `/implementors/A/trait.Foo.js` + // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js` // as the Single Source of Truth. // // We also want the `impl Baz for Quux` to be written to @@ -1110,7 +1112,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // because that'll load faster, and it's better for SEO. And we don't want // the same impl to show up twice on the same page. // - // To make this work, the implementors JS file has a structure kinda + // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda // like this: // // ```js @@ -1127,7 +1129,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // So C's HTML will have something like this: // // ```html - // // ``` // @@ -1137,7 +1139,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // [JSONP]: https://en.wikipedia.org/wiki/JSONP let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") .take(cx.current.len()) - .chain(std::iter::once("implementors")) + .chain(std::iter::once("trait.impl")) .collect(); if let Some(did) = it.item_id.as_def_id() && let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } && @@ -1319,6 +1321,102 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c // we need #14072 to make sense of the generics. write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); write!(w, "{}", document_type_layout(cx, def_id)); + + // [RUSTDOCIMPL] type.impl + // + // Include type definitions from the alias target type. + // + // Earlier versions of this code worked by having `render_assoc_items` + // include this data directly. That generates *O*`(types*impls)` of HTML + // text, and some real crates have a lot of types and impls. + // + // To create the same UX without generating half a gigabyte of HTML for a + // crate that only contains 20 megabytes of actual documentation[^115718], + // rustdoc stashes these type-alias-inlined docs in a [JSONP] + // "database-lite". The file itself is generated in `write_shared.rs`, + // and hooks into functions provided by `main.js`. + // + // The format of `trait.impl` and `type.impl` JS files are superficially + // similar. Each line, except the JSONP wrapper itself, belongs to a crate, + // and they are otherwise separate (rustdoc should be idempotent). The + // "meat" of the file is HTML strings, so the frontend code is very simple. + // Links are relative to the doc root, though, so the frontend needs to fix + // that up, and inlined docs can reuse these files. + // + // However, there are a few differences, caused by the sophisticated + // features that type aliases have. Consider this crate graph: + // + // ```text + // --------------------------------- + // | crate A: struct Foo | + // | type Bar = Foo | + // | impl X for Foo | + // | impl Y for Foo | + // --------------------------------- + // | + // ---------------------------------- + // | crate B: type Baz = A::Foo | + // | type Xyy = A::Foo | + // | impl Z for Xyy | + // ---------------------------------- + // ``` + // + // The type.impl/A/struct.Foo.js JS file has a structure kinda like this: + // + // ```js + // JSONP({ + // "A": [["impl Y for Foo", "Y", "A::Bar"]], + // "B": [["impl X for Foo", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]], + // }); + // ``` + // + // When the type.impl file is loaded, only the current crate's docs are + // actually used. The main reason to bundle them together is that there's + // enough duplication in them for DEFLATE to remove the redundancy. + // + // The contents of a crate are a list of impl blocks, themselves + // represented as lists. The first item in the sublist is the HTML block, + // the second item is the name of the trait (which goes in the sidebar), + // and all others are the names of type aliases that successfully match. + // + // This way: + // + // - There's no need to generate these files for types that have no aliases + // in the current crate. If a dependent crate makes a type alias, it'll + // take care of generating its own docs. + // - There's no need to reimplement parts of the type checker in + // JavaScript. The Rust backend does the checking, and includes its + // results in the file. + // - Docs defined directly on the type alias are dropped directly in the + // HTML by `render_assoc_items`, and are accessible without JavaScript. + // The JSONP file will not list impl items that are known to be part + // of the main HTML file already. + // + // [JSONP]: https://en.wikipedia.org/wiki/JSONP + // [^115718]: https://github.com/rust-lang/rust/issues/115718 + let cloned_shared = Rc::clone(&cx.shared); + let cache = &cloned_shared.cache; + if let Some(target_did) = t.type_.def_id(cache) && + let get_extern = { || cache.external_paths.get(&target_did) } && + let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) && + target_type.is_adt() && // primitives cannot be inlined + let Some(self_did) = it.item_id.as_def_id() && + let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } && + let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) + { + let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") + .take(cx.current.len()) + .chain(std::iter::once("type.impl")) + .collect(); + js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied()); + js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap())); + let self_path = self_fqp.iter().map(Symbol::as_str).collect::>().join("::"); + write!( + w, + "", + src = js_src_path.finish(), + ); + } } fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index fb429f237e3db..4e8d88c55b64f 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -38,18 +38,19 @@ pub(crate) struct LinkBlock<'a> { /// as well as the link to it, e.g. `#implementations`. /// Will be rendered inside an `

` tag heading: Link<'a>, + class: &'static str, links: Vec>, /// Render the heading even if there are no links force_render: bool, } impl<'a> LinkBlock<'a> { - pub fn new(heading: Link<'a>, links: Vec>) -> Self { - Self { heading, links, force_render: false } + pub fn new(heading: Link<'a>, class: &'static str, links: Vec>) -> Self { + Self { heading, links, class, force_render: false } } - pub fn forced(heading: Link<'a>) -> Self { - Self { heading, links: vec![], force_render: true } + pub fn forced(heading: Link<'a>, class: &'static str) -> Self { + Self { heading, links: vec![], class, force_render: true } } pub fn should_render(&self) -> bool { @@ -157,7 +158,7 @@ fn sidebar_struct<'a>( }; let mut items = vec![]; if let Some(name) = field_name { - items.push(LinkBlock::new(Link::new("fields", name), fields)); + items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields)); } sidebar_assoc_items(cx, it, &mut items); items @@ -214,12 +215,15 @@ fn sidebar_trait<'a>( ("foreign-impls", "Implementations on Foreign Types", foreign_impls), ] .into_iter() - .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items)) + .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)) .collect(); sidebar_assoc_items(cx, it, &mut blocks); - blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"))); + blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); if t.is_auto(cx.tcx()) { - blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors"))); + blocks.push(LinkBlock::forced( + Link::new("synthetic-implementors", "Auto Implementors"), + "impl-auto", + )); } blocks } @@ -245,7 +249,7 @@ fn sidebar_type_alias<'a>( ) -> Vec> { let mut items = vec![]; if let Some(inner_type) = &t.inner_type { - items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"))); + items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { let mut variants = variants @@ -256,12 +260,12 @@ fn sidebar_type_alias<'a>( .collect::>(); variants.sort_unstable(); - items.push(LinkBlock::new(Link::new("variants", "Variants"), variants)); + items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); } clean::TypeAliasInnerType::Union { fields } | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => { let fields = get_struct_fields_name(fields); - items.push(LinkBlock::new(Link::new("fields", "Fields"), fields)); + items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields)); } } } @@ -275,7 +279,7 @@ fn sidebar_union<'a>( u: &'a clean::Union, ) -> Vec> { let fields = get_struct_fields_name(&u.fields); - let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)]; + let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)]; sidebar_assoc_items(cx, it, &mut items); items } @@ -287,12 +291,11 @@ fn sidebar_assoc_items<'a>( links: &mut Vec>, ) { let did = it.item_id.expect_def_id(); - let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id()); - let v = v.as_slice(); + let cache = cx.cache(); let mut assoc_consts = Vec::new(); let mut methods = Vec::new(); - if !v.is_empty() { + if let Some(v) = cache.impls.get(&did) { let mut used_links = FxHashSet::default(); let mut id_map = IdMap::new(); @@ -328,7 +331,7 @@ fn sidebar_assoc_items<'a>( cx, &mut deref_methods, impl_, - v.iter().copied(), + v, &mut derefs, &mut used_links, ); @@ -341,12 +344,16 @@ fn sidebar_assoc_items<'a>( sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl) } else { - std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![])) + std::array::from_fn(|_| LinkBlock::new(Link::empty(), "", vec![])) }; let mut blocks = vec![ - LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts), - LinkBlock::new(Link::new("implementations", "Methods"), methods), + LinkBlock::new( + Link::new("implementations", "Associated Constants"), + "associatedconstant", + assoc_consts, + ), + LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), ]; blocks.append(&mut deref_methods); blocks.extend([concrete, synthetic, blanket]); @@ -358,7 +365,7 @@ fn sidebar_deref_methods<'a>( cx: &'a Context<'_>, out: &mut Vec>, impl_: &Impl, - v: impl Iterator, + v: &[Impl], derefs: &mut DefIdSet, used_links: &mut FxHashSet, ) { @@ -383,7 +390,7 @@ fn sidebar_deref_methods<'a>( // Avoid infinite cycles return; } - let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); let inner_impl = target .def_id(c) .or_else(|| { @@ -415,7 +422,7 @@ fn sidebar_deref_methods<'a>( ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); - out.push(LinkBlock::new(Link::new(id, title), ret)); + out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret)); } } @@ -434,7 +441,7 @@ fn sidebar_deref_methods<'a>( cx, out, target_deref_impl, - target_impls.iter(), + target_impls, derefs, used_links, ); @@ -454,7 +461,7 @@ fn sidebar_enum<'a>( .collect::>(); variants.sort_unstable(); - let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)]; + let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)]; sidebar_assoc_items(cx, it, &mut items); items } @@ -468,7 +475,7 @@ pub(crate) fn sidebar_module_like( .filter(|sec| item_sections_in_use.contains(sec)) .map(|sec| Link::new(sec.id(), sec.name())) .collect(); - LinkBlock::new(Link::empty(), item_sections) + LinkBlock::new(Link::empty(), "", item_sections) } fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { @@ -529,12 +536,21 @@ fn sidebar_render_assoc_items( let synthetic = format_impls(synthetic, id_map); let blanket = format_impls(blanket_impl, id_map); [ - LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete), + LinkBlock::new( + Link::new("trait-implementations", "Trait Implementations"), + "trait-implementation", + concrete, + ), LinkBlock::new( Link::new("synthetic-implementations", "Auto Trait Implementations"), + "synthetic-implementation", synthetic, ), - LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket), + LinkBlock::new( + Link::new("blanket-implementations", "Blanket Implementations"), + "blanket-implementation", + blanket, + ), ] } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index e68d5ab2fbdb5..3e58dd96ed92a 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -5,18 +5,28 @@ use std::io::{self, BufReader}; use std::path::{Component, Path}; use std::rc::{Rc, Weak}; +use indexmap::IndexMap; use itertools::Itertools; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_span::def_id::DefId; +use rustc_span::Symbol; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; use super::{collect_paths_for_type, ensure_trailing_slash, Context}; -use crate::clean::Crate; +use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::formats::{Impl, RenderMode}; +use crate::html::format::Buffer; +use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; +use crate::visit::DocVisitor; use crate::{try_err, try_none}; /// Rustdoc writes out two kinds of shared files: @@ -361,9 +371,264 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; } } + let cloned_shared = Rc::clone(&cx.shared); + let cache = &cloned_shared.cache; + + // Collect the list of aliased types and their aliases. + // + // + // The clean AST has type aliases that point at their types, but + // this visitor works to reverse that: `aliased_types` is a map + // from target to the aliases that reference it, and each one + // will generate one file. + struct TypeImplCollector<'cx, 'cache> { + // Map from DefId-of-aliased-type to its data. + aliased_types: IndexMap>, + visited_aliases: FxHashSet, + cache: &'cache Cache, + cx: &'cache mut Context<'cx>, + } + // Data for an aliased type. + // + // In the final file, the format will be roughly: + // + // ```json + // // type.impl/CRATE/TYPENAME.js + // JSONP( + // "CRATE": [ + // ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...], + // ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...], + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType + // ... + // ] + // ) + // ``` + struct AliasedType<'cache> { + // This is used to generate the actual filename of this aliased type. + target_fqp: &'cache [Symbol], + target_type: ItemType, + // This is the data stored inside the file. + // ItemId is used to deduplicate impls. + impl_: IndexMap>, + } + // The `impl_` contains data that's used to figure out if an alias will work, + // and to generate the HTML at the end. + // + // The `type_aliases` list is built up with each type alias that matches. + struct AliasedTypeImpl<'cache> { + impl_: &'cache Impl, + type_aliases: Vec<(&'cache [Symbol], Item)>, + } + impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> { + fn visit_item(&mut self, it: &Item) { + self.visit_item_recur(it); + let cache = self.cache; + let ItemKind::TypeAliasItem(ref t) = *it.kind else { return }; + let Some(self_did) = it.item_id.as_def_id() else { return }; + if !self.visited_aliases.insert(self_did) { + return; + } + let Some(target_did) = t.type_.def_id(cache) else { return }; + let get_extern = { || cache.external_paths.get(&target_did) }; + let Some(&(ref target_fqp, target_type)) = + cache.paths.get(&target_did).or_else(get_extern) + else { + return; + }; + let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| { + let impl_ = cache + .impls + .get(&target_did) + .map(|v| &v[..]) + .unwrap_or_default() + .iter() + .map(|impl_| { + ( + impl_.impl_item.item_id, + AliasedTypeImpl { impl_, type_aliases: Vec::new() }, + ) + }) + .collect(); + AliasedType { target_fqp: &target_fqp[..], target_type, impl_ } + }); + let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }; + let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else { + return; + }; + let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder(); + // Exclude impls that are directly on this type. They're already in the HTML. + // Some inlining scenarios can cause there to be two versions of the same + // impl: one on the type alias and one on the underlying target type. + let mut seen_impls: FxHashSet = cache + .impls + .get(&self_did) + .map(|s| &s[..]) + .unwrap_or_default() + .iter() + .map(|i| i.impl_item.item_id) + .collect(); + for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ { + // Only include this impl if it actually unifies with this alias. + // Synthetic impls are not included; those are also included in the HTML. + // + // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this + // to use type unification. + // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress. + let Some(impl_did) = impl_item_id.as_def_id() else { continue }; + let for_ty = self.cx.tcx().type_of(impl_did).skip_binder(); + let reject_cx = + DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; + if !reject_cx.types_may_unify(aliased_ty, for_ty) { + continue; + } + // Avoid duplicates + if !seen_impls.insert(*impl_item_id) { + continue; + } + // This impl was not found in the set of rejected impls + aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone())); + } + } + } + let mut type_impl_collector = TypeImplCollector { + aliased_types: IndexMap::default(), + visited_aliases: FxHashSet::default(), + cache, + cx, + }; + DocVisitor::visit_crate(&mut type_impl_collector, &krate); + // Final serialized form of the alias impl + struct AliasSerializableImpl { + text: String, + trait_: Option, + aliases: Vec, + } + impl Serialize for AliasSerializableImpl { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.text)?; + if let Some(trait_) = &self.trait_ { + seq.serialize_element(trait_)?; + } else { + seq.serialize_element(&0)?; + } + for type_ in &self.aliases { + seq.serialize_element(type_)?; + } + seq.end() + } + } + let cx = type_impl_collector.cx; + let dst = cx.dst.join("type.impl"); + let aliased_types = type_impl_collector.aliased_types; + for aliased_type in aliased_types.values() { + let impls = aliased_type + .impl_ + .values() + .flat_map(|AliasedTypeImpl { impl_, type_aliases }| { + let mut ret = Vec::new(); + let trait_ = impl_ + .inner_impl() + .trait_ + .as_ref() + .map(|trait_| format!("{:#}", trait_.print(cx))); + // render_impl will filter out "impossible-to-call" methods + // to make that functionality work here, it needs to be called with + // each type alias, and if it gives a different result, split the impl + for &(type_alias_fqp, ref type_alias_item) in type_aliases { + let mut buf = Buffer::html(); + cx.id_map = Default::default(); + cx.deref_id_map = Default::default(); + let target_did = impl_ + .inner_impl() + .trait_ + .as_ref() + .map(|trait_| trait_.def_id()) + .or_else(|| impl_.inner_impl().for_.def_id(cache)); + let provided_methods; + let assoc_link = if let Some(target_did) = target_did { + provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx()); + AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods) + } else { + AssocItemLink::Anchor(None) + }; + super::render_impl( + &mut buf, + cx, + *impl_, + &type_alias_item, + assoc_link, + RenderMode::Normal, + None, + &[], + ImplRenderingParameters { + show_def_docs: true, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, + ); + let text = buf.into_inner(); + let type_alias_fqp = (*type_alias_fqp).iter().join("::"); + if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) { + ret.last_mut() + .expect("already established that ret.last() is Some()") + .aliases + .push(type_alias_fqp); + } else { + ret.push(AliasSerializableImpl { + text, + trait_: trait_.clone(), + aliases: vec![type_alias_fqp], + }) + } + } + ret + }) + .collect::>(); + let impls = format!( + r#""{}":{}"#, + krate.name(cx.tcx()), + serde_json::to_string(&impls).expect("failed serde conversion"), + ); + + let mut mydst = dst.clone(); + for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] { + mydst.push(part.to_string()); + } + cx.shared.ensure_dir(&mydst)?; + let aliased_item_type = aliased_type.target_type; + mydst.push(&format!( + "{aliased_item_type}.{}.js", + aliased_type.target_fqp[aliased_type.target_fqp.len() - 1] + )); + + let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst); + all_impls.push(impls); + // Sort the implementors by crate so the file will be generated + // identically even with rustdoc running in parallel. + all_impls.sort(); + + let mut v = String::from("(function() {var type_impls = {\n"); + v.push_str(&all_impls.join(",\n")); + v.push_str("\n};"); + v.push_str( + "if (window.register_type_impls) {\ + window.register_type_impls(type_impls);\ + } else {\ + window.pending_type_impls = type_impls;\ + }", + ); + v.push_str("})()"); + cx.shared.fs.write(mydst, v)?; + } + // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - let cache = cx.cache(); + // + let dst = cx.dst.join("trait.impl"); for (&did, imps) in &cache.implementors { // Private modules can leak through to this phase of rustdoc, which // could contain implementations for otherwise private types. In some diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 2e9897ef82ba2..7c052606abacb 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -549,6 +549,7 @@ function preLoadCss(cssUrl) { } } + // window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -615,7 +616,7 @@ function preLoadCss(cssUrl) { onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); - if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) { + if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) { elem.setAttribute("href", window.rootPath + href); } }); @@ -639,6 +640,216 @@ function preLoadCss(cssUrl) { window.register_implementors(window.pending_implementors); } + /** + * + * + * [RUSTDOCIMPL] type.impl + * + * This code inlines implementations into the type alias docs at runtime. It's done at + * runtime because some crates have many type aliases and many methods, and we don't want + * to generate *O*`(types*methods)` HTML text. The data inside is mostly HTML fragments, + * wrapped in JSON. + * + * - It only includes docs generated for the current crate. This function accepts an + * object mapping crate names to the set of impls. + * + * - It filters down to the set of applicable impls. The Rust type checker is used to + * tag each HTML blob with the set of type aliases that can actually use it, so the + * JS only needs to consult the attached list of type aliases. + * + * - It renames the ID attributes, to avoid conflicting IDs in the resulting DOM. + * + * - It adds the necessary items to the sidebar. If it's an inherent impl, that means + * adding methods, associated types, and associated constants. If it's a trait impl, + * that means adding it to the trait impl sidebar list. + * + * - It adds the HTML block itself. If it's an inherent impl, it goes after the type + * alias's own inherent impls. If it's a trait impl, it goes in the Trait + * Implementations section. + * + * - After processing all of the impls, it sorts the sidebar items by name. + * + * @param {{[cratename: string]: Array>}} impl + */ + window.register_type_impls = imp => { + if (!imp || !imp[window.currentCrate]) { + return; + } + window.pending_type_impls = null; + const idMap = new Map(); + + let implementations = document.getElementById("implementations-list"); + let trait_implementations = document.getElementById("trait-implementations-list"); + let trait_implementations_header = document.getElementById("trait-implementations"); + + // We want to include the current type alias's impls, and no others. + const script = document.querySelector("script[data-self-path]"); + const selfPath = script ? script.getAttribute("data-self-path") : null; + + // These sidebar blocks need filled in, too. + const mainContent = document.querySelector("#main-content"); + const sidebarSection = document.querySelector(".sidebar section"); + let methods = document.querySelector(".sidebar .block.method"); + let associatedTypes = document.querySelector(".sidebar .block.associatedtype"); + let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); + let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); + + for (const impList of imp[window.currentCrate]) { + const types = impList.slice(2); + const text = impList[0]; + const isTrait = impList[1] !== 0; + const traitName = impList[1]; + if (types.indexOf(selfPath) === -1) { + continue; + } + let outputList = isTrait ? trait_implementations : implementations; + if (outputList === null) { + const outputListName = isTrait ? "Trait Implementations" : "Implementations"; + const outputListId = isTrait ? + "trait-implementations-list" : + "implementations-list"; + const outputListHeaderId = isTrait ? "trait-implementations" : "implementations"; + const outputListHeader = document.createElement("h2"); + outputListHeader.id = outputListHeaderId; + outputListHeader.innerText = outputListName; + outputList = document.createElement("div"); + outputList.id = outputListId; + if (isTrait) { + const link = document.createElement("a"); + link.href = `#${outputListHeaderId}`; + link.innerText = "Trait Implementations"; + const h = document.createElement("h3"); + h.appendChild(link); + trait_implementations = outputList; + trait_implementations_header = outputListHeader; + sidebarSection.appendChild(h); + sidebarTraitList = document.createElement("ul"); + sidebarTraitList.className = "block trait-implementation"; + sidebarSection.appendChild(sidebarTraitList); + mainContent.appendChild(outputListHeader); + mainContent.appendChild(outputList); + } else { + implementations = outputList; + if (trait_implementations) { + mainContent.insertBefore(outputListHeader, trait_implementations_header); + mainContent.insertBefore(outputList, trait_implementations_header); + } else { + const mainContent = document.querySelector("#main-content"); + mainContent.appendChild(outputListHeader); + mainContent.appendChild(outputList); + } + } + } + const template = document.createElement("template"); + template.innerHTML = text; + + onEachLazy(template.content.querySelectorAll("a"), elem => { + const href = elem.getAttribute("href"); + + if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) { + elem.setAttribute("href", window.rootPath + href); + } + }); + onEachLazy(template.content.querySelectorAll("[id]"), el => { + let i = 0; + if (idMap.has(el.id)) { + i = idMap.get(el.id); + } else if (document.getElementById(el.id)) { + i = 1; + while (document.getElementById(`${el.id}-${2 * i}`)) { + i = 2 * i; + } + while (document.getElementById(`${el.id}-${i}`)) { + i += 1; + } + } + if (i !== 0) { + const oldHref = `#${el.id}`; + const newHref = `#${el.id}-${i}`; + el.id = `${el.id}-${i}`; + onEachLazy(template.content.querySelectorAll("a[href]"), link => { + if (link.getAttribute("href") === oldHref) { + link.href = newHref; + } + }); + } + idMap.set(el.id, i + 1); + }); + const templateAssocItems = template.content.querySelectorAll("section.tymethod, " + + "section.method, section.associatedtype, section.associatedconstant"); + if (isTrait) { + const li = document.createElement("li"); + const a = document.createElement("a"); + a.href = `#${template.content.querySelector(".impl").id}`; + a.textContent = traitName; + li.appendChild(a); + sidebarTraitList.append(li); + } else { + onEachLazy(templateAssocItems, item => { + let block = hasClass(item, "associatedtype") ? associatedTypes : ( + hasClass(item, "associatedconstant") ? associatedConstants : ( + methods)); + if (!block) { + const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : ( + hasClass(item, "associatedconstant") ? "Associated Constants" : ( + "Methods")); + const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : ( + hasClass(item, "associatedconstant") ? "associatedconstant" : ( + "method")); + const blockHeader = document.createElement("h3"); + const blockLink = document.createElement("a"); + blockLink.href = "#implementations"; + blockLink.innerText = blockTitle; + blockHeader.appendChild(blockLink); + block = document.createElement("ul"); + block.className = `block ${blockClass}`; + const insertionReference = methods || sidebarTraitList; + if (insertionReference) { + const insertionReferenceH = insertionReference.previousElementSibling; + sidebarSection.insertBefore(blockHeader, insertionReferenceH); + sidebarSection.insertBefore(block, insertionReferenceH); + } else { + sidebarSection.appendChild(blockHeader); + sidebarSection.appendChild(block); + } + if (hasClass(item, "associatedtype")) { + associatedTypes = block; + } else if (hasClass(item, "associatedconstant")) { + associatedConstants = block; + } else { + methods = block; + } + } + const li = document.createElement("li"); + const a = document.createElement("a"); + a.innerText = item.id.split("-")[0].split(".")[1]; + a.href = `#${item.id}`; + li.appendChild(a); + block.appendChild(li); + }); + } + outputList.appendChild(template.content); + } + + for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) { + if (!list) { + continue; + } + const newChildren = Array.prototype.slice.call(list.children); + newChildren.sort((a, b) => { + const aI = a.innerText; + const bI = b.innerText; + return aI < bI ? -1 : + aI > bI ? 1 : + 0; + }); + list.replaceChildren(...newChildren); + } + }; + if (window.pending_type_impls) { + window.register_type_impls(window.pending_type_impls); + } + function addSidebarCrates() { if (!window.ALL_CRATES) { return; diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index a99198141e281..d982134181c52 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -18,7 +18,7 @@

{# #}

{% endif %} {% if !block.links.is_empty() %} -

{{block.heading.name}}